All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options
@ 2015-08-06 18:28 Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 01/25] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
                   ` (24 more replies)
  0 siblings, 25 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Eduardo Habkost

This patch series adds support to multiple audio backends.  The first
part of the series (until -audiodev command line option) was submitted
multiple times, see
http://lists.nongnu.org/archive/html/qemu-devel/2015-06/msg05037.html
and
http://lists.nongnu.org/archive/html/qemu-devel/2015-07/msg00920.html

Afterwards I add support to multiple backends.  Audio fronteds gain a
new audiodev option to specify the id of the audiodev to use.  The
audiodev= option is required, unless you use the old environment
variable based configuration, in that case it must not used (and you
can't use multiple backends).

Finally I also make mixeng usage optional, it can save us some useless
format converting when not needed.  Also makes easier to support formats
currently not supported by qemu (as only the backend has to support it,
not the mixeng).

For easier testing pull https://github.com/DirtYiCE/qemu.git tag 
audio-multi-v1.

Please review.

Kővágó, Zoltán (25):
  qapi: support implicit structs in OptsVisitor
  qapi: convert NumaOptions into a flat union
  net: remove NetLegacy struct
  net: use Netdev instead of NetClientOptions in client init
  qapi: change Netdev into a flat union
  qapi: reorder NetdevBase and Netdev
  qapi: qapi for audio backends
  qapi: support nested structs in OptsVisitor
  audio: use qapi AudioFormat instead of audfmt_e
  audio: -audiodev command line option
  audio: reduce glob_audio_state usage
  audio: basic support for multi backend audio
  audio: add audiodev properties to frontends
  audio: audiodev= parameters no longer optional when -audiodev present
  paaudio: do not create multiple connections to the same server
  paaudio: properly disconnect streams in fini_*
  audio: remove gcc specific audio_MIN, audio_MAX
  audio: do not run each backend in audio_run
  paaudio: fix playback glitches
  audio: remove mixeng specific code from backends
  audio: common rate control code for timer based outputs
  audio: add mixeng option (documentation)
  audio: make mixeng optional
  paaudio: get/put_buffer functions
  audio: split ctl_* functions into enable_* and volume_*

 Makefile                                |    4 +-
 audio/Makefile.objs                     |    3 +-
 audio/alsaaudio.c                       |  739 +++++-----------
 audio/audio.c                           | 1436 +++++++++++++++----------------
 audio/audio.h                           |   67 +-
 audio/audio_int.h                       |  121 +--
 audio/audio_legacy.c                    |  328 +++++++
 audio/audio_pt_int.c                    |  173 ----
 audio/audio_pt_int.h                    |   22 -
 audio/audio_template.h                  |   99 +--
 audio/audio_win_int.c                   |   18 +-
 audio/coreaudio.c                       |  198 ++---
 audio/dsound_template.h                 |   59 +-
 audio/dsoundaudio.c                     |  444 +++-------
 audio/mixeng.h                          |   11 +-
 audio/noaudio.c                         |   98 +--
 audio/ossaudio.c                        |  589 +++++--------
 audio/paaudio.c                         |  860 ++++++++----------
 audio/rate_template.h                   |    2 +-
 audio/sdlaudio.c                        |  183 ++--
 audio/spiceaudio.c                      |  283 ++----
 audio/wavaudio.c                        |  156 +---
 audio/wavcapture.c                      |    8 +-
 configure                               |    5 -
 hmp-commands.hx                         |   11 +-
 hw/arm/musicpal.c                       |    2 +-
 hw/arm/omap2.c                          |    2 +-
 hw/audio/ac97.c                         |    3 +-
 hw/audio/adlib.c                        |    3 +-
 hw/audio/cs4231a.c                      |    7 +-
 hw/audio/es1370.c                       |   11 +-
 hw/audio/gus.c                          |    3 +-
 hw/audio/hda-codec.c                    |   19 +-
 hw/audio/lm4549.c                       |    6 +-
 hw/audio/milkymist-ac97.c               |    8 +-
 hw/audio/pcspk.c                        |    3 +-
 hw/audio/pl041.c                        |    1 +
 hw/audio/sb16.c                         |   15 +-
 hw/audio/wm8750.c                       |   10 +-
 hw/core/qdev-properties-system.c        |   59 +-
 hw/input/tsc210x.c                      |    2 +-
 hw/net/allwinner_emac.c                 |    2 +-
 hw/net/cadence_gem.c                    |    2 +-
 hw/net/dp8393x.c                        |    2 +-
 hw/net/e1000.c                          |    2 +-
 hw/net/eepro100.c                       |    2 +-
 hw/net/etraxfs_eth.c                    |    2 +-
 hw/net/fsl_etsec/etsec.c                |    2 +-
 hw/net/lan9118.c                        |    2 +-
 hw/net/lance.c                          |    2 +-
 hw/net/mcf_fec.c                        |    2 +-
 hw/net/milkymist-minimac2.c             |    2 +-
 hw/net/mipsnet.c                        |    2 +-
 hw/net/ne2000-isa.c                     |    2 +-
 hw/net/ne2000.c                         |    2 +-
 hw/net/opencores_eth.c                  |    2 +-
 hw/net/pcnet-pci.c                      |    2 +-
 hw/net/rocker/rocker_fp.c               |    2 +-
 hw/net/rtl8139.c                        |    2 +-
 hw/net/smc91c111.c                      |    2 +-
 hw/net/spapr_llan.c                     |    2 +-
 hw/net/stellaris_enet.c                 |    2 +-
 hw/net/vhost_net.c                      |   18 +-
 hw/net/virtio-net.c                     |    6 +-
 hw/net/vmxnet3.c                        |    2 +-
 hw/net/xen_nic.c                        |    2 +-
 hw/net/xgmac.c                          |    2 +-
 hw/net/xilinx_axienet.c                 |    2 +-
 hw/net/xilinx_ethlite.c                 |    2 +-
 hw/usb/dev-audio.c                      |    3 +-
 hw/usb/dev-network.c                    |    2 +-
 include/hw/qdev-properties.h            |    3 +
 include/net/net.h                       |    4 +-
 monitor.c                               |   26 +-
 net/clients.h                           |   20 +-
 net/dump.c                              |    8 +-
 net/hub.c                               |   24 +-
 net/l2tpv3.c                            |    8 +-
 net/net.c                               |  129 ++-
 net/netmap.c                            |    6 +-
 net/slirp.c                             |    8 +-
 net/socket.c                            |   10 +-
 net/tap-win32.c                         |    8 +-
 net/tap.c                               |   28 +-
 net/vde.c                               |    8 +-
 net/vhost-user.c                        |   14 +-
 numa.c                                  |    2 +-
 qapi-schema.json                        |  132 +--
 qapi/audio.json                         |  268 ++++++
 qapi/opts-visitor.c                     |  129 ++-
 qemu-options.hx                         |  237 ++++-
 tests/qapi-schema/qapi-schema-test.json |    9 +-
 tests/test-opts-visitor.c               |   34 +
 ui/vnc.c                                |   29 +-
 ui/vnc.h                                |    2 +
 vl.c                                    |   11 +-
 96 files changed, 3553 insertions(+), 3756 deletions(-)
 create mode 100644 audio/audio_legacy.c
 delete mode 100644 audio/audio_pt_int.c
 delete mode 100644 audio/audio_pt_int.h
 create mode 100644 qapi/audio.json

-- 
2.4.5

^ permalink raw reply	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 01/25] qapi: support implicit structs in OptsVisitor
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 02/25] qapi: convert NumaOptions into a flat union Kővágó, Zoltán
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Michael Roth, Gerd Hoffmann, Markus Armbruster

They are required for flat unions (you still have to allocate the
structs).

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 qapi/opts-visitor.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 7ae33b3..aa68814 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -149,6 +149,12 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
     }
 }
 
+static void
+opts_start_implicit_struct(Visitor *v, void **obj, size_t size, Error **errp)
+{
+    opts_start_struct(v, obj, NULL, NULL, size, errp);
+}
+
 
 static gboolean
 ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data)
@@ -185,6 +191,12 @@ opts_end_struct(Visitor *v, Error **errp)
     ov->fake_id_opt = NULL;
 }
 
+static void
+opts_end_implicit_struct(Visitor *v, Error **errp)
+{
+    opts_end_struct(v, errp);
+}
+
 
 static GQueue *
 lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
@@ -508,6 +520,9 @@ opts_visitor_new(const QemuOpts *opts)
     ov->visitor.start_struct = &opts_start_struct;
     ov->visitor.end_struct   = &opts_end_struct;
 
+    ov->visitor.start_implicit_struct = &opts_start_implicit_struct;
+    ov->visitor.end_implicit_struct = &opts_end_implicit_struct;
+
     ov->visitor.start_list = &opts_start_list;
     ov->visitor.next_list  = &opts_next_list;
     ov->visitor.end_list   = &opts_end_list;
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 02/25] qapi: convert NumaOptions into a flat union
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 01/25] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 03/25] net: remove NetLegacy struct Kővágó, Zoltán
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Markus Armbruster, Gerd Hoffmann, Eduardo Habkost

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 numa.c           |  2 +-
 qapi-schema.json | 47 ++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/numa.c b/numa.c
index 402804b..376f990 100644
--- a/numa.c
+++ b/numa.c
@@ -227,7 +227,7 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
     }
 
     switch (object->kind) {
-    case NUMA_OPTIONS_KIND_NODE:
+    case NUMA_DRIVER_NODE:
         numa_node_parse(object->node, opts, &err);
         if (err) {
             goto error;
diff --git a/qapi-schema.json b/qapi-schema.json
index 4342a08..999faa3 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3536,17 +3536,6 @@
   'data': { '*console':'int', 'events': [ 'InputEvent' ] } }
 
 ##
-# @NumaOptions
-#
-# A discriminated record of NUMA options. (for OptsVisitor)
-#
-# Since 2.1
-##
-{ 'union': 'NumaOptions',
-  'data': {
-    'node': 'NumaNodeOptions' }}
-
-##
 # @NumaNodeOptions
 #
 # Create a guest NUMA node. (for OptsVisitor)
@@ -3573,6 +3562,42 @@
    '*memdev': 'str' }}
 
 ##
+# @NumaDriver
+#
+# List of possible numa drivers.
+#
+# Since: 2.5
+##
+{ 'enum': 'NumaDriver',
+  'data': [ 'node' ] }
+
+##
+# @NumaCommonOptions
+#
+# Common set of numa options.
+#
+# @type: the numa driver to use
+#
+# Since: 2.5
+##
+{ 'struct': 'NumaCommonOptions',
+  'data': {
+    'type': 'NumaDriver' } }
+
+##
+# @NumaOptions
+#
+# A discriminated record of NUMA options. (for OptsVisitor)
+#
+# Since 2.1
+##
+{ 'union': 'NumaOptions',
+  'base': 'NumaCommonOptions',
+  'discriminator': 'type',
+  'data': {
+    'node': 'NumaNodeOptions' }}
+
+##
 # @HostMemPolicy
 #
 # Host memory policy types
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 03/25] net: remove NetLegacy struct
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 01/25] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 02/25] qapi: convert NumaOptions into a flat union Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 04/25] net: use Netdev instead of NetClientOptions in client init Kővágó, Zoltán
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Jason Wang, Gerd Hoffmann, Stefan Hajnoczi, Markus Armbruster

NetLegacy is just Netdev with some extra fields (name, vlan) and an
optional id.  This patch merges the two structs, and net_client_init1
got some extra checks to make sure only accept valid -netdev command
lines.  This is some extra work, but allows us to uniformly manage both
legacy -net and non-legacy -netdev in code.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 net/net.c        | 42 +++++++++++++++++++++---------------------
 qapi-schema.json | 30 +++++++++---------------------
 2 files changed, 30 insertions(+), 42 deletions(-)

diff --git a/net/net.c b/net/net.c
index 28a5597..10fbaca 100644
--- a/net/net.c
+++ b/net/net.c
@@ -911,17 +911,29 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
 };
 
 
-static int net_client_init1(const void *object, int is_netdev, Error **errp)
+static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
 {
-    const NetClientOptions *opts;
+    const NetClientOptions *opts = netdev->opts;
     const char *name;
     NetClientState *peer = NULL;
 
     if (is_netdev) {
-        const Netdev *netdev = object;
-        opts = netdev->opts;
         name = netdev->id;
 
+        /* validate -netdev option: has id, no vlan or name */
+        if (!netdev->has_id) {
+            error_setg(errp, QERR_MISSING_PARAMETER, "id");
+            return -1;
+        }
+        if (netdev->has_name) {
+            error_setg(errp, QERR_INVALID_PARAMETER, "name");
+            return -1;
+        }
+        if (netdev->has_vlan) {
+            error_setg(errp, QERR_INVALID_PARAMETER, "vlan");
+            return -1;
+        }
+
         if (opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP ||
             opts->kind == NET_CLIENT_OPTIONS_KIND_NIC ||
             !net_client_init_fun[opts->kind]) {
@@ -930,10 +942,8 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
             return -1;
         }
     } else {
-        const NetLegacy *net = object;
-        opts = net->opts;
         /* missing optional values have been initialized to "all bits zero" */
-        name = net->has_id ? net->id : net->name;
+        name = netdev->has_id ? netdev->id : netdev->name;
 
         if (opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) {
             return 0; /* nothing to do */
@@ -954,7 +964,7 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
         /* Do not add to a vlan if it's a nic with a netdev= parameter. */
         if (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
             !opts->nic->has_netdev) {
-            peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL);
+            peer = net_hub_add_port(netdev->has_vlan ? netdev->vlan : 0, NULL);
         }
     }
 
@@ -970,26 +980,16 @@ static int net_client_init1(const void *object, int is_netdev, Error **errp)
 }
 
 
-static void net_visit(Visitor *v, int is_netdev, void **object, Error **errp)
-{
-    if (is_netdev) {
-        visit_type_Netdev(v, (Netdev **)object, NULL, errp);
-    } else {
-        visit_type_NetLegacy(v, (NetLegacy **)object, NULL, errp);
-    }
-}
-
-
 int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
 {
-    void *object = NULL;
+    Netdev *object = NULL;
     Error *err = NULL;
     int ret = -1;
 
     {
         OptsVisitor *ov = opts_visitor_new(opts);
 
-        net_visit(opts_get_visitor(ov), is_netdev, &object, &err);
+        visit_type_Netdev(opts_get_visitor(ov), &object, NULL, &err);
         opts_visitor_cleanup(ov);
     }
 
@@ -1000,7 +1000,7 @@ int net_client_init(QemuOpts *opts, int is_netdev, Error **errp)
     if (object) {
         QapiDeallocVisitor *dv = qapi_dealloc_visitor_new();
 
-        net_visit(qapi_dealloc_get_visitor(dv), is_netdev, &object, NULL);
+        visit_type_Netdev(qapi_dealloc_get_visitor(dv), &object, NULL, NULL);
         qapi_dealloc_visitor_cleanup(dv);
     }
 
diff --git a/qapi-schema.json b/qapi-schema.json
index 999faa3..8253d0a 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2499,21 +2499,25 @@
     'vhost-user': 'NetdevVhostUserOptions' } }
 
 ##
-# @NetLegacy
+# @Netdev
 #
-# Captures the configuration of a network device; legacy.
+# Captures the configuration of a network device.
 #
-# @vlan: #optional vlan number
+# @vlan: #optional vlan number (legacy, forbidden with -netdev)
 #
-# @id: #optional identifier for monitor commands
+# @id: #optional identifier for monitor commands (required with -netdev)
 #
 # @name: #optional identifier for monitor commands, ignored if @id is present
+#        (legacy, forbidden with -netdev)
 #
 # @opts: device type specific properties (legacy)
 #
 # Since 1.2
+#
+# @id #optional - since 2.5
+# @vlan, @name - since 2.5
 ##
-{ 'struct': 'NetLegacy',
+{ 'struct': 'Netdev',
   'data': {
     '*vlan': 'int32',
     '*id':   'str',
@@ -2521,22 +2525,6 @@
     'opts':  'NetClientOptions' } }
 
 ##
-# @Netdev
-#
-# Captures the configuration of a network device.
-#
-# @id: identifier for monitor commands.
-#
-# @opts: device type specific properties
-#
-# Since 1.2
-##
-{ 'struct': 'Netdev',
-  'data': {
-    'id':   'str',
-    'opts': 'NetClientOptions' } }
-
-##
 # @InetSocketAddress
 #
 # Captures a socket address or address range in the Internet namespace.
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 04/25] net: use Netdev instead of NetClientOptions in client init
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (2 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 03/25] net: remove NetLegacy struct Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 05/25] qapi: change Netdev into a flat union Kővágó, Zoltán
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael S. Tsirkin, Jason Wang, Vincenzo Maffione, Gerd Hoffmann,
	Stefan Hajnoczi, Giuseppe Lettieri, Luigi Rizzo

This way we no longer need NetClientOptions and can convert Netdev
into a flat union.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 net/clients.h    | 20 ++++++++++----------
 net/dump.c       |  6 +++---
 net/hub.c        |  6 +++---
 net/l2tpv3.c     |  6 +++---
 net/net.c        | 29 ++++++++++++++---------------
 net/netmap.c     |  4 ++--
 net/slirp.c      |  6 +++---
 net/socket.c     |  6 +++---
 net/tap-win32.c  |  6 +++---
 net/tap.c        | 12 ++++++------
 net/vde.c        |  6 +++---
 net/vhost-user.c |  6 +++---
 12 files changed, 56 insertions(+), 57 deletions(-)

diff --git a/net/clients.h b/net/clients.h
index d47530e..5cae479 100644
--- a/net/clients.h
+++ b/net/clients.h
@@ -27,39 +27,39 @@
 #include "net/net.h"
 #include "qapi-types.h"
 
-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const Netdev *netdev, const char *name,
                   NetClientState *peer, Error **errp);
 
 #ifdef CONFIG_SLIRP
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const Netdev *netdev, const char *name,
                    NetClientState *peer, Error **errp);
 #endif
 
-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const Netdev *netdev, const char *name,
                      NetClientState *peer, Error **errp);
 
-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);
 
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp);
 
-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);
 
-int net_init_l2tpv3(const NetClientOptions *opts, const char *name,
+int net_init_l2tpv3(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);
 #ifdef CONFIG_VDE
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp);
 #endif
 
 #ifdef CONFIG_NETMAP
-int net_init_netmap(const NetClientOptions *opts, const char *name,
+int net_init_netmap(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp);
 #endif
 
-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const Netdev *netdev, const char *name,
                         NetClientState *peer, Error **errp);
 
 #endif /* QEMU_NET_CLIENTS_H */
diff --git a/net/dump.c b/net/dump.c
index 02c8064..d80fa94 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -146,7 +146,7 @@ static int net_dump_init(NetClientState *peer, const char *device,
     return 0;
 }
 
-int net_init_dump(const NetClientOptions *opts, const char *name,
+int net_init_dump(const Netdev *netdev, const char *name,
                   NetClientState *peer, Error **errp)
 {
     int len;
@@ -154,8 +154,8 @@ int net_init_dump(const NetClientOptions *opts, const char *name,
     char def_file[128];
     const NetdevDumpOptions *dump;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
-    dump = opts->dump;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
+    dump = netdev->opts->dump;
 
     assert(peer);
 
diff --git a/net/hub.c b/net/hub.c
index 3047f12..29f65b2 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -280,14 +280,14 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
     return 0;
 }
 
-int net_init_hubport(const NetClientOptions *opts, const char *name,
+int net_init_hubport(const Netdev *netdev, const char *name,
                      NetClientState *peer, Error **errp)
 {
     const NetdevHubPortOptions *hubport;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
     assert(!peer);
-    hubport = opts->hubport;
+    hubport = netdev->opts->hubport;
 
     net_hub_add_port(hubport->hubid, name);
     return 0;
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index 4f9bcee..d2f8431 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -524,7 +524,7 @@ static NetClientInfo net_l2tpv3_info = {
     .cleanup = net_l2tpv3_cleanup,
 };
 
-int net_init_l2tpv3(const NetClientOptions *opts,
+int net_init_l2tpv3(const Netdev *netdev,
                     const char *name,
                     NetClientState *peer, Error **errp)
 {
@@ -545,8 +545,8 @@ int net_init_l2tpv3(const NetClientOptions *opts,
     s->queue_tail = 0;
     s->header_mismatch = false;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_L2TPV3);
-    l2tpv3 = opts->l2tpv3;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_L2TPV3);
+    l2tpv3 = netdev->opts->l2tpv3;
 
     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index 10fbaca..339f188 100644
--- a/net/net.c
+++ b/net/net.c
@@ -813,15 +813,15 @@ int qemu_find_nic_model(NICInfo *nd, const char * const *models,
     return -1;
 }
 
-static int net_init_nic(const NetClientOptions *opts, const char *name,
+static int net_init_nic(const Netdev *netdev, const char *name,
                         NetClientState *peer, Error **errp)
 {
     int idx;
     NICInfo *nd;
     const NetLegacyNicOptions *nic;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
-    nic = opts->nic;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
+    nic = netdev->opts->nic;
 
     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -882,7 +882,7 @@ static int net_init_nic(const NetClientOptions *opts, const char *name,
 
 
 static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
-    const NetClientOptions *opts,
+    const Netdev *netdev,
     const char *name,
     NetClientState *peer, Error **errp) = {
         [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,
@@ -913,7 +913,6 @@ static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
 
 static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
 {
-    const NetClientOptions *opts = netdev->opts;
     const char *name;
     NetClientState *peer = NULL;
 
@@ -934,9 +933,9 @@ static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
             return -1;
         }
 
-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP ||
-            opts->kind == NET_CLIENT_OPTIONS_KIND_NIC ||
-            !net_client_init_fun[opts->kind]) {
+        if (netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP ||
+            netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_NIC ||
+            !net_client_init_fun[netdev->opts->kind]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a netdev backend type");
             return -1;
@@ -945,16 +944,16 @@ static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
         /* missing optional values have been initialized to "all bits zero" */
         name = netdev->has_id ? netdev->id : netdev->name;
 
-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) {
+        if (netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) {
             return 0; /* nothing to do */
         }
-        if (opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+        if (netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net type");
             return -1;
         }
 
-        if (!net_client_init_fun[opts->kind]) {
+        if (!net_client_init_fun[netdev->opts->kind]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net backend type (maybe it is not compiled "
                        "into this binary)");
@@ -962,17 +961,17 @@ static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
         }
 
         /* Do not add to a vlan if it's a nic with a netdev= parameter. */
-        if (opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
-            !opts->nic->has_netdev) {
+        if (netdev->opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
+            !netdev->opts->nic->has_netdev) {
             peer = net_hub_add_port(netdev->has_vlan ? netdev->vlan : 0, NULL);
         }
     }
 
-    if (net_client_init_fun[opts->kind](opts, name, peer, errp) < 0) {
+    if (net_client_init_fun[netdev->opts->kind](netdev, name, peer, errp) < 0) {
         /* FIXME drop when all init functions store an Error */
         if (errp && !*errp) {
             error_setg(errp, QERR_DEVICE_INIT_FAILED,
-                       NetClientOptionsKind_lookup[opts->kind]);
+                       NetClientOptionsKind_lookup[netdev->opts->kind]);
         }
         return -1;
     }
diff --git a/net/netmap.c b/net/netmap.c
index 508b829..a464618 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -435,11 +435,11 @@ static NetClientInfo net_netmap_info = {
  *
  * ... -net netmap,ifname="..."
  */
-int net_init_netmap(const NetClientOptions *opts,
+int net_init_netmap(const Netdev *netdev,
                     const char *name, NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
-    const NetdevNetmapOptions *netmap_opts = opts->netmap;
+    const NetdevNetmapOptions *netmap_opts = netdev->opts->netmap;
     NetClientState *nc;
     NetmapPriv me;
     NetmapState *s;
diff --git a/net/slirp.c b/net/slirp.c
index 7657b38..0fc2c52 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -736,7 +736,7 @@ static const char **slirp_dnssearch(const StringList *dnsname)
     return ret;
 }
 
-int net_init_slirp(const NetClientOptions *opts, const char *name,
+int net_init_slirp(const Netdev *netdev, const char *name,
                    NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
@@ -746,8 +746,8 @@ int net_init_slirp(const NetClientOptions *opts, const char *name,
     const NetdevUserOptions *user;
     const char **dnssearch;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
-    user = opts->user;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
+    user = netdev->opts->user;
 
     vnet = user->has_net ? g_strdup(user->net) :
            user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
diff --git a/net/socket.c b/net/socket.c
index b1e3b1c..75f693c 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -699,15 +699,15 @@ static int net_socket_udp_init(NetClientState *peer,
     return 0;
 }
 
-int net_init_socket(const NetClientOptions *opts, const char *name,
+int net_init_socket(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     Error *err = NULL;
     const NetdevSocketOptions *sock;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
-    sock = opts->socket;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
+    sock = netdev->opts->socket;
 
     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
         sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index 625d53c..acce480 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -761,14 +761,14 @@ static int tap_win32_init(NetClientState *peer, const char *model,
     return 0;
 }
 
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevTapOptions *tap;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->tap;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+    tap = netdev->opts->tap;
 
     if (!tap->has_ifname) {
         error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index bd01590..263f807 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -557,7 +557,7 @@ static int net_bridge_run_helper(const char *helper, const char *bridge,
     }
 }
 
-int net_init_bridge(const NetClientOptions *opts, const char *name,
+int net_init_bridge(const Netdev *netdev, const char *name,
                     NetClientState *peer, Error **errp)
 {
     const NetdevBridgeOptions *bridge;
@@ -565,8 +565,8 @@ int net_init_bridge(const NetClientOptions *opts, const char *name,
     TAPState *s;
     int fd, vnet_hdr;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
-    bridge = opts->bridge;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
+    bridge = netdev->opts->bridge;
 
     helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
     br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;
@@ -716,7 +716,7 @@ static int get_fds(char *str, char *fds[], int max)
     return i;
 }
 
-int net_init_tap(const NetClientOptions *opts, const char *name,
+int net_init_tap(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp)
 {
     const NetdevTapOptions *tap;
@@ -728,8 +728,8 @@ int net_init_tap(const NetClientOptions *opts, const char *name,
     const char *vhostfdname;
     char ifname[128];
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = opts->tap;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+    tap = netdev->opts->tap;
     queues = tap->has_queues ? tap->queues : 1;
     vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
 
diff --git a/net/vde.c b/net/vde.c
index dacaa64..0ac2525 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -109,14 +109,14 @@ static int net_vde_init(NetClientState *peer, const char *model,
     return 0;
 }
 
-int net_init_vde(const NetClientOptions *opts, const char *name,
+int net_init_vde(const Netdev *netdev, const char *name,
                  NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevVdeOptions *vde;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
-    vde = opts->vde;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
+    vde = netdev->opts->vde;
 
     /* missing optional values have been initialized to "all bits zero" */
     if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 93dcecd..20981a9 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -223,14 +223,14 @@ static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
     return 0;
 }
 
-int net_init_vhost_user(const NetClientOptions *opts, const char *name,
+int net_init_vhost_user(const Netdev *netdev, const char *name,
                         NetClientState *peer, Error **errp)
 {
     const NetdevVhostUserOptions *vhost_user_opts;
     CharDriverState *chr;
 
-    assert(opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
-    vhost_user_opts = opts->vhost_user;
+    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    vhost_user_opts = netdev->opts->vhost_user;
 
     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 05/25] qapi: change Netdev into a flat union
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (3 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 04/25] net: use Netdev instead of NetClientOptions in client init Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 15:03   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 06/25] qapi: reorder NetdevBase and Netdev Kővágó, Zoltán
                   ` (19 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, Vincenzo Maffione,
	Markus Armbruster, Max Filippov, Gerd Hoffmann, Dmitry Fleytman,
	Edgar E. Iglesias, Rob Herring, Alexander Graf, Scott Feldman,
	Jiri Pirko, Jan Kiszka, Stefan Hajnoczi, Giuseppe Lettieri,
	Luiz Capitulino, Luigi Rizzo, David Gibson, Peter Crosthwaite,
	Michael Walle, open list:sPAPR pseries

Except qapi-schema.json, this patch was geenrated by:

find . -name .git -prune -o -type f \! -name '*~' -print0 | \
  xargs -0 sed -i \
    -e 's/NetClientOptionsKind/NetClientDriver/g' \
    -e 's/NET_CLIENT_OPTIONS_KIND_/NET_CLIENT_DRIVER_/g' \
    -e 's/netdev->opts/netdev/g'

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 hw/arm/musicpal.c                |  2 +-
 hw/core/qdev-properties-system.c |  2 +-
 hw/net/allwinner_emac.c          |  2 +-
 hw/net/cadence_gem.c             |  2 +-
 hw/net/dp8393x.c                 |  2 +-
 hw/net/e1000.c                   |  2 +-
 hw/net/eepro100.c                |  2 +-
 hw/net/etraxfs_eth.c             |  2 +-
 hw/net/fsl_etsec/etsec.c         |  2 +-
 hw/net/lan9118.c                 |  2 +-
 hw/net/lance.c                   |  2 +-
 hw/net/mcf_fec.c                 |  2 +-
 hw/net/milkymist-minimac2.c      |  2 +-
 hw/net/mipsnet.c                 |  2 +-
 hw/net/ne2000-isa.c              |  2 +-
 hw/net/ne2000.c                  |  2 +-
 hw/net/opencores_eth.c           |  2 +-
 hw/net/pcnet-pci.c               |  2 +-
 hw/net/rocker/rocker_fp.c        |  2 +-
 hw/net/rtl8139.c                 |  2 +-
 hw/net/smc91c111.c               |  2 +-
 hw/net/spapr_llan.c              |  2 +-
 hw/net/stellaris_enet.c          |  2 +-
 hw/net/vhost_net.c               | 18 ++++-----
 hw/net/virtio-net.c              |  6 +--
 hw/net/vmxnet3.c                 |  2 +-
 hw/net/xen_nic.c                 |  2 +-
 hw/net/xgmac.c                   |  2 +-
 hw/net/xilinx_axienet.c          |  2 +-
 hw/net/xilinx_ethlite.c          |  2 +-
 hw/usb/dev-network.c             |  2 +-
 include/net/net.h                |  4 +-
 monitor.c                        | 14 +++----
 net/dump.c                       |  6 +--
 net/hub.c                        | 22 +++++------
 net/l2tpv3.c                     |  6 +--
 net/net.c                        | 84 ++++++++++++++++++++--------------------
 net/netmap.c                     |  4 +-
 net/slirp.c                      |  6 +--
 net/socket.c                     |  8 ++--
 net/tap-win32.c                  |  6 +--
 net/tap.c                        | 24 ++++++------
 net/vde.c                        |  6 +--
 net/vhost-user.c                 | 12 +++---
 qapi-schema.json                 | 36 +++++++++++------
 45 files changed, 166 insertions(+), 154 deletions(-)

diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 42f66b3..94bdf5f 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -374,7 +374,7 @@ static void eth_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_mv88w8618_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_receive,
     .cleanup = eth_cleanup,
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 921e799..249976e 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -229,7 +229,7 @@ static void set_netdev(Object *obj, Visitor *v, void *opaque,
     }
 
     queues = qemu_find_net_clients_except(str, peers,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     if (queues == 0) {
         err = -ENOENT;
diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c
index 0407dee..4fdf824 100644
--- a/hw/net/allwinner_emac.c
+++ b/hw/net/allwinner_emac.c
@@ -422,7 +422,7 @@ static const MemoryRegionOps aw_emac_mem_ops = {
 };
 
 static NetClientInfo net_aw_emac_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = aw_emac_can_receive,
     .receive = aw_emac_receive,
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 494a346..d74136a 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -1175,7 +1175,7 @@ static void gem_set_link(NetClientState *nc)
 }
 
 static NetClientInfo net_gem_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = gem_can_receive,
     .receive = gem_receive,
diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c
index ab607e4..fb57900 100644
--- a/hw/net/dp8393x.c
+++ b/hw/net/dp8393x.c
@@ -810,7 +810,7 @@ static void dp8393x_reset(DeviceState *dev)
 }
 
 static NetClientInfo net_dp83932_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = dp8393x_can_receive,
     .receive = dp8393x_receive,
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 5c6bcd0..06b5a52 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1515,7 +1515,7 @@ pci_e1000_uninit(PCIDevice *dev)
 }
 
 static NetClientInfo net_e1000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = e1000_can_receive,
     .receive = e1000_receive,
diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c
index 60333b7..28552f7 100644
--- a/hw/net/eepro100.c
+++ b/hw/net/eepro100.c
@@ -1832,7 +1832,7 @@ static void pci_nic_uninit(PCIDevice *pci_dev)
 }
 
 static NetClientInfo net_eepro100_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = nic_receive,
 };
diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c
index d600275..f43a170 100644
--- a/hw/net/etraxfs_eth.c
+++ b/hw/net/etraxfs_eth.c
@@ -577,7 +577,7 @@ static const MemoryRegionOps eth_ops = {
 };
 
 static NetClientInfo net_etraxfs_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_receive,
     .link_status_changed = eth_set_link,
diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c
index 0f5cf44..c936b3b 100644
--- a/hw/net/fsl_etsec/etsec.c
+++ b/hw/net/fsl_etsec/etsec.c
@@ -369,7 +369,7 @@ static void etsec_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo net_etsec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = etsec_receive,
     .link_status_changed = etsec_set_link_status,
diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c
index 4f0e840..11bfd23 100644
--- a/hw/net/lan9118.c
+++ b/hw/net/lan9118.c
@@ -1305,7 +1305,7 @@ static const MemoryRegionOps lan9118_16bit_mem_ops = {
 };
 
 static NetClientInfo net_lan9118_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = lan9118_receive,
     .link_status_changed = lan9118_set_link,
diff --git a/hw/net/lance.c b/hw/net/lance.c
index 780b39d..7940c05 100644
--- a/hw/net/lance.c
+++ b/hw/net/lance.c
@@ -92,7 +92,7 @@ static const MemoryRegionOps lance_mem_ops = {
 };
 
 static NetClientInfo net_lance_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = pcnet_receive,
     .link_status_changed = pcnet_set_link_status,
diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c
index 21928f9..5386597 100644
--- a/hw/net/mcf_fec.c
+++ b/hw/net/mcf_fec.c
@@ -506,7 +506,7 @@ static const MemoryRegionOps mcf_fec_ops = {
 };
 
 static NetClientInfo net_mcf_fec_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = mcf_fec_receive,
 };
diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c
index 5d1cf08..aff0c9f 100644
--- a/hw/net/milkymist-minimac2.c
+++ b/hw/net/milkymist-minimac2.c
@@ -443,7 +443,7 @@ static void milkymist_minimac2_reset(DeviceState *d)
 }
 
 static NetClientInfo net_milkymist_minimac2_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = minimac2_rx,
 };
diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c
index f261011..b9bf03f 100644
--- a/hw/net/mipsnet.c
+++ b/hw/net/mipsnet.c
@@ -218,7 +218,7 @@ static const VMStateDescription vmstate_mipsnet = {
 };
 
 static NetClientInfo net_mipsnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = mipsnet_receive,
 };
diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c
index 17e7199..7da26c9 100644
--- a/hw/net/ne2000-isa.c
+++ b/hw/net/ne2000-isa.c
@@ -42,7 +42,7 @@ typedef struct ISANE2000State {
 } ISANE2000State;
 
 static NetClientInfo net_ne2000_isa_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = ne2000_can_receive,
     .receive = ne2000_receive,
diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c
index 3492db3..09fa0e3 100644
--- a/hw/net/ne2000.c
+++ b/hw/net/ne2000.c
@@ -703,7 +703,7 @@ void ne2000_setup_io(NE2000State *s, DeviceState *dev, unsigned size)
 }
 
 static NetClientInfo net_ne2000_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = ne2000_can_receive,
     .receive = ne2000_receive,
diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c
index 3642046..4b6407b 100644
--- a/hw/net/opencores_eth.c
+++ b/hw/net/opencores_eth.c
@@ -473,7 +473,7 @@ static ssize_t open_eth_receive(NetClientState *nc,
 }
 
 static NetClientInfo net_open_eth_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = open_eth_can_receive,
     .receive = open_eth_receive,
diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c
index b4d60b8..c04c3a2 100644
--- a/hw/net/pcnet-pci.c
+++ b/hw/net/pcnet-pci.c
@@ -271,7 +271,7 @@ static void pci_pcnet_uninit(PCIDevice *dev)
 }
 
 static NetClientInfo net_pci_pcnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = pcnet_receive,
     .link_status_changed = pcnet_set_link_status,
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c
index c693ae5..3f82e30 100644
--- a/hw/net/rocker/rocker_fp.c
+++ b/hw/net/rocker/rocker_fp.c
@@ -166,7 +166,7 @@ static void fp_port_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo fp_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = fp_port_receive,
     .receive_iov = fp_port_receive_iov,
diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c
index edbb61c..974e706 100644
--- a/hw/net/rtl8139.c
+++ b/hw/net/rtl8139.c
@@ -3462,7 +3462,7 @@ static void rtl8139_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo net_rtl8139_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = rtl8139_can_receive,
     .receive = rtl8139_receive,
diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c
index 74e06e6..4c85eb5 100644
--- a/hw/net/smc91c111.c
+++ b/hw/net/smc91c111.c
@@ -737,7 +737,7 @@ static const MemoryRegionOps smc91c111_mem_ops = {
 };
 
 static NetClientInfo net_smc91c111_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = smc91c111_can_receive,
     .receive = smc91c111_receive,
diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c
index 1ca5e9c..4ee4b92 100644
--- a/hw/net/spapr_llan.c
+++ b/hw/net/spapr_llan.c
@@ -188,7 +188,7 @@ static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf,
 }
 
 static NetClientInfo net_spapr_vlan_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = spapr_vlan_can_receive,
     .receive = spapr_vlan_receive,
diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c
index 21a4773..10290fe 100644
--- a/hw/net/stellaris_enet.c
+++ b/hw/net/stellaris_enet.c
@@ -449,7 +449,7 @@ static void stellaris_enet_reset(stellaris_enet_state *s)
 }
 
 static NetClientInfo net_stellaris_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = stellaris_enet_receive,
 };
diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c
index 5c1d11f..5d24c29 100644
--- a/hw/net/vhost_net.c
+++ b/hw/net/vhost_net.c
@@ -95,10 +95,10 @@ static const int *vhost_net_get_feature_bits(struct vhost_net *net)
     const int *feature_bits = 0;
 
     switch (net->nc->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         feature_bits = kernel_feature_bits;
         break;
-    case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+    case NET_CLIENT_DRIVER_VHOST_USER:
         feature_bits = user_feature_bits;
         break;
     default:
@@ -125,7 +125,7 @@ void vhost_net_ack_features(struct vhost_net *net, uint64_t features)
 static int vhost_net_get_fd(NetClientState *backend)
 {
     switch (backend->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         return tap_get_fd(backend);
     default:
         fprintf(stderr, "vhost-net requires tap backend\n");
@@ -236,7 +236,7 @@ static int vhost_net_start_one(struct vhost_net *net,
         net->nc->info->poll(net->nc, false);
     }
 
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         qemu_set_fd_handler(net->backend, NULL, NULL, NULL);
         file.fd = net->backend;
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
@@ -252,7 +252,7 @@ static int vhost_net_start_one(struct vhost_net *net,
     return 0;
 fail:
     file.fd = -1;
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         while (file.index-- > 0) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
@@ -275,14 +275,14 @@ static void vhost_net_stop_one(struct vhost_net *net,
 {
     struct vhost_vring_file file = { .fd = -1 };
 
-    if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (net->nc->info->type == NET_CLIENT_DRIVER_TAP) {
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_call(&net->dev, VHOST_NET_SET_BACKEND,
                                           &file);
             assert(r >= 0);
         }
-    } else if (net->nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER) {
+    } else if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) {
         for (file.index = 0; file.index < net->dev.nvqs; ++file.index) {
             const VhostOps *vhost_ops = net->dev.vhost_ops;
             int r = vhost_ops->vhost_call(&net->dev, VHOST_RESET_OWNER,
@@ -399,10 +399,10 @@ VHostNetState *get_vhost_net(NetClientState *nc)
     }
 
     switch (nc->info->type) {
-    case NET_CLIENT_OPTIONS_KIND_TAP:
+    case NET_CLIENT_DRIVER_TAP:
         vhost_net = tap_get_vhost_net(nc);
         break;
-    case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+    case NET_CLIENT_DRIVER_VHOST_USER:
         vhost_net = vhost_user_get_vhost_net(nc);
         break;
     default:
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 1510839..ac47bc3 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -406,7 +406,7 @@ static int peer_attach(VirtIONet *n, int index)
         return 0;
     }
 
-    if (nc->peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) {
         return 0;
     }
 
@@ -421,7 +421,7 @@ static int peer_detach(VirtIONet *n, int index)
         return 0;
     }
 
-    if (nc->peer->info->type !=  NET_CLIENT_OPTIONS_KIND_TAP) {
+    if (nc->peer->info->type !=  NET_CLIENT_DRIVER_TAP) {
         return 0;
     }
 
@@ -1599,7 +1599,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f,
 }
 
 static NetClientInfo net_virtio_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = virtio_net_can_receive,
     .receive = virtio_net_receive,
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 59b06b8..1772683 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -1986,7 +1986,7 @@ static void vmxnet3_set_link_status(NetClientState *nc)
 }
 
 static NetClientInfo net_vmxnet3_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_NIC,
+        .type = NET_CLIENT_DRIVER_NIC,
         .size = sizeof(NICState),
         .can_receive = vmxnet3_can_receive,
         .receive = vmxnet3_receive,
diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c
index d7cbfc1..fd2da6a 100644
--- a/hw/net/xen_nic.c
+++ b/hw/net/xen_nic.c
@@ -280,7 +280,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size
 /* ------------------------------------------------------------- */
 
 static NetClientInfo net_xen_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = net_rx_packet,
 };
diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c
index 15fb681..ccf8a77 100644
--- a/hw/net/xgmac.c
+++ b/hw/net/xgmac.c
@@ -370,7 +370,7 @@ out:
 }
 
 static NetClientInfo net_xgmac_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_rx,
 };
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index d63c423..cc464c9 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -933,7 +933,7 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
 }
 
 static NetClientInfo net_xilinx_enet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = eth_rx,
 };
diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c
index ad6b553..ab555f6 100644
--- a/hw/net/xilinx_ethlite.c
+++ b/hw/net/xilinx_ethlite.c
@@ -214,7 +214,7 @@ static void xilinx_ethlite_reset(DeviceState *dev)
 }
 
 static NetClientInfo net_xilinx_ethlite_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .can_receive = eth_can_rx,
     .receive = eth_rx,
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 7800cee..97b2c2a 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1330,7 +1330,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
 }
 
 static NetClientInfo net_usbnet_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .type = NET_CLIENT_DRIVER_NIC,
     .size = sizeof(NICState),
     .receive = usbnet_receive,
     .cleanup = usbnet_cleanup,
diff --git a/include/net/net.h b/include/net/net.h
index 6a6cbef..c0e00ef 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -59,7 +59,7 @@ typedef int (SetVnetLE)(NetClientState *, bool);
 typedef int (SetVnetBE)(NetClientState *, bool);
 
 typedef struct NetClientInfo {
-    NetClientOptionsKind type;
+    NetClientDriver type;
     size_t size;
     NetReceive *receive;
     NetReceive *receive_raw;
@@ -104,7 +104,7 @@ typedef struct NICState {
 char *qemu_mac_strdup_printf(const uint8_t *macaddr);
 NetClientState *qemu_find_netdev(const char *id);
 int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
-                                 NetClientOptionsKind type, int max);
+                                 NetClientDriver type, int max);
 NetClientState *qemu_new_net_client(NetClientInfo *info,
                                     NetClientState *peer,
                                     const char *model,
diff --git a/monitor.c b/monitor.c
index aeea2b5..a40138b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4188,8 +4188,8 @@ void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
     }
     len = strlen(str);
     readline_set_completion_index(rs, len);
-    for (i = 0; NetClientOptionsKind_lookup[i]; i++) {
-        add_completion_option(rs, str, NetClientOptionsKind_lookup[i]);
+    for (i = 0; NetClientDriver_lookup[i]; i++) {
+        add_completion_option(rs, str, NetClientDriver_lookup[i]);
     }
 }
 
@@ -4389,7 +4389,7 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
         NetClientState *ncs[MAX_QUEUE_NUM];
         int count, i;
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NONE,
+                                             NET_CLIENT_DRIVER_NONE,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             const char *name = ncs[i]->name;
@@ -4414,7 +4414,7 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
 
     len = strlen(str);
     readline_set_completion_index(rs, len);
-    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC,
+    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC,
                                          MAX_QUEUE_NUM);
     for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
         QemuOpts *opts;
@@ -4506,7 +4506,7 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
     readline_set_completion_index(rs, len);
     if (nb_args == 2) {
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NONE,
+                                             NET_CLIENT_DRIVER_NONE,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             int id;
@@ -4523,13 +4523,13 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str)
         return;
     } else if (nb_args == 3) {
         count = qemu_find_net_clients_except(NULL, ncs,
-                                             NET_CLIENT_OPTIONS_KIND_NIC,
+                                             NET_CLIENT_DRIVER_NIC,
                                              MAX_QUEUE_NUM);
         for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) {
             int id;
             const char *name;
 
-            if (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT ||
+            if (ncs[i]->info->type == NET_CLIENT_DRIVER_HUBPORT ||
                 net_hub_id_for_client(ncs[i], &id)) {
                 continue;
             }
diff --git a/net/dump.c b/net/dump.c
index d80fa94..e7f77c3 100644
--- a/net/dump.c
+++ b/net/dump.c
@@ -94,7 +94,7 @@ static void dump_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_dump_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_DUMP,
+    .type = NET_CLIENT_DRIVER_DUMP,
     .size = sizeof(DumpState),
     .receive = dump_receive,
     .cleanup = dump_cleanup,
@@ -154,8 +154,8 @@ int net_init_dump(const Netdev *netdev, const char *name,
     char def_file[128];
     const NetdevDumpOptions *dump;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP);
-    dump = netdev->opts->dump;
+    assert(netdev->kind == NET_CLIENT_DRIVER_DUMP);
+    dump = netdev->dump;
 
     assert(peer);
 
diff --git a/net/hub.c b/net/hub.c
index 29f65b2..671e9ba 100644
--- a/net/hub.c
+++ b/net/hub.c
@@ -130,7 +130,7 @@ static void net_hub_port_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_hub_port_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_HUBPORT,
+    .type = NET_CLIENT_DRIVER_HUBPORT,
     .size = sizeof(NetHubPort),
     .can_receive = net_hub_port_can_receive,
     .receive = net_hub_port_receive,
@@ -265,10 +265,10 @@ int net_hub_id_for_client(NetClientState *nc, int *id)
 {
     NetHubPort *port;
 
-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+    if (nc->info->type == NET_CLIENT_DRIVER_HUBPORT) {
         port = DO_UPCAST(NetHubPort, nc, nc);
     } else if (nc->peer != NULL && nc->peer->info->type ==
-            NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+            NET_CLIENT_DRIVER_HUBPORT) {
         port = DO_UPCAST(NetHubPort, nc, nc->peer);
     } else {
         return -ENOENT;
@@ -285,9 +285,9 @@ int net_init_hubport(const Netdev *netdev, const char *name,
 {
     const NetdevHubPortOptions *hubport;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT);
+    assert(netdev->kind == NET_CLIENT_DRIVER_HUBPORT);
     assert(!peer);
-    hubport = netdev->opts->hubport;
+    hubport = netdev->hubport;
 
     net_hub_add_port(hubport->hubid, name);
     return 0;
@@ -314,14 +314,14 @@ void net_hub_check_clients(void)
             }
 
             switch (peer->info->type) {
-            case NET_CLIENT_OPTIONS_KIND_NIC:
+            case NET_CLIENT_DRIVER_NIC:
                 has_nic = 1;
                 break;
-            case NET_CLIENT_OPTIONS_KIND_USER:
-            case NET_CLIENT_OPTIONS_KIND_TAP:
-            case NET_CLIENT_OPTIONS_KIND_SOCKET:
-            case NET_CLIENT_OPTIONS_KIND_VDE:
-            case NET_CLIENT_OPTIONS_KIND_VHOST_USER:
+            case NET_CLIENT_DRIVER_USER:
+            case NET_CLIENT_DRIVER_TAP:
+            case NET_CLIENT_DRIVER_SOCKET:
+            case NET_CLIENT_DRIVER_VDE:
+            case NET_CLIENT_DRIVER_VHOST_USER:
                 has_host_dev = 1;
                 break;
             default:
diff --git a/net/l2tpv3.c b/net/l2tpv3.c
index d2f8431..f68cd76 100644
--- a/net/l2tpv3.c
+++ b/net/l2tpv3.c
@@ -516,7 +516,7 @@ static void net_l2tpv3_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_l2tpv3_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_L2TPV3,
+    .type = NET_CLIENT_DRIVER_L2TPV3,
     .size = sizeof(NetL2TPV3State),
     .receive = net_l2tpv3_receive_dgram,
     .receive_iov = net_l2tpv3_receive_dgram_iov,
@@ -545,8 +545,8 @@ int net_init_l2tpv3(const Netdev *netdev,
     s->queue_tail = 0;
     s->header_mismatch = false;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_L2TPV3);
-    l2tpv3 = netdev->opts->l2tpv3;
+    assert(netdev->kind == NET_CLIENT_DRIVER_L2TPV3);
+    l2tpv3 = netdev->l2tpv3;
 
     if (l2tpv3->has_ipv6 && l2tpv3->ipv6) {
         s->ipv6 = l2tpv3->ipv6;
diff --git a/net/net.c b/net/net.c
index 339f188..7915d31 100644
--- a/net/net.c
+++ b/net/net.c
@@ -315,7 +315,7 @@ NICState *qemu_new_nic(NetClientInfo *info,
     NICState *nic;
     int i, queues = MAX(1, conf->peers.queues);
 
-    assert(info->type == NET_CLIENT_OPTIONS_KIND_NIC);
+    assert(info->type == NET_CLIENT_DRIVER_NIC);
     assert(info->size >= sizeof(NICState));
 
     nic = g_malloc0(info->size + sizeof(NetClientState) * queues);
@@ -385,18 +385,18 @@ void qemu_del_net_client(NetClientState *nc)
     NetClientState *ncs[MAX_QUEUE_NUM];
     int queues, i;
 
-    assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
+    assert(nc->info->type != NET_CLIENT_DRIVER_NIC);
 
     /* If the NetClientState belongs to a multiqueue backend, we will change all
      * other NetClientStates also.
      */
     queues = qemu_find_net_clients_except(nc->name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_NIC,
+                                          NET_CLIENT_DRIVER_NIC,
                                           MAX_QUEUE_NUM);
     assert(queues != 0);
 
     /* If there is a peer NIC, delete and cleanup client, but do not free. */
-    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
         NICState *nic = qemu_get_nic(nc->peer);
         if (nic->peer_deleted) {
             return;
@@ -452,7 +452,7 @@ void qemu_foreach_nic(qemu_nic_foreach func, void *opaque)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
             if (nc->queue_index == 0) {
                 func(qemu_get_nic(nc), opaque);
             }
@@ -598,7 +598,7 @@ void qemu_flush_or_purge_queued_packets(NetClientState *nc, bool purge)
 {
     nc->receive_disabled = 0;
 
-    if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+    if (nc->peer && nc->peer->info->type == NET_CLIENT_DRIVER_HUBPORT) {
         if (net_hub_flush(nc->peer)) {
             qemu_notify_event();
         }
@@ -728,7 +728,7 @@ NetClientState *qemu_find_netdev(const char *id)
     NetClientState *nc;
 
     QTAILQ_FOREACH(nc, &net_clients, next) {
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC)
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC)
             continue;
         if (!strcmp(nc->name, id)) {
             return nc;
@@ -739,7 +739,7 @@ NetClientState *qemu_find_netdev(const char *id)
 }
 
 int qemu_find_net_clients_except(const char *id, NetClientState **ncs,
-                                 NetClientOptionsKind type, int max)
+                                 NetClientDriver type, int max)
 {
     NetClientState *nc;
     int ret = 0;
@@ -820,8 +820,8 @@ static int net_init_nic(const Netdev *netdev, const char *name,
     NICInfo *nd;
     const NetLegacyNicOptions *nic;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_NIC);
-    nic = netdev->opts->nic;
+    assert(netdev->kind == NET_CLIENT_DRIVER_NIC);
+    nic = netdev->nic;
 
     idx = nic_get_free_idx();
     if (idx == -1 || nb_nics >= MAX_NICS) {
@@ -881,32 +881,32 @@ static int net_init_nic(const Netdev *netdev, const char *name,
 }
 
 
-static int (* const net_client_init_fun[NET_CLIENT_OPTIONS_KIND_MAX])(
+static int (* const net_client_init_fun[NET_CLIENT_DRIVER_MAX])(
     const Netdev *netdev,
     const char *name,
     NetClientState *peer, Error **errp) = {
-        [NET_CLIENT_OPTIONS_KIND_NIC]       = net_init_nic,
+        [NET_CLIENT_DRIVER_NIC]       = net_init_nic,
 #ifdef CONFIG_SLIRP
-        [NET_CLIENT_OPTIONS_KIND_USER]      = net_init_slirp,
+        [NET_CLIENT_DRIVER_USER]      = net_init_slirp,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_TAP]       = net_init_tap,
-        [NET_CLIENT_OPTIONS_KIND_SOCKET]    = net_init_socket,
+        [NET_CLIENT_DRIVER_TAP]       = net_init_tap,
+        [NET_CLIENT_DRIVER_SOCKET]    = net_init_socket,
 #ifdef CONFIG_VDE
-        [NET_CLIENT_OPTIONS_KIND_VDE]       = net_init_vde,
+        [NET_CLIENT_DRIVER_VDE]       = net_init_vde,
 #endif
 #ifdef CONFIG_NETMAP
-        [NET_CLIENT_OPTIONS_KIND_NETMAP]    = net_init_netmap,
+        [NET_CLIENT_DRIVER_NETMAP]    = net_init_netmap,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_DUMP]      = net_init_dump,
+        [NET_CLIENT_DRIVER_DUMP]      = net_init_dump,
 #ifdef CONFIG_NET_BRIDGE
-        [NET_CLIENT_OPTIONS_KIND_BRIDGE]    = net_init_bridge,
+        [NET_CLIENT_DRIVER_BRIDGE]    = net_init_bridge,
 #endif
-        [NET_CLIENT_OPTIONS_KIND_HUBPORT]   = net_init_hubport,
+        [NET_CLIENT_DRIVER_HUBPORT]   = net_init_hubport,
 #ifdef CONFIG_VHOST_NET_USED
-        [NET_CLIENT_OPTIONS_KIND_VHOST_USER] = net_init_vhost_user,
+        [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user,
 #endif
 #ifdef CONFIG_L2TPV3
-        [NET_CLIENT_OPTIONS_KIND_L2TPV3]    = net_init_l2tpv3,
+        [NET_CLIENT_DRIVER_L2TPV3]    = net_init_l2tpv3,
 #endif
 };
 
@@ -933,9 +933,9 @@ static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
             return -1;
         }
 
-        if (netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_DUMP ||
-            netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_NIC ||
-            !net_client_init_fun[netdev->opts->kind]) {
+        if (netdev->kind == NET_CLIENT_DRIVER_DUMP ||
+            netdev->kind == NET_CLIENT_DRIVER_NIC ||
+            !net_client_init_fun[netdev->kind]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a netdev backend type");
             return -1;
@@ -944,16 +944,16 @@ static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
         /* missing optional values have been initialized to "all bits zero" */
         name = netdev->has_id ? netdev->id : netdev->name;
 
-        if (netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_NONE) {
+        if (netdev->kind == NET_CLIENT_DRIVER_NONE) {
             return 0; /* nothing to do */
         }
-        if (netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_HUBPORT) {
+        if (netdev->kind == NET_CLIENT_DRIVER_HUBPORT) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net type");
             return -1;
         }
 
-        if (!net_client_init_fun[netdev->opts->kind]) {
+        if (!net_client_init_fun[netdev->kind]) {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type",
                        "a net backend type (maybe it is not compiled "
                        "into this binary)");
@@ -961,17 +961,17 @@ static int net_client_init1(const Netdev *netdev, int is_netdev, Error **errp)
         }
 
         /* Do not add to a vlan if it's a nic with a netdev= parameter. */
-        if (netdev->opts->kind != NET_CLIENT_OPTIONS_KIND_NIC ||
-            !netdev->opts->nic->has_netdev) {
+        if (netdev->kind != NET_CLIENT_DRIVER_NIC ||
+            !netdev->nic->has_netdev) {
             peer = net_hub_add_port(netdev->has_vlan ? netdev->vlan : 0, NULL);
         }
     }
 
-    if (net_client_init_fun[netdev->opts->kind](netdev, name, peer, errp) < 0) {
+    if (net_client_init_fun[netdev->kind](netdev, name, peer, errp) < 0) {
         /* FIXME drop when all init functions store an Error */
         if (errp && !*errp) {
             error_setg(errp, QERR_DEVICE_INIT_FAILED,
-                       NetClientOptionsKind_lookup[netdev->opts->kind]);
+                       NetClientDriver_lookup[netdev->kind]);
         }
         return -1;
     }
@@ -1060,7 +1060,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict)
                      device, vlan_id);
         return;
     }
-    if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+    if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
         error_report("invalid host network device '%s'", device);
         return;
     }
@@ -1126,7 +1126,7 @@ void print_net_client(Monitor *mon, NetClientState *nc)
 {
     monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
                    nc->queue_index,
-                   NetClientOptionsKind_lookup[nc->info->type],
+                   NetClientDriver_lookup[nc->info->type],
                    nc->info_str);
 }
 
@@ -1145,7 +1145,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
         }
 
         /* only query rx-filter information of NIC */
-        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type != NET_CLIENT_DRIVER_NIC) {
             if (has_name) {
                 error_setg(errp, "net client(%s) isn't a NIC", name);
                 return NULL;
@@ -1185,7 +1185,7 @@ RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
 void hmp_info_network(Monitor *mon, const QDict *qdict)
 {
     NetClientState *nc, *peer;
-    NetClientOptionsKind type;
+    NetClientDriver type;
 
     net_hub_info(mon);
 
@@ -1198,10 +1198,10 @@ void hmp_info_network(Monitor *mon, const QDict *qdict)
             continue;
         }
 
-        if (!peer || type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (!peer || type == NET_CLIENT_DRIVER_NIC) {
             print_net_client(mon, nc);
         } /* else it's a netdev connected to a NIC, printed with the NIC */
-        if (peer && type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (peer && type == NET_CLIENT_DRIVER_NIC) {
             monitor_printf(mon, " \\ ");
             print_net_client(mon, peer);
         }
@@ -1215,7 +1215,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
     int queues, i;
 
     queues = qemu_find_net_clients_except(name, ncs,
-                                          NET_CLIENT_OPTIONS_KIND_MAX,
+                                          NET_CLIENT_DRIVER_MAX,
                                           MAX_QUEUE_NUM);
 
     if (queues == 0) {
@@ -1242,7 +1242,7 @@ void qmp_set_link(const char *name, bool up, Error **errp)
          * multiple clients that can still communicate with each other in
          * disconnected mode. For now maintain this compatibility.
          */
-        if (nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->peer->info->type == NET_CLIENT_DRIVER_NIC) {
             for (i = 0; i < queues; i++) {
                 ncs[i]->peer->link_down = !up;
             }
@@ -1283,7 +1283,7 @@ void net_cleanup(void)
      */
     while (!QTAILQ_EMPTY(&net_clients)) {
         nc = QTAILQ_FIRST(&net_clients);
-        if (nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
+        if (nc->info->type == NET_CLIENT_DRIVER_NIC) {
             qemu_del_nic(qemu_get_nic(nc));
         } else {
             qemu_del_net_client(nc);
@@ -1315,7 +1315,7 @@ void net_check_clients(void)
     QTAILQ_FOREACH(nc, &net_clients, next) {
         if (!nc->peer) {
             fprintf(stderr, "Warning: %s %s has no peer\n",
-                    nc->info->type == NET_CLIENT_OPTIONS_KIND_NIC ?
+                    nc->info->type == NET_CLIENT_DRIVER_NIC ?
                     "nic" : "netdev", nc->name);
         }
     }
diff --git a/net/netmap.c b/net/netmap.c
index a464618..eff9b8c 100644
--- a/net/netmap.c
+++ b/net/netmap.c
@@ -417,7 +417,7 @@ static void netmap_set_offload(NetClientState *nc, int csum, int tso4, int tso6,
 
 /* NetClientInfo methods */
 static NetClientInfo net_netmap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_NETMAP,
+    .type = NET_CLIENT_DRIVER_NETMAP,
     .size = sizeof(NetmapState),
     .receive = netmap_receive,
     .receive_iov = netmap_receive_iov,
@@ -439,7 +439,7 @@ int net_init_netmap(const Netdev *netdev,
                     const char *name, NetClientState *peer, Error **errp)
 {
     /* FIXME error_setg(errp, ...) on failure */
-    const NetdevNetmapOptions *netmap_opts = netdev->opts->netmap;
+    const NetdevNetmapOptions *netmap_opts = netdev->netmap;
     NetClientState *nc;
     NetmapPriv me;
     NetmapState *s;
diff --git a/net/slirp.c b/net/slirp.c
index 0fc2c52..707dca7 100644
--- a/net/slirp.c
+++ b/net/slirp.c
@@ -126,7 +126,7 @@ static void net_slirp_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_slirp_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_USER,
+    .type = NET_CLIENT_DRIVER_USER,
     .size = sizeof(SlirpState),
     .receive = net_slirp_receive,
     .cleanup = net_slirp_cleanup,
@@ -746,8 +746,8 @@ int net_init_slirp(const Netdev *netdev, const char *name,
     const NetdevUserOptions *user;
     const char **dnssearch;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_USER);
-    user = netdev->opts->user;
+    assert(netdev->kind == NET_CLIENT_DRIVER_USER);
+    user = netdev->user;
 
     vnet = user->has_net ? g_strdup(user->net) :
            user->has_ip  ? g_strdup_printf("%s/24", user->ip) :
diff --git a/net/socket.c b/net/socket.c
index 75f693c..feb1337 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -346,7 +346,7 @@ static void net_socket_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_dgram_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+    .type = NET_CLIENT_DRIVER_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive_dgram,
     .cleanup = net_socket_cleanup,
@@ -429,7 +429,7 @@ static void net_socket_connect(void *opaque)
 }
 
 static NetClientInfo net_socket_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_SOCKET,
+    .type = NET_CLIENT_DRIVER_SOCKET,
     .size = sizeof(NetSocketState),
     .receive = net_socket_receive,
     .cleanup = net_socket_cleanup,
@@ -706,8 +706,8 @@ int net_init_socket(const Netdev *netdev, const char *name,
     Error *err = NULL;
     const NetdevSocketOptions *sock;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_SOCKET);
-    sock = netdev->opts->socket;
+    assert(netdev->kind == NET_CLIENT_DRIVER_SOCKET);
+    sock = netdev->socket;
 
     if (sock->has_fd + sock->has_listen + sock->has_connect + sock->has_mcast +
         sock->has_udp != 1) {
diff --git a/net/tap-win32.c b/net/tap-win32.c
index acce480..3528e4f 100644
--- a/net/tap-win32.c
+++ b/net/tap-win32.c
@@ -723,7 +723,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 }
 
 static NetClientInfo net_tap_win32_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
+    .type = NET_CLIENT_DRIVER_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .cleanup = tap_cleanup,
@@ -767,8 +767,8 @@ int net_init_tap(const Netdev *netdev, const char *name,
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevTapOptions *tap;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = netdev->opts->tap;
+    assert(netdev->kind == NET_CLIENT_DRIVER_TAP);
+    tap = netdev->tap;
 
     if (!tap->has_ifname) {
         error_report("tap: no interface name");
diff --git a/net/tap.c b/net/tap.c
index 263f807..38a8f2c 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -221,7 +221,7 @@ static bool tap_has_ufo(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
 
     return s->has_ufo;
 }
@@ -230,7 +230,7 @@ static bool tap_has_vnet_hdr(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
 
     return !!s->host_vnet_hdr_len;
 }
@@ -239,7 +239,7 @@ static bool tap_has_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
 
     return !!tap_probe_vnet_hdr_len(s->fd, len);
 }
@@ -248,7 +248,7 @@ static void tap_set_vnet_hdr_len(NetClientState *nc, int len)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
            len == sizeof(struct virtio_net_hdr));
 
@@ -260,7 +260,7 @@ static void tap_using_vnet_hdr(NetClientState *nc, bool using_vnet_hdr)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
 
     s->using_vnet_hdr = using_vnet_hdr;
@@ -326,14 +326,14 @@ static void tap_poll(NetClientState *nc, bool enable)
 int tap_get_fd(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     return s->fd;
 }
 
 /* fd support */
 
 static NetClientInfo net_tap_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_TAP,
+    .type = NET_CLIENT_DRIVER_TAP,
     .size = sizeof(TAPState),
     .receive = tap_receive,
     .receive_raw = tap_receive_raw,
@@ -565,8 +565,8 @@ int net_init_bridge(const Netdev *netdev, const char *name,
     TAPState *s;
     int fd, vnet_hdr;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
-    bridge = netdev->opts->bridge;
+    assert(netdev->kind == NET_CLIENT_DRIVER_BRIDGE);
+    bridge = netdev->bridge;
 
     helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
     br     = bridge->has_br     ? bridge->br     : DEFAULT_BRIDGE_INTERFACE;
@@ -728,8 +728,8 @@ int net_init_tap(const Netdev *netdev, const char *name,
     const char *vhostfdname;
     char ifname[128];
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
-    tap = netdev->opts->tap;
+    assert(netdev->kind == NET_CLIENT_DRIVER_TAP);
+    tap = netdev->tap;
     queues = tap->has_queues ? tap->queues : 1;
     vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL;
 
@@ -890,7 +890,7 @@ int net_init_tap(const Netdev *netdev, const char *name,
 VHostNetState *tap_get_vhost_net(NetClientState *nc)
 {
     TAPState *s = DO_UPCAST(TAPState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
+    assert(nc->info->type == NET_CLIENT_DRIVER_TAP);
     return s->vhost_net;
 }
 
diff --git a/net/vde.c b/net/vde.c
index 0ac2525..585fc23 100644
--- a/net/vde.c
+++ b/net/vde.c
@@ -68,7 +68,7 @@ static void vde_cleanup(NetClientState *nc)
 }
 
 static NetClientInfo net_vde_info = {
-    .type = NET_CLIENT_OPTIONS_KIND_VDE,
+    .type = NET_CLIENT_DRIVER_VDE,
     .size = sizeof(VDEState),
     .receive = vde_receive,
     .cleanup = vde_cleanup,
@@ -115,8 +115,8 @@ int net_init_vde(const Netdev *netdev, const char *name,
     /* FIXME error_setg(errp, ...) on failure */
     const NetdevVdeOptions *vde;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_VDE);
-    vde = netdev->opts->vde;
+    assert(netdev->kind == NET_CLIENT_DRIVER_VDE);
+    vde = netdev->vde;
 
     /* missing optional values have been initialized to "all bits zero" */
     if (net_vde_init(peer, "vde", name, vde->sock, vde->port, vde->group,
diff --git a/net/vhost-user.c b/net/vhost-user.c
index 20981a9..d504e26 100644
--- a/net/vhost-user.c
+++ b/net/vhost-user.c
@@ -30,7 +30,7 @@ typedef struct VhostUserChardevProps {
 VHostNetState *vhost_user_get_vhost_net(NetClientState *nc)
 {
     VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc);
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
     return s->vhost_net;
 }
 
@@ -75,20 +75,20 @@ static void vhost_user_cleanup(NetClientState *nc)
 
 static bool vhost_user_has_vnet_hdr(NetClientState *nc)
 {
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
 
     return true;
 }
 
 static bool vhost_user_has_ufo(NetClientState *nc)
 {
-    assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
+    assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER);
 
     return true;
 }
 
 static NetClientInfo net_vhost_user_info = {
-        .type = NET_CLIENT_OPTIONS_KIND_VHOST_USER,
+        .type = NET_CLIENT_DRIVER_VHOST_USER,
         .size = sizeof(VhostUserState),
         .cleanup = vhost_user_cleanup,
         .has_vnet_hdr = vhost_user_has_vnet_hdr,
@@ -229,8 +229,8 @@ int net_init_vhost_user(const Netdev *netdev, const char *name,
     const NetdevVhostUserOptions *vhost_user_opts;
     CharDriverState *chr;
 
-    assert(netdev->opts->kind == NET_CLIENT_OPTIONS_KIND_VHOST_USER);
-    vhost_user_opts = netdev->opts->vhost_user;
+    assert(netdev->kind == NET_CLIENT_DRIVER_VHOST_USER);
+    vhost_user_opts = netdev->vhost_user;
 
     chr = net_vhost_parse_chardev(vhost_user_opts, errp);
     if (!chr) {
diff --git a/qapi-schema.json b/qapi-schema.json
index 8253d0a..fd6b6a2 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2474,16 +2474,31 @@
     '*vhostforce':    'bool' } }
 
 ##
-# @NetClientOptions
+# @NetClientDriver
 #
-# A discriminated record of network device traits.
+# Available netdev drivers.
+#
+# Since 2.5
+##
+{ 'enum': 'NetClientDriver',
+  'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', 'dump',
+            'bridge', 'hubport', 'netmap', 'vhost-user' ] }
+
+##
+# @Netdev
+#
+# Captures the configuration of a network device.
 #
 # Since 1.2
 #
 # 'l2tpv3' - since 2.1
+# @id #optional - since 2.5
+# @vlan, @name - since 2.5
 #
 ##
-{ 'union': 'NetClientOptions',
+{ 'union': 'Netdev',
+  'base': 'NetdevBase',
+  'discriminator': 'type',
   'data': {
     'none':     'NetdevNoneOptions',
     'nic':      'NetLegacyNicOptions',
@@ -2499,9 +2514,9 @@
     'vhost-user': 'NetdevVhostUserOptions' } }
 
 ##
-# @Netdev
+# @NetdevBase
 #
-# Captures the configuration of a network device.
+# Captures the commopn configuration of a network device.
 #
 # @vlan: #optional vlan number (legacy, forbidden with -netdev)
 #
@@ -2510,19 +2525,16 @@
 # @name: #optional identifier for monitor commands, ignored if @id is present
 #        (legacy, forbidden with -netdev)
 #
-# @opts: device type specific properties (legacy)
+# @type: the netdev driver to use
 #
-# Since 1.2
-#
-# @id #optional - since 2.5
-# @vlan, @name - since 2.5
+# Since 2.5
 ##
-{ 'struct': 'Netdev',
+{ 'struct': 'NetdevBase',
   'data': {
     '*vlan': 'int32',
     '*id':   'str',
     '*name': 'str',
-    'opts':  'NetClientOptions' } }
+    'type':  'NetClientDriver' } }
 
 ##
 # @InetSocketAddress
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 06/25] qapi: reorder NetdevBase and Netdev
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (4 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 05/25] qapi: change Netdev into a flat union Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 07/25] qapi: qapi for audio backends Kővágó, Zoltán
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Markus Armbruster

Probably more logical if NetdevBase comes before Netdev.
No schematic changes.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 qapi-schema.json | 46 +++++++++++++++++++++++-----------------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index fd6b6a2..c9d9263 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2485,6 +2485,29 @@
             'bridge', 'hubport', 'netmap', 'vhost-user' ] }
 
 ##
+# @NetdevBase
+#
+# Captures the commopn configuration of a network device.
+#
+# @vlan: #optional vlan number (legacy, forbidden with -netdev)
+#
+# @id: #optional identifier for monitor commands (required with -netdev)
+#
+# @name: #optional identifier for monitor commands, ignored if @id is present
+#        (legacy, forbidden with -netdev)
+#
+# @type: the netdev driver to use
+#
+# Since 2.5
+##
+{ 'struct': 'NetdevBase',
+  'data': {
+    '*vlan': 'int32',
+    '*id':   'str',
+    '*name': 'str',
+    'type':  'NetClientDriver' } }
+
+##
 # @Netdev
 #
 # Captures the configuration of a network device.
@@ -2514,29 +2537,6 @@
     'vhost-user': 'NetdevVhostUserOptions' } }
 
 ##
-# @NetdevBase
-#
-# Captures the commopn configuration of a network device.
-#
-# @vlan: #optional vlan number (legacy, forbidden with -netdev)
-#
-# @id: #optional identifier for monitor commands (required with -netdev)
-#
-# @name: #optional identifier for monitor commands, ignored if @id is present
-#        (legacy, forbidden with -netdev)
-#
-# @type: the netdev driver to use
-#
-# Since 2.5
-##
-{ 'struct': 'NetdevBase',
-  'data': {
-    '*vlan': 'int32',
-    '*id':   'str',
-    '*name': 'str',
-    'type':  'NetClientDriver' } }
-
-##
 # @InetSocketAddress
 #
 # Captures a socket address or address range in the Internet namespace.
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 07/25] qapi: qapi for audio backends
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (5 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 06/25] qapi: reorder NetdevBase and Netdev Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 15:30   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
                   ` (17 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Markus Armbruster

This patch adds structures into qapi to replace the existing
configuration structures used by audio backends currently. This qapi
will be the base of the -audiodev command line parameter (that replaces
the old environment variables based config).

This is not a 1:1 translation of the old options, I've tried to make
them much more consistent (e.g. almost every backend had an option to
specify buffer size, but the name was different for every backend, and
some backends required usecs, while some other required frames, samples
or bytes). Also tried to reduce the number of abbreviations used by the
config keys.

Some of the more important changes:
* use `in` and `out` instead of `ADC` and `DAC`, as the former is more
  user friendly imho
* moved buffer settings into the global setting area (so it's the same
  for all backends that support it. Backends that can't change buffer
  size will simply ignore them). Also using usecs, as it's probably more
  user friendly than samples or bytes.
* try-poll is now an alsa backend specific option (as all other backends
  currently ignore it)

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 Makefile         |   4 +-
 qapi-schema.json |   3 +
 qapi/audio.json  | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 268 insertions(+), 2 deletions(-)
 create mode 100644 qapi/audio.json

diff --git a/Makefile b/Makefile
index 340d9c8..bdd0bc6 100644
--- a/Makefile
+++ b/Makefile
@@ -262,8 +262,8 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 		"  GEN   $@")
 
 qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
-               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
-               $(SRC_PATH)/qapi/event.json
+               $(SRC_PATH)/qapi/audio.json  $(SRC_PATH)/qapi/block.json \
+               $(SRC_PATH)/qapi/block-core.json $(SRC_PATH)/qapi/event.json
 
 qapi-types.c qapi-types.h :\
 $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
diff --git a/qapi-schema.json b/qapi-schema.json
index c9d9263..e9cbe15 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -5,6 +5,9 @@
 # QAPI common definitions
 { 'include': 'qapi/common.json' }
 
+# QAPI audio definitions
+{ 'include': 'qapi/audio.json' }
+
 # QAPI block definitions
 { 'include': 'qapi/block.json' }
 
diff --git a/qapi/audio.json b/qapi/audio.json
new file mode 100644
index 0000000..b57b215
--- /dev/null
+++ b/qapi/audio.json
@@ -0,0 +1,263 @@
+# -*- mode: python -*-
+#
+# Copyright (C) 2015 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+##
+# @AudiodevNoOptions
+#
+# The none, coreaudio, sdl and spice audio backend have no options.
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevNoOptions',
+  'data': { } }
+
+##
+# @AudiodevAlsaPerDirectionOptions
+#
+# Options of the alsa backend that are used for both playback and recording.
+#
+# @dev: #optional the name of the alsa device to use (default 'default')
+#
+# @try-poll: #optional attempt to use poll mode, falling back to non polling
+#            access on failure (default on)
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevAlsaPerDirectionOptions',
+  'data': {
+    '*dev':      'str',
+    '*try-poll': 'bool' } }
+
+##
+# @AudiodevAlsaOptions
+#
+# Options of the alsa audio backend.
+#
+# @alsa-in: options of the capture stream
+#
+# @alsa-out: options of the playback stream
+#
+# @threshold: #optional set the threshold (in microsecods) when playback starts
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevAlsaOptions',
+  'data': {
+    'alsa-in':    'AudiodevAlsaPerDirectionOptions',
+    'alsa-out':   'AudiodevAlsaPerDirectionOptions',
+    '*threshold': 'int' } }
+
+##
+# @AudiodevDsoundOptions
+#
+# Options of the dsound audio backend.
+#
+# @latency: #optional add extra latency to playback in microseconds (default
+#           10000)
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevDsoundOptions',
+  'data': {
+    '*latency': 'int' } }
+
+##
+# @AudiodevOssPerDirectionOptions
+#
+# Options of the oss backend that are used for both playback and recording.
+#
+# @dev: #optional file name of the oss device (default '/dev/dsp')
+#
+# @try-poll: #optional attempt to use poll mode, falling back to non polling
+#            access on failure (default on)
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevOssPerDirectionOptions',
+  'data': {
+    '*dev':      'str',
+    '*try-poll': 'bool' } }
+
+##
+# @AudiodevOssOptions
+#
+# Options of the oss audio backend.
+#
+# @oss-in: options of the capture stream
+#
+# @oss-out: options of the playback stream
+#
+# @try-mmap: #optional try using memory mapped access, falling back to non
+#            memory mapped access on failure (default off)
+#
+# @exclusive: #optional open device in exclusive mode (vmix won't work)
+#             (default off)
+#
+# @dsp-policy: #optional set the timing policy of the device (between 0 and 10,
+#              where smaller number means smaller latency but higher CPU usage)
+#              or -1 to use fragment mode (option ignored on some platforms)
+#              (default 5)
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevOssOptions',
+  'data': {
+    'oss-in':      'AudiodevOssPerDirectionOptions',
+    'oss-out':     'AudiodevOssPerDirectionOptions',
+    '*try-mmap':   'bool',
+    '*exclusive':  'bool',
+    '*dsp-policy': 'int' } }
+
+##
+# @AudiodevPaPerDirectionOptions
+#
+# Options of the pa backend that are used for both playback and recording.
+#
+# @name: #optional name of the sink/source to use
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevPaPerDirectionOptions',
+  'data': {
+    '*name': 'str' } }
+
+##
+# @AudiodevPaOptions
+#
+# Options of the pa (PulseAudio) audio backend.
+#
+# @server: #optional PulseAudio server address (default: let PulseAudio choose)
+#
+# @sink: sink specific options
+#
+# @source: source specific options
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevPaOptions',
+  'data': {
+    '*server': 'str',
+    'sink':    'AudiodevPaPerDirectionOptions',
+    'source':  'AudiodevPaPerDirectionOptions' } }
+
+##
+# @AudiodevWavOptions
+#
+# Options of the wav audio backend.
+#
+# @path: #optional name of the wav file to record (default 'qemu.wav')
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevWavOptions',
+  'data': {
+    '*path': 'str' } }
+
+
+##
+# @AudioFormat
+#
+# An enumeration of possible audio formats.
+#
+# Since: 2.5
+##
+{ 'enum': 'AudioFormat',
+  'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] }
+
+##
+# @AudiodevDriver
+#
+# An enumeration of possible audio backend drivers.
+#
+# Since: 2.5
+##
+{ 'enum': 'AudiodevDriver',
+  'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', 'spice',
+            'wav' ] }
+
+##
+# @AudiodevPerDirectionOptions
+#
+# General audio backend options that are used for both playback and recording.
+#
+# @fixed-settings: #optional use fixed settings for host input/output.  When
+#                  off, frequency, channels and format must not be specified
+#                  (default on)
+#
+# @frequency: #optional frequency to use when using fixed settings
+#             (default 44100)
+#
+# @channels: #optional number of channels when using fixed settings
+#            (default 2)
+#
+# @voices: #optional number of voices to use (default 1)
+#
+# @format: #optional sample format to use when using fixed settings
+#          (default s16)
+#
+# @buffer-len: #optional the buffer size in microseconds
+#
+# @buffer-count: #optional number of buffers
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevPerDirectionOptions',
+  'data': {
+    '*fixed-settings': 'bool',
+    '*frequency':      'int',
+    '*channels':       'int',
+    '*voices':         'int',
+    '*format':         'AudioFormat',
+    '*buffer-len':     'int',
+    '*buffer-count':   'int' } }
+
+##
+# @AudiodevCommonOptions
+#
+# Captures the common configuration of an audio backend.
+#
+# @id: identifier of the backend
+#
+# @driver: the backend driver to use
+#
+# @in: options of the capture stream
+#
+# @out: options of the playback stream
+#
+# @timer-period: #optional timer period (in microseconds, 0: use lowest
+#                possible)
+#
+# Since: 2.5
+##
+{ 'struct': 'AudiodevCommonOptions',
+  'data': {
+    'id':            'str',
+    'driver':        'AudiodevDriver',
+    'in':            'AudiodevPerDirectionOptions',
+    'out':           'AudiodevPerDirectionOptions',
+    '*timer-period': 'int' } }
+
+##
+# @AudiodevBackendOptions
+#
+# Options of an audio backend.
+#
+# Since: 2.5
+##
+{ 'union': 'Audiodev',
+  'base': 'AudiodevCommonOptions',
+  'discriminator': 'driver',
+  'data': {
+    'none':      'AudiodevNoOptions',
+    'alsa':      'AudiodevAlsaOptions',
+    'coreaudio': 'AudiodevNoOptions',
+    'dsound':    'AudiodevDsoundOptions',
+    'oss':       'AudiodevOssOptions',
+    'pa':        'AudiodevPaOptions',
+    'sdl':       'AudiodevNoOptions',
+    'spice':     'AudiodevNoOptions',
+    'wav':       'AudiodevWavOptions' } }
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (6 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 07/25] qapi: qapi for audio backends Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 15:55   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 09/25] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
                   ` (16 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Michael Roth, Gerd Hoffmann, Markus Armbruster

The current OptsVisitor flattens the whole structure, if there are same
named fields under different paths (like `in' and `out' in `Audiodev'),
the current visitor can't cope with them (for example setting
`frequency=44100' will set the in's frequency to 44100 and leave out's
frequency unspecified).

This patch fixes it, by always requiring a complete path in case of
nested structs.  Fields in the path are separated by dots, similar to C
structs (without pointers), like `in.frequency' or`out.frequency'.

You must provide a full path even in non-ambigous cases.  The qapi
flattening commits hopefully ensures that this change doesn't create
backward compatibility problems.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 qapi/opts-visitor.c                     | 114 ++++++++++++++++++++++++++------
 tests/qapi-schema/qapi-schema-test.json |   9 ++-
 tests/test-opts-visitor.c               |  34 ++++++++++
 3 files changed, 135 insertions(+), 22 deletions(-)

diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index aa68814..ff61d42 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -71,6 +71,7 @@ struct OptsVisitor
      * schema, with a single mandatory scalar member. */
     ListMode list_mode;
     GQueue *repeated_opts;
+    char *repeated_name;
 
     /* When parsing a list of repeating options as integers, values of the form
      * "a-b", representing a closed interval, are allowed. Elements in the
@@ -86,6 +87,9 @@ struct OptsVisitor
      * not survive or escape the OptsVisitor object.
      */
     QemuOpt *fake_id_opt;
+
+    /* List of field names leading to the current structure. */
+    GQueue *nested_names;
 };
 
 
@@ -100,6 +104,7 @@ static void
 opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
 {
     GQueue *list;
+    assert(opt);
 
     list = g_hash_table_lookup(unprocessed_opts, opt->name);
     if (list == NULL) {
@@ -127,6 +132,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
     if (obj) {
         *obj = g_malloc0(size > 0 ? size : 1);
     }
+
+    g_queue_push_tail(ov->nested_names, (gpointer) name);
+
     if (ov->depth++ > 0) {
         return;
     }
@@ -169,6 +177,8 @@ opts_end_struct(Visitor *v, Error **errp)
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
     GQueue *any;
 
+    g_queue_pop_tail(ov->nested_names);
+
     if (--ov->depth > 0) {
         return;
     }
@@ -198,15 +208,54 @@ opts_end_implicit_struct(Visitor *v, Error **errp)
 }
 
 
+static void
+sum_strlen(gpointer data, gpointer user_data)
+{
+    const char *str = data;
+    size_t *sum_len = user_data;
+
+    if (str) { /* skip NULLs */
+        *sum_len += strlen(str) + 1;
+    }
+}
+
+static void
+append_str(gpointer data, gpointer user_data)
+{
+    const char *str = data;
+    char *concat_str = user_data;
+
+    if (str) {
+        strcat(concat_str, str);
+        strcat(concat_str, ".");
+    }
+}
+
+/* lookup a name, using a fully qualified version */
 static GQueue *
-lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
+lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key,
+                Error **errp)
 {
-    GQueue *list;
+    GQueue *list = NULL;
+    char *key;
+    size_t sum_len = strlen(name);
+
+    g_queue_foreach(ov->nested_names, sum_strlen, &sum_len);
+    key = g_malloc(sum_len+1);
+    key[0] = 0;
+    g_queue_foreach(ov->nested_names, append_str, key);
+    strcat(key, name);
+
+    list = g_hash_table_lookup(ov->unprocessed_opts, key);
+    if (list && out_key) {
+        *out_key = g_strdup(key);
+    }
 
-    list = g_hash_table_lookup(ov->unprocessed_opts, name);
     if (!list) {
         error_setg(errp, QERR_MISSING_PARAMETER, name);
     }
+
+    g_free(key);
     return list;
 }
 
@@ -218,7 +267,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
 
     /* we can't traverse a list in a list */
     assert(ov->list_mode == LM_NONE);
-    ov->repeated_opts = lookup_distinct(ov, name, errp);
+    ov->repeated_opts = lookup_distinct(ov, name, &ov->repeated_name, errp);
     if (ov->repeated_opts != NULL) {
         ov->list_mode = LM_STARTED;
     }
@@ -254,11 +303,9 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
         /* range has been completed, fall through in order to pop option */
 
     case LM_IN_PROGRESS: {
-        const QemuOpt *opt;
-
-        opt = g_queue_pop_head(ov->repeated_opts);
+        g_queue_pop_head(ov->repeated_opts);
         if (g_queue_is_empty(ov->repeated_opts)) {
-            g_hash_table_remove(ov->unprocessed_opts, opt->name);
+            g_hash_table_remove(ov->unprocessed_opts, ov->repeated_name);
             return NULL;
         }
         link = &(*list)->next;
@@ -284,22 +331,28 @@ opts_end_list(Visitor *v, Error **errp)
            ov->list_mode == LM_SIGNED_INTERVAL ||
            ov->list_mode == LM_UNSIGNED_INTERVAL);
     ov->repeated_opts = NULL;
+
+    g_free(ov->repeated_name);
+    ov->repeated_name = NULL;
+
     ov->list_mode = LM_NONE;
 }
 
 
 static const QemuOpt *
-lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
+lookup_scalar(const OptsVisitor *ov, const char *name, char** out_key,
+              Error **errp)
 {
     if (ov->list_mode == LM_NONE) {
         GQueue *list;
 
         /* the last occurrence of any QemuOpt takes effect when queried by name
          */
-        list = lookup_distinct(ov, name, errp);
+        list = lookup_distinct(ov, name, out_key, errp);
         return list ? g_queue_peek_tail(list) : NULL;
     }
     assert(ov->list_mode == LM_IN_PROGRESS);
+    assert(out_key == NULL || *out_key == NULL);
     return g_queue_peek_head(ov->repeated_opts);
 }
 
@@ -321,13 +374,15 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
 {
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
     const QemuOpt *opt;
+    char *key = NULL;
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
     *obj = g_strdup(opt->str ? opt->str : "");
-    processed(ov, name);
+    processed(ov, key);
+    g_free(key);
 }
 
 
@@ -337,8 +392,9 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
 {
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
     const QemuOpt *opt;
+    char *key = NULL;
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -355,13 +411,15 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
         } else {
             error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                        "on|yes|y|off|no|n");
+            g_free(key);
             return;
         }
     } else {
         *obj = true;
     }
 
-    processed(ov, name);
+    processed(ov, key);
+    g_free(key);
 }
 
 
@@ -373,13 +431,14 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     const char *str;
     long long val;
     char *endptr;
+    char *key = NULL;
 
     if (ov->list_mode == LM_SIGNED_INTERVAL) {
         *obj = ov->range_next.s;
         return;
     }
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -393,11 +452,13 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
         if (*endptr == '\0') {
             *obj = val;
-            processed(ov, name);
+            processed(ov, key);
+            g_free(key);
             return;
         }
         if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
             long long val2;
+            assert(key == NULL);
 
             str = endptr + 1;
             val2 = strtoll(str, &endptr, 0);
@@ -418,6 +479,7 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
     error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                (ov->list_mode == LM_NONE) ? "an int64 value" :
                                             "an int64 value or range");
+    g_free(key);
 }
 
 
@@ -429,13 +491,14 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     const char *str;
     unsigned long long val;
     char *endptr;
+    char *key = NULL;
 
     if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
         *obj = ov->range_next.u;
         return;
     }
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -447,11 +510,13 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
         if (*endptr == '\0') {
             *obj = val;
-            processed(ov, name);
+            processed(ov, key);
+            g_free(key);
             return;
         }
         if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
             unsigned long long val2;
+            assert(key == NULL);
 
             str = endptr + 1;
             if (parse_uint_full(str, &val2, 0) == 0 &&
@@ -470,6 +535,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                (ov->list_mode == LM_NONE) ? "a uint64 value" :
                                             "a uint64 value or range");
+    g_free(key);
 }
 
 
@@ -480,8 +546,9 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     const QemuOpt *opt;
     int64_t val;
     char *endptr;
+    char *key = NULL;
 
-    opt = lookup_scalar(ov, name, errp);
+    opt = lookup_scalar(ov, name, &key, errp);
     if (!opt) {
         return;
     }
@@ -491,11 +558,13 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
     if (val < 0 || *endptr) {
         error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                    "a size value representible as a non-negative int64");
+        g_free(key);
         return;
     }
 
     *obj = val;
-    processed(ov, name);
+    processed(ov, key);
+    g_free(key);
 }
 
 
@@ -506,7 +575,7 @@ opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
 
     /* we only support a single mandatory scalar field in a list node */
     assert(ov->list_mode == LM_NONE);
-    *present = (lookup_distinct(ov, name, NULL) != NULL);
+    *present = (lookup_distinct(ov, name, NULL, NULL) != NULL);
 }
 
 
@@ -517,6 +586,8 @@ opts_visitor_new(const QemuOpts *opts)
 
     ov = g_malloc0(sizeof *ov);
 
+    ov->nested_names = g_queue_new();
+
     ov->visitor.start_struct = &opts_start_struct;
     ov->visitor.end_struct   = &opts_end_struct;
 
@@ -560,6 +631,7 @@ opts_visitor_cleanup(OptsVisitor *ov)
     if (ov->unprocessed_opts != NULL) {
         g_hash_table_destroy(ov->unprocessed_opts);
     }
+    g_queue_free(ov->nested_names);
     g_free(ov->fake_id_opt);
     g_free(ov);
 }
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
index c7eaa86..a818eff 100644
--- a/tests/qapi-schema/qapi-schema-test.json
+++ b/tests/qapi-schema/qapi-schema-test.json
@@ -81,6 +81,11 @@
 { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
   'returns': 'int' }
 
+# For testing hierarchy support in opts-visitor
+{ 'struct': 'UserDefOptionsSub',
+  'data': {
+    '*nint': 'int' } }
+
 # For testing integer range flattening in opts-visitor. The following schema
 # corresponds to the option format:
 #
@@ -94,7 +99,9 @@
     '*u64' : [ 'uint64' ],
     '*u16' : [ 'uint16' ],
     '*i64x':   'int'     ,
-    '*u64x':   'uint64'  } }
+    '*u64x':   'uint64'  ,
+    'sub0':    'UserDefOptionsSub',
+    'sub1':    'UserDefOptionsSub' } }
 
 # testing event
 { 'struct': 'EventStructOne',
diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
index 1c753d9..4393266 100644
--- a/tests/test-opts-visitor.c
+++ b/tests/test-opts-visitor.c
@@ -178,6 +178,34 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
     g_assert(f->userdef->u64->value == UINT64_MAX);
 }
 
+static void
+expect_both(OptsVisitorFixture *f, gconstpointer test_data)
+{
+    expect_ok(f, test_data);
+    g_assert(f->userdef->sub0->has_nint);
+    g_assert(f->userdef->sub0->nint == 13);
+    g_assert(f->userdef->sub1->has_nint);
+    g_assert(f->userdef->sub1->nint == 17);
+}
+
+static void
+expect_sub0(OptsVisitorFixture *f, gconstpointer test_data)
+{
+    expect_ok(f, test_data);
+    g_assert(f->userdef->sub0->has_nint);
+    g_assert(f->userdef->sub0->nint == 13);
+    g_assert(!f->userdef->sub1->has_nint);
+}
+
+static void
+expect_sub1(OptsVisitorFixture *f, gconstpointer test_data)
+{
+    expect_ok(f, test_data);
+    g_assert(!f->userdef->sub0->has_nint);
+    g_assert(f->userdef->sub1->has_nint);
+    g_assert(f->userdef->sub1->nint == 13);
+}
+
 /* test cases */
 
 int
@@ -271,6 +299,12 @@ main(int argc, char **argv)
     add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
              "i64=-0x8000000000000000-0x7fffffffffffffff");
 
+    /* Test nested structs support */
+    add_test("/visitor/opts/nested/unqualified", &expect_fail, "nint=13");
+    add_test("/visitor/opts/nested/both",        &expect_both,
+             "sub0.nint=13,sub1.nint=17");
+    add_test("/visitor/opts/nested/sub0",        &expect_sub0, "sub0.nint=13");
+    add_test("/visitor/opts/nested/sub1",        &expect_sub1, "sub1.nint=13");
     g_test_run();
     return 0;
 }
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 09/25] audio: use qapi AudioFormat instead of audfmt_e
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (7 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 16:00   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 10/25] audio: -audiodev command line option Kővágó, Zoltán
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Peter Maydell, Michael Walle, Gerd Hoffmann

I had to include an enum for audio sampling formats into qapi, but that
meant duplicating the audfmt_e enum.  This patch replaces audfmt_e and
associated values with the qapi generated AudioFormat enum.

This patch is mostly a search-and-replace, except for switches where the
qapi generated AUDIO_FORMAT_MAX caused problems.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/alsaaudio.c         | 53 ++++++++++++++------------
 audio/audio.c             | 97 ++++++++++++++++++++++++++---------------------
 audio/audio.h             | 11 +-----
 audio/audio_win_int.c     | 18 ++++-----
 audio/ossaudio.c          | 30 +++++++--------
 audio/paaudio.c           | 28 +++++++-------
 audio/sdlaudio.c          | 26 ++++++-------
 audio/spiceaudio.c        |  4 +-
 audio/wavaudio.c          | 17 +++++----
 audio/wavcapture.c        |  2 +-
 hw/arm/omap2.c            |  2 +-
 hw/audio/ac97.c           |  2 +-
 hw/audio/adlib.c          |  2 +-
 hw/audio/cs4231a.c        |  6 +--
 hw/audio/es1370.c         |  4 +-
 hw/audio/gus.c            |  2 +-
 hw/audio/hda-codec.c      | 18 ++++-----
 hw/audio/lm4549.c         |  6 +--
 hw/audio/milkymist-ac97.c |  2 +-
 hw/audio/pcspk.c          |  2 +-
 hw/audio/sb16.c           | 14 +++----
 hw/audio/wm8750.c         |  4 +-
 hw/input/tsc210x.c        |  2 +-
 hw/usb/dev-audio.c        |  2 +-
 ui/vnc.c                  | 14 +++----
 25 files changed, 187 insertions(+), 181 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 6315b2d..2b28b99 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -88,7 +88,7 @@ struct alsa_params_req {
 
 struct alsa_params_obt {
     int freq;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int endianness;
     int nchannels;
     snd_pcm_uframes_t samples;
@@ -295,16 +295,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
+static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 {
     switch (fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return SND_PCM_FORMAT_S8;
 
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return SND_PCM_FORMAT_U8;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         if (endianness) {
             return SND_PCM_FORMAT_S16_BE;
         }
@@ -312,7 +312,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
             return SND_PCM_FORMAT_S16_LE;
         }
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         if (endianness) {
             return SND_PCM_FORMAT_U16_BE;
         }
@@ -320,7 +320,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
             return SND_PCM_FORMAT_U16_LE;
         }
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         if (endianness) {
             return SND_PCM_FORMAT_S32_BE;
         }
@@ -328,7 +328,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
             return SND_PCM_FORMAT_S32_LE;
         }
 
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         if (endianness) {
             return SND_PCM_FORMAT_U32_BE;
         }
@@ -345,58 +345,58 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
     }
 }
 
-static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
                            int *endianness)
 {
     switch (alsafmt) {
     case SND_PCM_FORMAT_S8:
         *endianness = 0;
-        *fmt = AUD_FMT_S8;
+        *fmt = AUDIO_FORMAT_S8;
         break;
 
     case SND_PCM_FORMAT_U8:
         *endianness = 0;
-        *fmt = AUD_FMT_U8;
+        *fmt = AUDIO_FORMAT_U8;
         break;
 
     case SND_PCM_FORMAT_S16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case SND_PCM_FORMAT_U16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case SND_PCM_FORMAT_S16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case SND_PCM_FORMAT_U16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case SND_PCM_FORMAT_S32_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_S32;
+        *fmt = AUDIO_FORMAT_S32;
         break;
 
     case SND_PCM_FORMAT_U32_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_U32;
+        *fmt = AUDIO_FORMAT_U32;
         break;
 
     case SND_PCM_FORMAT_S32_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_S32;
+        *fmt = AUDIO_FORMAT_S32;
         break;
 
     case SND_PCM_FORMAT_U32_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_U32;
+        *fmt = AUDIO_FORMAT_U32;
         break;
 
     default:
@@ -639,19 +639,22 @@ static int alsa_open (int in, struct alsa_params_req *req,
         bytes_per_sec = freq << (nchannels == 2);
 
         switch (obt->fmt) {
-        case AUD_FMT_S8:
-        case AUD_FMT_U8:
+        case AUDIO_FORMAT_S8:
+        case AUDIO_FORMAT_U8:
             break;
 
-        case AUD_FMT_S16:
-        case AUD_FMT_U16:
+        case AUDIO_FORMAT_S16:
+        case AUDIO_FORMAT_U16:
             bytes_per_sec <<= 1;
             break;
 
-        case AUD_FMT_S32:
-        case AUD_FMT_U32:
+        case AUDIO_FORMAT_S32:
+        case AUDIO_FORMAT_U32:
             bytes_per_sec <<= 2;
             break;
+
+        default:
+            abort();
         }
 
         threshold = (conf->threshold * bytes_per_sec) / 1000;
diff --git a/audio/audio.c b/audio/audio.c
index 5be4b15..334c935 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -75,7 +75,7 @@ static struct {
         .settings = {
             .freq = 44100,
             .nchannels = 2,
-            .fmt = AUD_FMT_S16,
+            .fmt = AUDIO_FORMAT_S16,
             .endianness =  AUDIO_HOST_ENDIANNESS,
         }
     },
@@ -87,7 +87,7 @@ static struct {
         .settings = {
             .freq = 44100,
             .nchannels = 2,
-            .fmt = AUD_FMT_S16,
+            .fmt = AUDIO_FORMAT_S16,
             .endianness = AUDIO_HOST_ENDIANNESS,
         }
     },
@@ -219,58 +219,61 @@ static char *audio_alloc_prefix (const char *s)
     return r;
 }
 
-static const char *audio_audfmt_to_string (audfmt_e fmt)
+static const char *audio_audfmt_to_string (AudioFormat fmt)
 {
     switch (fmt) {
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return "U8";
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         return "U16";
 
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return "S8";
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         return "S16";
 
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         return "U32";
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         return "S32";
+
+    default:
+        abort();
     }
 
     dolog ("Bogus audfmt %d returning S16\n", fmt);
     return "S16";
 }
 
-static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
+static AudioFormat audio_string_to_audfmt (const char *s, AudioFormat defval,
                                         int *defaultp)
 {
     if (!strcasecmp (s, "u8")) {
         *defaultp = 0;
-        return AUD_FMT_U8;
+        return AUDIO_FORMAT_U8;
     }
     else if (!strcasecmp (s, "u16")) {
         *defaultp = 0;
-        return AUD_FMT_U16;
+        return AUDIO_FORMAT_U16;
     }
     else if (!strcasecmp (s, "u32")) {
         *defaultp = 0;
-        return AUD_FMT_U32;
+        return AUDIO_FORMAT_U32;
     }
     else if (!strcasecmp (s, "s8")) {
         *defaultp = 0;
-        return AUD_FMT_S8;
+        return AUDIO_FORMAT_S8;
     }
     else if (!strcasecmp (s, "s16")) {
         *defaultp = 0;
-        return AUD_FMT_S16;
+        return AUDIO_FORMAT_S16;
     }
     else if (!strcasecmp (s, "s32")) {
         *defaultp = 0;
-        return AUD_FMT_S32;
+        return AUDIO_FORMAT_S32;
     }
     else {
         dolog ("Bogus audio format `%s' using %s\n",
@@ -280,8 +283,8 @@ static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
     }
 }
 
-static audfmt_e audio_get_conf_fmt (const char *envname,
-                                    audfmt_e defval,
+static AudioFormat audio_get_conf_fmt (const char *envname,
+                                    AudioFormat defval,
                                     int *defaultp)
 {
     const char *var = getenv (envname);
@@ -384,7 +387,7 @@ static void audio_print_options (const char *prefix,
 
         case AUD_OPT_FMT:
             {
-                audfmt_e *fmtp = opt->valp;
+                AudioFormat *fmtp = opt->valp;
                 printf (
                     "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
                     state,
@@ -471,7 +474,7 @@ static void audio_process_options (const char *prefix,
 
         case AUD_OPT_FMT:
             {
-                audfmt_e *fmtp = opt->valp;
+                AudioFormat *fmtp = opt->valp;
                 *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
             }
             break;
@@ -502,22 +505,22 @@ static void audio_print_settings (struct audsettings *as)
     dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         AUD_log (NULL, "S8");
         break;
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         AUD_log (NULL, "U8");
         break;
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         AUD_log (NULL, "S16");
         break;
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         AUD_log (NULL, "U16");
         break;
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         AUD_log (NULL, "S32");
         break;
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         AUD_log (NULL, "U32");
         break;
     default:
@@ -548,12 +551,12 @@ static int audio_validate_settings (struct audsettings *as)
     invalid |= as->endianness != 0 && as->endianness != 1;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         break;
     default:
         invalid = 1;
@@ -569,25 +572,28 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
     int bits = 8, sign = 0;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         sign = 1;
         /* fall through */
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         break;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         sign = 1;
         /* fall through */
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         bits = 16;
         break;
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         sign = 1;
         /* fall through */
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         bits = 32;
         break;
+
+    default:
+        abort();
     }
     return info->freq == as->freq
         && info->nchannels == as->nchannels
@@ -601,24 +607,27 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
     int bits = 8, sign = 0, shift = 0;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         sign = 1;
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         break;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         sign = 1;
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         bits = 16;
         shift = 1;
         break;
 
-    case AUD_FMT_S32:
+    case AUDIO_FORMAT_S32:
         sign = 1;
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_U32:
         bits = 32;
         shift = 2;
         break;
+
+    default:
+        abort();
     }
 
     info->freq = as->freq;
diff --git a/audio/audio.h b/audio/audio.h
index e7ea397..e300511 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -29,15 +29,6 @@
 
 typedef void (*audio_callback_fn) (void *opaque, int avail);
 
-typedef enum {
-    AUD_FMT_U8,
-    AUD_FMT_S8,
-    AUD_FMT_U16,
-    AUD_FMT_S16,
-    AUD_FMT_U32,
-    AUD_FMT_S32
-} audfmt_e;
-
 #ifdef HOST_WORDS_BIGENDIAN
 #define AUDIO_HOST_ENDIANNESS 1
 #else
@@ -47,7 +38,7 @@ typedef enum {
 struct audsettings {
     int freq;
     int nchannels;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int endianness;
 };
 
diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c
index e132405..a8cfa77 100644
--- a/audio/audio_win_int.c
+++ b/audio/audio_win_int.c
@@ -23,20 +23,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
     wfx->cbSize = 0;
 
     switch (as->fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
         wfx->wBitsPerSample = 8;
         break;
 
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
         wfx->wBitsPerSample = 16;
         wfx->nAvgBytesPerSec <<= 1;
         wfx->nBlockAlign <<= 1;
         break;
 
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         wfx->wBitsPerSample = 32;
         wfx->nAvgBytesPerSec <<= 2;
         wfx->nBlockAlign <<= 2;
@@ -84,15 +84,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
 
     switch (wfx->wBitsPerSample) {
     case 8:
-        as->fmt = AUD_FMT_U8;
+        as->fmt = AUDIO_FORMAT_U8;
         break;
 
     case 16:
-        as->fmt = AUD_FMT_S16;
+        as->fmt = AUDIO_FORMAT_S16;
         break;
 
     case 32:
-        as->fmt = AUD_FMT_S32;
+        as->fmt = AUDIO_FORMAT_S32;
         break;
 
     default:
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 7dbe333..3ea7d27 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -72,7 +72,7 @@ typedef struct OSSVoiceIn {
 
 struct oss_params {
     int freq;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int nchannels;
     int nfrags;
     int fragsize;
@@ -150,16 +150,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static int aud_to_ossfmt (audfmt_e fmt, int endianness)
+static int aud_to_ossfmt (AudioFormat fmt, int endianness)
 {
     switch (fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return AFMT_S8;
 
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return AFMT_U8;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         if (endianness) {
             return AFMT_S16_BE;
         }
@@ -167,7 +167,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
             return AFMT_S16_LE;
         }
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         if (endianness) {
             return AFMT_U16_BE;
         }
@@ -184,37 +184,37 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
     }
 }
 
-static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
+static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness)
 {
     switch (ossfmt) {
     case AFMT_S8:
         *endianness = 0;
-        *fmt = AUD_FMT_S8;
+        *fmt = AUDIO_FORMAT_S8;
         break;
 
     case AFMT_U8:
         *endianness = 0;
-        *fmt = AUD_FMT_U8;
+        *fmt = AUDIO_FORMAT_U8;
         break;
 
     case AFMT_S16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AFMT_U16_LE:
         *endianness = 0;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case AFMT_S16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AFMT_U16_BE:
         *endianness = 1;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     default:
@@ -502,7 +502,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     int endianness;
     int err;
     int fd;
-    audfmt_e effective_fmt;
+    AudioFormat effective_fmt;
     struct audsettings obt_as;
     OSSConf *conf = drv_opaque;
 
@@ -671,7 +671,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     int endianness;
     int err;
     int fd;
-    audfmt_e effective_fmt;
+    AudioFormat effective_fmt;
     struct audsettings obt_as;
     OSSConf *conf = drv_opaque;
 
diff --git a/audio/paaudio.c b/audio/paaudio.c
index fea6071..cfdbdc6 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -384,21 +384,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
     return audio_pcm_sw_read (sw, buf, len);
 }
 
-static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
+static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
 {
     int format;
 
     switch (afmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
         format = PA_SAMPLE_U8;
         break;
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
         format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
         break;
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
         break;
     default:
@@ -409,26 +409,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
     return format;
 }
 
-static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
+static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 {
     switch (fmt) {
     case PA_SAMPLE_U8:
-        return AUD_FMT_U8;
+        return AUDIO_FORMAT_U8;
     case PA_SAMPLE_S16BE:
         *endianness = 1;
-        return AUD_FMT_S16;
+        return AUDIO_FORMAT_S16;
     case PA_SAMPLE_S16LE:
         *endianness = 0;
-        return AUD_FMT_S16;
+        return AUDIO_FORMAT_S16;
     case PA_SAMPLE_S32BE:
         *endianness = 1;
-        return AUD_FMT_S32;
+        return AUDIO_FORMAT_S32;
     case PA_SAMPLE_S32LE:
         *endianness = 0;
-        return AUD_FMT_S32;
+        return AUDIO_FORMAT_S32;
     default:
         dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
-        return AUD_FMT_U8;
+        return AUDIO_FORMAT_U8;
     }
 }
 
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index 1140f2e..db0f95a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -115,19 +115,19 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
     return sdl_post (s, forfn);
 }
 
-static int aud_to_sdlfmt (audfmt_e fmt)
+static int aud_to_sdlfmt (AudioFormat fmt)
 {
     switch (fmt) {
-    case AUD_FMT_S8:
+    case AUDIO_FORMAT_S8:
         return AUDIO_S8;
 
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_U8:
         return AUDIO_U8;
 
-    case AUD_FMT_S16:
+    case AUDIO_FORMAT_S16:
         return AUDIO_S16LSB;
 
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_U16:
         return AUDIO_U16LSB;
 
     default:
@@ -139,37 +139,37 @@ static int aud_to_sdlfmt (audfmt_e fmt)
     }
 }
 
-static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
+static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
 {
     switch (sdlfmt) {
     case AUDIO_S8:
         *endianness = 0;
-        *fmt = AUD_FMT_S8;
+        *fmt = AUDIO_FORMAT_S8;
         break;
 
     case AUDIO_U8:
         *endianness = 0;
-        *fmt = AUD_FMT_U8;
+        *fmt = AUDIO_FORMAT_U8;
         break;
 
     case AUDIO_S16LSB:
         *endianness = 0;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AUDIO_U16LSB:
         *endianness = 0;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     case AUDIO_S16MSB:
         *endianness = 1;
-        *fmt = AUD_FMT_S16;
+        *fmt = AUDIO_FORMAT_S16;
         break;
 
     case AUDIO_U16MSB:
         *endianness = 1;
-        *fmt = AUD_FMT_U16;
+        *fmt = AUDIO_FORMAT_U16;
         break;
 
     default:
@@ -341,7 +341,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     SDL_AudioSpec req, obt;
     int endianness;
     int err;
-    audfmt_e effective_fmt;
+    AudioFormat effective_fmt;
     struct audsettings obt_as;
 
     req.freq = as->freq;
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 42ae4a4..141fd8d 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -128,7 +128,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
     settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ;
 #endif
     settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN;
-    settings.fmt        = AUD_FMT_S16;
+    settings.fmt        = AUDIO_FORMAT_S16;
     settings.endianness = AUDIO_HOST_ENDIANNESS;
 
     audio_pcm_init_info (&hw->info, &settings);
@@ -256,7 +256,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     settings.freq       = SPICE_INTERFACE_RECORD_FREQ;
 #endif
     settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN;
-    settings.fmt        = AUD_FMT_S16;
+    settings.fmt        = AUDIO_FORMAT_S16;
     settings.endianness = AUDIO_HOST_ENDIANNESS;
 
     audio_pcm_init_info (&hw->info, &settings);
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index c586020..81250e6 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -116,20 +116,23 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
 
     stereo = wav_as.nchannels == 2;
     switch (wav_as.fmt) {
-    case AUD_FMT_S8:
-    case AUD_FMT_U8:
+    case AUDIO_FORMAT_S8:
+    case AUDIO_FORMAT_U8:
         bits16 = 0;
         break;
 
-    case AUD_FMT_S16:
-    case AUD_FMT_U16:
+    case AUDIO_FORMAT_S16:
+    case AUDIO_FORMAT_U16:
         bits16 = 1;
         break;
 
-    case AUD_FMT_S32:
-    case AUD_FMT_U32:
+    case AUDIO_FORMAT_S32:
+    case AUDIO_FORMAT_U32:
         dolog ("WAVE files can not handle 32bit formats\n");
         return -1;
+
+    default:
+        abort();
     }
 
     hdr[34] = bits16 ? 0x10 : 0x08;
@@ -224,7 +227,7 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
 static WAVConf glob_conf = {
     .settings.freq      = 44100,
     .settings.nchannels = 2,
-    .settings.fmt       = AUD_FMT_S16,
+    .settings.fmt       = AUDIO_FORMAT_S16,
     .wav_path           = "qemu.wav"
 };
 
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index 86e9056..798acdd 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -136,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
 
     as.freq = freq;
     as.nchannels = 1 << stereo;
-    as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+    as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
     as.endianness = 0;
 
     ops.notify = wav_notify;
diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c
index e39b317..3b14a5d 100644
--- a/hw/arm/omap2.c
+++ b/hw/arm/omap2.c
@@ -269,7 +269,7 @@ static void omap_eac_format_update(struct omap_eac_s *s)
      * does I2S specify it?  */
     /* All register writes are 16 bits so we we store 16-bit samples
      * in the buffers regardless of AGCFR[B8_16] value.  */
-    fmt.fmt = AUD_FMT_U16;
+    fmt.fmt = AUDIO_FORMAT_U16;
 
     s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice,
                     "eac.codec.in", s, omap_eac_in_cb, &fmt);
diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c
index b173835..fa75f33 100644
--- a/hw/audio/ac97.c
+++ b/hw/audio/ac97.c
@@ -360,7 +360,7 @@ static void open_voice (AC97LinkState *s, int index, int freq)
 
     as.freq = freq;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 0;
 
     if (freq > 0) {
diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
index 656eb37..f8f0f55 100644
--- a/hw/audio/adlib.c
+++ b/hw/audio/adlib.c
@@ -323,7 +323,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
 
     as.freq = s->freq;
     as.nchannels = SHIFT;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = AUDIO_HOST_ENDIANNESS;
 
     AUD_register_card ("adlib", &s->card);
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
index f96f561..626a173 100644
--- a/hw/audio/cs4231a.c
+++ b/hw/audio/cs4231a.c
@@ -284,7 +284,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
 
     switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
     case 0:
-        as.fmt = AUD_FMT_U8;
+        as.fmt = AUDIO_FORMAT_U8;
         s->shift = as.nchannels == 2;
         break;
 
@@ -294,7 +294,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
     case 3:
         s->tab = ALawDecompressTable;
     x_law:
-        as.fmt = AUD_FMT_S16;
+        as.fmt = AUDIO_FORMAT_S16;
         as.endianness = AUDIO_HOST_ENDIANNESS;
         s->shift = as.nchannels == 2;
         break;
@@ -302,7 +302,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
     case 6:
         as.endianness = 1;
     case 2:
-        as.fmt = AUD_FMT_S16;
+        as.fmt = AUDIO_FORMAT_S16;
         s->shift = as.nchannels;
         break;
 
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index 8e7bcf5..f6e74cb 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -414,14 +414,14 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
                     i,
                     new_freq,
                     1 << (new_fmt & 1),
-                    (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
+                    (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8,
                     d->shift);
             if (new_freq) {
                 struct audsettings as;
 
                 as.freq = new_freq;
                 as.nchannels = 1 << (new_fmt & 1);
-                as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+                as.fmt = (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8;
                 as.endianness = 0;
 
                 if (i == ADC_CHANNEL) {
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index 86223a9..6107824 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -242,7 +242,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
 
     as.freq = s->freq;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = GUS_ENDIANNESS;
 
     s->voice = AUD_open_out (
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 3c03ff5..8693b7a 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -97,9 +97,9 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
     }
 
     switch (format & AC_FMT_BITS_MASK) {
-    case AC_FMT_BITS_8:  as->fmt = AUD_FMT_S8;  break;
-    case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
-    case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
+    case AC_FMT_BITS_8:  as->fmt = AUDIO_FORMAT_S8;  break;
+    case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break;
+    case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break;
     }
 
     as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
@@ -128,12 +128,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
 /* -------------------------------------------------------------------------- */
 
 static const char *fmt2name[] = {
-    [ AUD_FMT_U8  ] = "PCM-U8",
-    [ AUD_FMT_S8  ] = "PCM-S8",
-    [ AUD_FMT_U16 ] = "PCM-U16",
-    [ AUD_FMT_S16 ] = "PCM-S16",
-    [ AUD_FMT_U32 ] = "PCM-U32",
-    [ AUD_FMT_S32 ] = "PCM-S32",
+    [ AUDIO_FORMAT_U8  ] = "PCM-U8",
+    [ AUDIO_FORMAT_S8  ] = "PCM-S8",
+    [ AUDIO_FORMAT_U16 ] = "PCM-U16",
+    [ AUDIO_FORMAT_S16 ] = "PCM-S16",
+    [ AUDIO_FORMAT_U32 ] = "PCM-U32",
+    [ AUDIO_FORMAT_S32 ] = "PCM-S32",
 };
 
 typedef struct HDAAudioState HDAAudioState;
diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c
index 380ef60..9d4f4b5 100644
--- a/hw/audio/lm4549.c
+++ b/hw/audio/lm4549.c
@@ -185,7 +185,7 @@ void lm4549_write(lm4549_state *s,
         struct audsettings as;
         as.freq = value;
         as.nchannels = 2;
-        as.fmt = AUD_FMT_S16;
+        as.fmt = AUDIO_FORMAT_S16;
         as.endianness = 0;
 
         s->voice = AUD_open_out(
@@ -255,7 +255,7 @@ static int lm4549_post_load(void *opaque, int version_id)
     struct audsettings as;
     as.freq = freq;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 0;
 
     s->voice = AUD_open_out(
@@ -292,7 +292,7 @@ void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
     /* Open a default voice */
     as.freq = 48000;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 0;
 
     s->voice = AUD_open_out(
diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c
index 28f55e8..15169e2 100644
--- a/hw/audio/milkymist-ac97.c
+++ b/hw/audio/milkymist-ac97.c
@@ -297,7 +297,7 @@ static int milkymist_ac97_init(SysBusDevice *dev)
 
     as.freq = 48000;
     as.nchannels = 2;
-    as.fmt = AUD_FMT_S16;
+    as.fmt = AUDIO_FORMAT_S16;
     as.endianness = 1;
 
     s->voice_in = AUD_open_in(&s->card, s->voice_in,
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
index 5266fb5..302debf 100644
--- a/hw/audio/pcspk.c
+++ b/hw/audio/pcspk.c
@@ -112,7 +112,7 @@ static void pcspk_callback(void *opaque, int free)
 static int pcspk_audio_init(ISABus *bus)
 {
     PCSpkState *s = pcspk_state;
-    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
+    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0};
 
     AUD_register_card(s_spk, &s->card);
 
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index b052de5..a159dcc 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -66,7 +66,7 @@ typedef struct SB16State {
     int fmt_stereo;
     int fmt_signed;
     int fmt_bits;
-    audfmt_e fmt;
+    AudioFormat fmt;
     int dma_auto;
     int block_size;
     int fifo;
@@ -221,7 +221,7 @@ static void continue_dma8 (SB16State *s)
 
 static void dma_cmd8 (SB16State *s, int mask, int dma_len)
 {
-    s->fmt = AUD_FMT_U8;
+    s->fmt = AUDIO_FORMAT_U8;
     s->use_hdma = 0;
     s->fmt_bits = 8;
     s->fmt_signed = 0;
@@ -316,18 +316,18 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
 
     if (16 == s->fmt_bits) {
         if (s->fmt_signed) {
-            s->fmt = AUD_FMT_S16;
+            s->fmt = AUDIO_FORMAT_S16;
         }
         else {
-            s->fmt = AUD_FMT_U16;
+            s->fmt = AUDIO_FORMAT_U16;
         }
     }
     else {
         if (s->fmt_signed) {
-            s->fmt = AUD_FMT_S8;
+            s->fmt = AUDIO_FORMAT_S8;
         }
         else {
-            s->fmt = AUD_FMT_U8;
+            s->fmt = AUDIO_FORMAT_U8;
         }
     }
 
@@ -839,7 +839,7 @@ static void legacy_reset (SB16State *s)
 
     as.freq = s->freq;
     as.nchannels = 1;
-    as.fmt = AUD_FMT_U8;
+    as.fmt = AUDIO_FORMAT_U8;
     as.endianness = 0;
 
     s->voice = AUD_open_out (
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
index b50b331..4c4333c 100644
--- a/hw/audio/wm8750.c
+++ b/hw/audio/wm8750.c
@@ -201,7 +201,7 @@ static void wm8750_set_format(WM8750State *s)
     in_fmt.endianness = 0;
     in_fmt.nchannels = 2;
     in_fmt.freq = s->adc_hz;
-    in_fmt.fmt = AUD_FMT_S16;
+    in_fmt.fmt = AUDIO_FORMAT_S16;
 
     s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
                     CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
@@ -214,7 +214,7 @@ static void wm8750_set_format(WM8750State *s)
     out_fmt.endianness = 0;
     out_fmt.nchannels = 2;
     out_fmt.freq = s->dac_hz;
-    out_fmt.fmt = AUD_FMT_S16;
+    out_fmt.fmt = AUDIO_FORMAT_S16;
 
     s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
                     CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c
index fae3385..3cf938b 100644
--- a/hw/input/tsc210x.c
+++ b/hw/input/tsc210x.c
@@ -315,7 +315,7 @@ static void tsc2102_audio_output_update(TSC210xState *s)
     fmt.endianness = 0;
     fmt.nchannels = 2;
     fmt.freq = s->codec.tx_rate;
-    fmt.fmt = AUD_FMT_S16;
+    fmt.fmt = AUDIO_FORMAT_S16;
 
     s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
                     "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index f092bb8..0171579 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -646,7 +646,7 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
     s->out.vol[1]        = 240; /* 0 dB */
     s->out.as.freq       = USBAUDIO_SAMPLE_RATE;
     s->out.as.nchannels  = 2;
-    s->out.as.fmt        = AUD_FMT_S16;
+    s->out.as.fmt        = AUDIO_FORMAT_S16;
     s->out.as.endianness = 0;
     streambuf_init(&s->out.buf, s->buffer);
 
diff --git a/ui/vnc.c b/ui/vnc.c
index e26973a..febbad6 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -2363,12 +2363,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
                 if (len == 4)
                     return 10;
                 switch (read_u8(data, 4)) {
-                case 0: vs->as.fmt = AUD_FMT_U8; break;
-                case 1: vs->as.fmt = AUD_FMT_S8; break;
-                case 2: vs->as.fmt = AUD_FMT_U16; break;
-                case 3: vs->as.fmt = AUD_FMT_S16; break;
-                case 4: vs->as.fmt = AUD_FMT_U32; break;
-                case 5: vs->as.fmt = AUD_FMT_S32; break;
+                case 0: vs->as.fmt = AUDIO_FORMAT_U8; break;
+                case 1: vs->as.fmt = AUDIO_FORMAT_S8; break;
+                case 2: vs->as.fmt = AUDIO_FORMAT_U16; break;
+                case 3: vs->as.fmt = AUDIO_FORMAT_S16; break;
+                case 4: vs->as.fmt = AUDIO_FORMAT_U32; break;
+                case 5: vs->as.fmt = AUDIO_FORMAT_S32; break;
                 default:
                     VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
                     vnc_client_error(vs);
@@ -3069,7 +3069,7 @@ void vnc_init_state(VncState *vs)
 
     vs->as.freq = 44100;
     vs->as.nchannels = 2;
-    vs->as.fmt = AUD_FMT_S16;
+    vs->as.fmt = AUDIO_FORMAT_S16;
     vs->as.endianness = 0;
 
     qemu_mutex_init(&vs->output_mutex);
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 10/25] audio: -audiodev command line option
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (8 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 09/25] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 17:17   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 11/25] audio: reduce glob_audio_state usage Kővágó, Zoltán
                   ` (14 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Paolo Bonzini, Gerd Hoffmann

This patch adds an -audiodev command line option, and deprecates the
QEMU_* environment variables for audio backend configuration.  It's
syntax is similar to existing options (-netdev, -device, etc): -audiodev
driver_name,property=value,...

Audio drivers now get an Audiodev * as config paramters, instead of the
global audio_option structs.  There is some code in audio/audio_legacy.c
that converts the old environment variables to audiodev options (this
way backends do not have to worry about legacy options).  It also
contains a replacement of -audio-help, which prints out the equivalent
-audiodev based config of the currently specified environment variables.

Although now it's possible to specify multiple -audiodev options on
command line, multiple audio backends are not supported yet.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/Makefile.objs     |   2 +-
 audio/alsaaudio.c       | 311 ++++++--------------
 audio/audio.c           | 760 ++++++++++++++----------------------------------
 audio/audio.h           |  23 +-
 audio/audio_int.h       |  23 +-
 audio/audio_legacy.c    | 328 +++++++++++++++++++++
 audio/audio_template.h  |  13 +-
 audio/coreaudio.c       |  49 +---
 audio/dsound_template.h |   6 +-
 audio/dsoundaudio.c     |  60 ++--
 audio/noaudio.c         |   3 +-
 audio/ossaudio.c        | 155 +++-------
 audio/paaudio.c         |  82 ++----
 audio/sdlaudio.c        |  24 +-
 audio/spiceaudio.c      |   7 +-
 audio/wavaudio.c        |  60 +---
 qemu-options.hx         | 226 +++++++++++++-
 vl.c                    |  10 +-
 18 files changed, 1015 insertions(+), 1127 deletions(-)
 create mode 100644 audio/audio_legacy.c

diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index 481d1aa..9d8f579 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -1,4 +1,4 @@
-common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
+common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
 common-obj-$(CONFIG_SDL) += sdlaudio.o
 common-obj-$(CONFIG_OSS) += ossaudio.o
 common-obj-$(CONFIG_SPICE) += spiceaudio.o
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 2b28b99..cfe4aec 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -22,6 +22,7 @@
  * THE SOFTWARE.
  */
 #include <alsa/asoundlib.h>
+#include "qapi-visit.h"
 #include "qemu-common.h"
 #include "qemu/main-loop.h"
 #include "audio.h"
@@ -34,28 +35,9 @@
 #define AUDIO_CAP "alsa"
 #include "audio_int.h"
 
-typedef struct ALSAConf {
-    int size_in_usec_in;
-    int size_in_usec_out;
-    const char *pcm_name_in;
-    const char *pcm_name_out;
-    unsigned int buffer_size_in;
-    unsigned int period_size_in;
-    unsigned int buffer_size_out;
-    unsigned int period_size_out;
-    unsigned int threshold;
-
-    int buffer_size_in_overridden;
-    int period_size_in_overridden;
-
-    int buffer_size_out_overridden;
-    int period_size_out_overridden;
-} ALSAConf;
-
 struct pollhlp {
     snd_pcm_t *handle;
     struct pollfd *pfds;
-    ALSAConf *conf;
     int count;
     int mask;
 };
@@ -67,6 +49,7 @@ typedef struct ALSAVoiceOut {
     void *pcm_buf;
     snd_pcm_t *handle;
     struct pollhlp pollhlp;
+    Audiodev *dev;
 } ALSAVoiceOut;
 
 typedef struct ALSAVoiceIn {
@@ -74,16 +57,13 @@ typedef struct ALSAVoiceIn {
     snd_pcm_t *handle;
     void *pcm_buf;
     struct pollhlp pollhlp;
+    Audiodev *dev;
 } ALSAVoiceIn;
 
 struct alsa_params_req {
     int freq;
     snd_pcm_format_t fmt;
     int nchannels;
-    int size_in_usec;
-    int override_mask;
-    unsigned int buffer_size;
-    unsigned int period_size;
 };
 
 struct alsa_params_obt {
@@ -409,7 +389,8 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
 
 static void alsa_dump_info (struct alsa_params_req *req,
                             struct alsa_params_obt *obt,
-                            snd_pcm_format_t obtfmt)
+                            snd_pcm_format_t obtfmt,
+                            AudiodevPerDirectionOptions *pdo)
 {
     dolog ("parameter | requested value | obtained value\n");
     dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt);
@@ -417,8 +398,9 @@ static void alsa_dump_info (struct alsa_params_req *req,
            req->nchannels, obt->nchannels);
     dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
     dolog ("============================================\n");
-    dolog ("requested: buffer size %d period size %d\n",
-           req->buffer_size, req->period_size);
+    dolog ("requested: buffer size %" PRId64 " buffer count %" PRId64 "\n",
+           pdo->has_buffer_len ? pdo->buffer_len : 0,
+           pdo->has_buffer_len ? pdo->buffer_len : 0);
     dolog ("obtained: samples %ld\n", obt->samples);
 }
 
@@ -452,23 +434,25 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
     }
 }
 
-static int alsa_open (int in, struct alsa_params_req *req,
-                      struct alsa_params_obt *obt, snd_pcm_t **handlep,
-                      ALSAConf *conf)
+static int alsa_open(bool in, struct alsa_params_req *req,
+                     struct alsa_params_obt *obt, snd_pcm_t **handlep,
+                     Audiodev *dev)
 {
+    AudiodevPerDirectionOptions *pdo = in ? dev->in : dev->out;
+    AudiodevAlsaOptions *aopts = dev->alsa;
+    AudiodevAlsaPerDirectionOptions *apdo =
+        in ? aopts->alsa_in : aopts->alsa_out;
     snd_pcm_t *handle;
     snd_pcm_hw_params_t *hw_params;
     int err;
-    int size_in_usec;
     unsigned int freq, nchannels;
-    const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
+    const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
     snd_pcm_uframes_t obt_buffer_size;
     const char *typ = in ? "ADC" : "DAC";
     snd_pcm_format_t obtfmt;
 
     freq = req->freq;
     nchannels = req->nchannels;
-    size_in_usec = req->size_in_usec;
 
     snd_pcm_hw_params_alloca (&hw_params);
 
@@ -528,79 +512,49 @@ static int alsa_open (int in, struct alsa_params_req *req,
         goto err;
     }
 
-    if (req->buffer_size) {
-        unsigned long obt;
+    if (pdo->buffer_count) {
+        if (pdo->buffer_len) {
+            int64_t req = pdo->buffer_len * pdo->buffer_count;
 
-        if (size_in_usec) {
             int dir = 0;
-            unsigned int btime = req->buffer_size;
+            unsigned int btime = req;
 
-            err = snd_pcm_hw_params_set_buffer_time_near (
-                handle,
-                hw_params,
-                &btime,
-                &dir
-                );
-            obt = btime;
-        }
-        else {
-            snd_pcm_uframes_t bsize = req->buffer_size;
+            err = snd_pcm_hw_params_set_buffer_time_near(
+                handle, hw_params, &btime, &dir);
 
-            err = snd_pcm_hw_params_set_buffer_size_near (
-                handle,
-                hw_params,
-                &bsize
-                );
-            obt = bsize;
-        }
-        if (err < 0) {
-            alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
-                          size_in_usec ? "time" : "size", req->buffer_size);
-            goto err;
-        }
+            if (err < 0) {
+                alsa_logerr2(err, typ,
+                             "Failed to set buffer time to %" PRId64 "\n",
+                             req);
+                goto err;
+            }
 
-        if ((req->override_mask & 2) && (obt - req->buffer_size))
-            dolog ("Requested buffer %s %u was rejected, using %lu\n",
-                   size_in_usec ? "time" : "size", req->buffer_size, obt);
+            if (pdo->has_buffer_count && btime != req) {
+                dolog("Requested buffer time %" PRId64
+                      " was rejected, using %u\n", req, btime);
+            }
+        } else {
+            dolog("Can't set buffer_count without buffer_size!\n");
+        }
     }
 
-    if (req->period_size) {
-        unsigned long obt;
+    if (pdo->buffer_len) {
+        int dir = 0;
+        unsigned int ptime = pdo->buffer_len;
 
-        if (size_in_usec) {
-            int dir = 0;
-            unsigned int ptime = req->period_size;
-
-            err = snd_pcm_hw_params_set_period_time_near (
-                handle,
-                hw_params,
-                &ptime,
-                &dir
-                );
-            obt = ptime;
-        }
-        else {
-            int dir = 0;
-            snd_pcm_uframes_t psize = req->period_size;
-
-            err = snd_pcm_hw_params_set_period_size_near (
-                handle,
-                hw_params,
-                &psize,
-                &dir
-                );
-            obt = psize;
-        }
+        err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
+                                                     &dir);
 
         if (err < 0) {
-            alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
-                          size_in_usec ? "time" : "size", req->period_size);
+            alsa_logerr2(err, typ, "Failed to set period time to %" PRId64 "\n",
+                         pdo->buffer_len);
             goto err;
         }
 
-        if (((req->override_mask & 1) && (obt - req->period_size)))
-            dolog ("Requested period %s %u was rejected, using %lu\n",
-                   size_in_usec ? "time" : "size", req->period_size, obt);
+        if (pdo->has_buffer_len && ptime != pdo->buffer_len) {
+            dolog("Requested period time %" PRId64 " was rejected, using %d\n",
+                  pdo->buffer_len, ptime);
+        }
     }
 
     err = snd_pcm_hw_params (handle, hw_params);
@@ -632,33 +586,10 @@ static int alsa_open (int in, struct alsa_params_req *req,
         goto err;
     }
 
-    if (!in && conf->threshold) {
-        snd_pcm_uframes_t threshold;
-        int bytes_per_sec;
-
-        bytes_per_sec = freq << (nchannels == 2);
-
-        switch (obt->fmt) {
-        case AUDIO_FORMAT_S8:
-        case AUDIO_FORMAT_U8:
-            break;
-
-        case AUDIO_FORMAT_S16:
-        case AUDIO_FORMAT_U16:
-            bytes_per_sec <<= 1;
-            break;
-
-        case AUDIO_FORMAT_S32:
-        case AUDIO_FORMAT_U32:
-            bytes_per_sec <<= 2;
-            break;
-
-        default:
-            abort();
-        }
-
-        threshold = (conf->threshold * bytes_per_sec) / 1000;
-        alsa_set_threshold (handle, threshold);
+    if (!in && aopts->has_threshold && aopts->threshold) {
+        struct audsettings as = { .freq = freq };
+        alsa_set_threshold(handle,
+                           audio_buffer_frames(pdo, &as, aopts->threshold));
     }
 
     obt->nchannels = nchannels;
@@ -671,11 +602,11 @@ static int alsa_open (int in, struct alsa_params_req *req,
          obt->nchannels != req->nchannels ||
          obt->freq != req->freq) {
         dolog ("Audio parameters for %s\n", typ);
-        alsa_dump_info (req, obt, obtfmt);
+        alsa_dump_info (req, obt, obtfmt, pdo);
     }
 
 #ifdef DEBUG
-    alsa_dump_info (req, obt, obtfmt);
+    alsa_dump_info (req, obt, obtfmt, pdo);
 #endif
     return 0;
 
@@ -801,19 +732,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
     struct alsa_params_obt obt;
     snd_pcm_t *handle;
     struct audsettings obt_as;
-    ALSAConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
 
     req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.period_size = conf->period_size_out;
-    req.buffer_size = conf->buffer_size_out;
-    req.size_in_usec = conf->size_in_usec_out;
-    req.override_mask =
-        (conf->period_size_out_overridden ? 1 : 0) |
-        (conf->buffer_size_out_overridden ? 2 : 0);
 
-    if (alsa_open (0, &req, &obt, &handle, conf)) {
+    if (alsa_open (0, &req, &obt, &handle, dev)) {
         return -1;
     }
 
@@ -834,7 +759,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     alsa->handle = handle;
-    alsa->pollhlp.conf = conf;
+    alsa->dev = dev;
     return 0;
 }
 
@@ -874,16 +799,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
 static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_out;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
 
             ldebug ("enabling voice\n");
             if (poll_mode && alsa_poll_out (hw)) {
@@ -912,19 +833,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     struct alsa_params_obt obt;
     snd_pcm_t *handle;
     struct audsettings obt_as;
-    ALSAConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
 
     req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.period_size = conf->period_size_in;
-    req.buffer_size = conf->buffer_size_in;
-    req.size_in_usec = conf->size_in_usec_in;
-    req.override_mask =
-        (conf->period_size_in_overridden ? 1 : 0) |
-        (conf->buffer_size_in_overridden ? 2 : 0);
 
-    if (alsa_open (1, &req, &obt, &handle, conf)) {
+    if (alsa_open (1, &req, &obt, &handle, dev)) {
         return -1;
     }
 
@@ -945,7 +860,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     alsa->handle = handle;
-    alsa->pollhlp.conf = conf;
+    alsa->dev = dev;
     return 0;
 }
 
@@ -1087,16 +1002,12 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
 static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_in;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
 
             ldebug ("enabling voice\n");
             if (poll_mode && alsa_poll_in (hw)) {
@@ -1119,88 +1030,35 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return -1;
 }
 
-static ALSAConf glob_conf = {
-    .buffer_size_out = 4096,
-    .period_size_out = 1024,
-    .pcm_name_out = "default",
-    .pcm_name_in = "default",
-};
-
-static void *alsa_audio_init (void)
+static void *alsa_audio_init(Audiodev *dev)
 {
-    ALSAConf *conf = g_malloc(sizeof(ALSAConf));
-    *conf = glob_conf;
-    return conf;
+    assert(dev->kind == AUDIODEV_DRIVER_ALSA);
+
+    /* need to define them, as otherwise alsa produces no sound
+     * doesn't set has_* so alsa_open can identify it wasn't set by the user */
+    if (!dev->out->has_buffer_count) {
+        dev->out->buffer_count = 4;
+    }
+    if (!dev->out->has_buffer_len) {
+        dev->out->buffer_len = 23219; /* 1024 frames assuming 44100Hz */
+    }
+
+    /* OptsVisitor sets unspecified optional fields to zero, but do not depend
+     * on it... */
+    if (!dev->in->has_buffer_count) {
+        dev->in->buffer_count = 0;
+    }
+    if (!dev->in->has_buffer_len) {
+        dev->in->buffer_len = 0;
+    }
+
+    return dev;
 }
 
 static void alsa_audio_fini (void *opaque)
 {
-    g_free(opaque);
 }
 
-static struct audio_option alsa_options[] = {
-    {
-        .name        = "DAC_SIZE_IN_USEC",
-        .tag         = AUD_OPT_BOOL,
-        .valp        = &glob_conf.size_in_usec_out,
-        .descr       = "DAC period/buffer size in microseconds (otherwise in frames)"
-    },
-    {
-        .name        = "DAC_PERIOD_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.period_size_out,
-        .descr       = "DAC period size (0 to go with system default)",
-        .overriddenp = &glob_conf.period_size_out_overridden
-    },
-    {
-        .name        = "DAC_BUFFER_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.buffer_size_out,
-        .descr       = "DAC buffer size (0 to go with system default)",
-        .overriddenp = &glob_conf.buffer_size_out_overridden
-    },
-    {
-        .name        = "ADC_SIZE_IN_USEC",
-        .tag         = AUD_OPT_BOOL,
-        .valp        = &glob_conf.size_in_usec_in,
-        .descr       =
-        "ADC period/buffer size in microseconds (otherwise in frames)"
-    },
-    {
-        .name        = "ADC_PERIOD_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.period_size_in,
-        .descr       = "ADC period size (0 to go with system default)",
-        .overriddenp = &glob_conf.period_size_in_overridden
-    },
-    {
-        .name        = "ADC_BUFFER_SIZE",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.buffer_size_in,
-        .descr       = "ADC buffer size (0 to go with system default)",
-        .overriddenp = &glob_conf.buffer_size_in_overridden
-    },
-    {
-        .name        = "THRESHOLD",
-        .tag         = AUD_OPT_INT,
-        .valp        = &glob_conf.threshold,
-        .descr       = "(undocumented)"
-    },
-    {
-        .name        = "DAC_DEV",
-        .tag         = AUD_OPT_STR,
-        .valp        = &glob_conf.pcm_name_out,
-        .descr       = "DAC device name (for instance dmix)"
-    },
-    {
-        .name        = "ADC_DEV",
-        .tag         = AUD_OPT_STR,
-        .valp        = &glob_conf.pcm_name_in,
-        .descr       = "ADC device name"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops alsa_pcm_ops = {
     .init_out = alsa_init_out,
     .fini_out = alsa_fini_out,
@@ -1218,7 +1076,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
 struct audio_driver alsa_audio_driver = {
     .name           = "alsa",
     .descr          = "ALSA http://www.alsa-project.org",
-    .options        = alsa_options,
     .init           = alsa_audio_init,
     .fini           = alsa_audio_fini,
     .pcm_ops        = &alsa_pcm_ops,
diff --git a/audio/audio.c b/audio/audio.c
index 334c935..08ac15e 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -24,7 +24,10 @@
 #include "hw/hw.h"
 #include "audio.h"
 #include "monitor/monitor.h"
+#include "qapi-visit.h"
+#include "qapi/opts-visitor.h"
 #include "qemu/timer.h"
+#include "qemu/config-file.h"
 #include "sysemu/sysemu.h"
 
 #define AUDIO_CAP "audio"
@@ -42,59 +45,14 @@
    The 1st one is the one used by default, that is the reason
     that we generate the list.
 */
-static struct audio_driver *drvtab[] = {
+struct audio_driver *drvtab[] = {
 #ifdef CONFIG_SPICE
     &spice_audio_driver,
 #endif
     CONFIG_AUDIO_DRIVERS
     &no_audio_driver,
-    &wav_audio_driver
-};
-
-struct fixed_settings {
-    int enabled;
-    int nb_voices;
-    int greedy;
-    struct audsettings settings;
-};
-
-static struct {
-    struct fixed_settings fixed_out;
-    struct fixed_settings fixed_in;
-    union {
-        int hertz;
-        int64_t ticks;
-    } period;
-    int try_poll_in;
-    int try_poll_out;
-} conf = {
-    .fixed_out = { /* DAC fixed settings */
-        .enabled = 1,
-        .nb_voices = 1,
-        .greedy = 1,
-        .settings = {
-            .freq = 44100,
-            .nchannels = 2,
-            .fmt = AUDIO_FORMAT_S16,
-            .endianness =  AUDIO_HOST_ENDIANNESS,
-        }
-    },
-
-    .fixed_in = { /* ADC fixed settings */
-        .enabled = 1,
-        .nb_voices = 1,
-        .greedy = 1,
-        .settings = {
-            .freq = 44100,
-            .nchannels = 2,
-            .fmt = AUDIO_FORMAT_S16,
-            .endianness = AUDIO_HOST_ENDIANNESS,
-        }
-    },
-
-    .period = { .hertz = 100 },
-    .try_poll_in = 1,
-    .try_poll_out = 1,
+    &wav_audio_driver,
+    NULL
 };
 
 static AudioState glob_audio_state;
@@ -113,9 +71,6 @@ const struct mixeng_volume nominal_volume = {
 #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
 #error No its not
 #else
-static void audio_print_options (const char *prefix,
-                                 struct audio_option *opt);
-
 int audio_bug (const char *funcname, int cond)
 {
     if (cond) {
@@ -123,16 +78,9 @@ int audio_bug (const char *funcname, int cond)
 
         AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
         if (!shown) {
-            struct audio_driver *d;
-
             shown = 1;
             AUD_log (NULL, "Save all your work and restart without audio\n");
-            AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
             AUD_log (NULL, "I am sorry\n");
-            d = glob_audio_state.drv;
-            if (d) {
-                audio_print_options (d->name, d->options);
-            }
         }
         AUD_log (NULL, "Context:\n");
 
@@ -194,139 +142,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size)
     return g_malloc0 (len);
 }
 
-static char *audio_alloc_prefix (const char *s)
-{
-    const char qemu_prefix[] = "QEMU_";
-    size_t len, i;
-    char *r, *u;
-
-    if (!s) {
-        return NULL;
-    }
-
-    len = strlen (s);
-    r = g_malloc (len + sizeof (qemu_prefix));
-
-    u = r + sizeof (qemu_prefix) - 1;
-
-    pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
-    pstrcat (r, len + sizeof (qemu_prefix), s);
-
-    for (i = 0; i < len; ++i) {
-        u[i] = qemu_toupper(u[i]);
-    }
-
-    return r;
-}
-
-static const char *audio_audfmt_to_string (AudioFormat fmt)
-{
-    switch (fmt) {
-    case AUDIO_FORMAT_U8:
-        return "U8";
-
-    case AUDIO_FORMAT_U16:
-        return "U16";
-
-    case AUDIO_FORMAT_S8:
-        return "S8";
-
-    case AUDIO_FORMAT_S16:
-        return "S16";
-
-    case AUDIO_FORMAT_U32:
-        return "U32";
-
-    case AUDIO_FORMAT_S32:
-        return "S32";
-
-    default:
-        abort();
-    }
-
-    dolog ("Bogus audfmt %d returning S16\n", fmt);
-    return "S16";
-}
-
-static AudioFormat audio_string_to_audfmt (const char *s, AudioFormat defval,
-                                        int *defaultp)
-{
-    if (!strcasecmp (s, "u8")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_U8;
-    }
-    else if (!strcasecmp (s, "u16")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_U16;
-    }
-    else if (!strcasecmp (s, "u32")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_U32;
-    }
-    else if (!strcasecmp (s, "s8")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_S8;
-    }
-    else if (!strcasecmp (s, "s16")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_S16;
-    }
-    else if (!strcasecmp (s, "s32")) {
-        *defaultp = 0;
-        return AUDIO_FORMAT_S32;
-    }
-    else {
-        dolog ("Bogus audio format `%s' using %s\n",
-               s, audio_audfmt_to_string (defval));
-        *defaultp = 1;
-        return defval;
-    }
-}
-
-static AudioFormat audio_get_conf_fmt (const char *envname,
-                                    AudioFormat defval,
-                                    int *defaultp)
-{
-    const char *var = getenv (envname);
-    if (!var) {
-        *defaultp = 1;
-        return defval;
-    }
-    return audio_string_to_audfmt (var, defval, defaultp);
-}
-
-static int audio_get_conf_int (const char *key, int defval, int *defaultp)
-{
-    int val;
-    char *strval;
-
-    strval = getenv (key);
-    if (strval) {
-        *defaultp = 0;
-        val = atoi (strval);
-        return val;
-    }
-    else {
-        *defaultp = 1;
-        return defval;
-    }
-}
-
-static const char *audio_get_conf_str (const char *key,
-                                       const char *defval,
-                                       int *defaultp)
-{
-    const char *val = getenv (key);
-    if (!val) {
-        *defaultp = 1;
-        return defval;
-    }
-    else {
-        *defaultp = 0;
-        return val;
-    }
-}
-
 void AUD_vlog (const char *cap, const char *fmt, va_list ap)
 {
     if (cap) {
@@ -345,161 +160,6 @@ void AUD_log (const char *cap, const char *fmt, ...)
     va_end (ap);
 }
 
-static void audio_print_options (const char *prefix,
-                                 struct audio_option *opt)
-{
-    char *uprefix;
-
-    if (!prefix) {
-        dolog ("No prefix specified\n");
-        return;
-    }
-
-    if (!opt) {
-        dolog ("No options\n");
-        return;
-    }
-
-    uprefix = audio_alloc_prefix (prefix);
-
-    for (; opt->name; opt++) {
-        const char *state = "default";
-        printf ("  %s_%s: ", uprefix, opt->name);
-
-        if (opt->overriddenp && *opt->overriddenp) {
-            state = "current";
-        }
-
-        switch (opt->tag) {
-        case AUD_OPT_BOOL:
-            {
-                int *intp = opt->valp;
-                printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
-            }
-            break;
-
-        case AUD_OPT_INT:
-            {
-                int *intp = opt->valp;
-                printf ("integer, %s = %d\n", state, *intp);
-            }
-            break;
-
-        case AUD_OPT_FMT:
-            {
-                AudioFormat *fmtp = opt->valp;
-                printf (
-                    "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
-                    state,
-                    audio_audfmt_to_string (*fmtp)
-                    );
-            }
-            break;
-
-        case AUD_OPT_STR:
-            {
-                const char **strp = opt->valp;
-                printf ("string, %s = %s\n",
-                        state,
-                        *strp ? *strp : "(not set)");
-            }
-            break;
-
-        default:
-            printf ("???\n");
-            dolog ("Bad value tag for option %s_%s %d\n",
-                   uprefix, opt->name, opt->tag);
-            break;
-        }
-        printf ("    %s\n", opt->descr);
-    }
-
-    g_free (uprefix);
-}
-
-static void audio_process_options (const char *prefix,
-                                   struct audio_option *opt)
-{
-    char *optname;
-    const char qemu_prefix[] = "QEMU_";
-    size_t preflen, optlen;
-
-    if (audio_bug (AUDIO_FUNC, !prefix)) {
-        dolog ("prefix = NULL\n");
-        return;
-    }
-
-    if (audio_bug (AUDIO_FUNC, !opt)) {
-        dolog ("opt = NULL\n");
-        return;
-    }
-
-    preflen = strlen (prefix);
-
-    for (; opt->name; opt++) {
-        size_t len, i;
-        int def;
-
-        if (!opt->valp) {
-            dolog ("Option value pointer for `%s' is not set\n",
-                   opt->name);
-            continue;
-        }
-
-        len = strlen (opt->name);
-        /* len of opt->name + len of prefix + size of qemu_prefix
-         * (includes trailing zero) + zero + underscore (on behalf of
-         * sizeof) */
-        optlen = len + preflen + sizeof (qemu_prefix) + 1;
-        optname = g_malloc (optlen);
-
-        pstrcpy (optname, optlen, qemu_prefix);
-
-        /* copy while upper-casing, including trailing zero */
-        for (i = 0; i <= preflen; ++i) {
-            optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]);
-        }
-        pstrcat (optname, optlen, "_");
-        pstrcat (optname, optlen, opt->name);
-
-        def = 1;
-        switch (opt->tag) {
-        case AUD_OPT_BOOL:
-        case AUD_OPT_INT:
-            {
-                int *intp = opt->valp;
-                *intp = audio_get_conf_int (optname, *intp, &def);
-            }
-            break;
-
-        case AUD_OPT_FMT:
-            {
-                AudioFormat *fmtp = opt->valp;
-                *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
-            }
-            break;
-
-        case AUD_OPT_STR:
-            {
-                const char **strp = opt->valp;
-                *strp = audio_get_conf_str (optname, *strp, &def);
-            }
-            break;
-
-        default:
-            dolog ("Bad value tag for option `%s' - %d\n",
-                   optname, opt->tag);
-            break;
-        }
-
-        if (!opt->overriddenp) {
-            opt->overriddenp = &opt->overridden;
-        }
-        *opt->overriddenp = !def;
-        g_free (optname);
-    }
-}
-
 static void audio_print_settings (struct audsettings *as)
 {
     dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
@@ -1120,7 +780,7 @@ static void audio_reset_timer (AudioState *s)
 {
     if (audio_is_timer_needed ()) {
         timer_mod (s->ts,
-            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
+            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
     }
     else {
         timer_del (s->ts);
@@ -1196,7 +856,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
+                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
                     audio_reset_timer (s);
                 }
             }
@@ -1241,7 +901,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
+                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
                     audio_reset_timer (s);
                 }
             }
@@ -1558,168 +1218,10 @@ void audio_run (const char *msg)
 #endif
 }
 
-static struct audio_option audio_options[] = {
-    /* DAC */
-    {
-        .name  = "DAC_FIXED_SETTINGS",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.fixed_out.enabled,
-        .descr = "Use fixed settings for host DAC"
-    },
-    {
-        .name  = "DAC_FIXED_FREQ",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_out.settings.freq,
-        .descr = "Frequency for fixed host DAC"
-    },
-    {
-        .name  = "DAC_FIXED_FMT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &conf.fixed_out.settings.fmt,
-        .descr = "Format for fixed host DAC"
-    },
-    {
-        .name  = "DAC_FIXED_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_out.settings.nchannels,
-        .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "DAC_VOICES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_out.nb_voices,
-        .descr = "Number of voices for DAC"
-    },
-    {
-        .name  = "DAC_TRY_POLL",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.try_poll_out,
-        .descr = "Attempt using poll mode for DAC"
-    },
-    /* ADC */
-    {
-        .name  = "ADC_FIXED_SETTINGS",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.fixed_in.enabled,
-        .descr = "Use fixed settings for host ADC"
-    },
-    {
-        .name  = "ADC_FIXED_FREQ",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_in.settings.freq,
-        .descr = "Frequency for fixed host ADC"
-    },
-    {
-        .name  = "ADC_FIXED_FMT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &conf.fixed_in.settings.fmt,
-        .descr = "Format for fixed host ADC"
-    },
-    {
-        .name  = "ADC_FIXED_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_in.settings.nchannels,
-        .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "ADC_VOICES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.fixed_in.nb_voices,
-        .descr = "Number of voices for ADC"
-    },
-    {
-        .name  = "ADC_TRY_POLL",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &conf.try_poll_in,
-        .descr = "Attempt using poll mode for ADC"
-    },
-    /* Misc */
-    {
-        .name  = "TIMER_PERIOD",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.period.hertz,
-        .descr = "Timer period in HZ (0 - use lowest possible)"
-    },
-    { /* End of list */ }
-};
-
-static void audio_pp_nb_voices (const char *typ, int nb)
+static int audio_driver_init(AudioState *s, struct audio_driver *drv,
+                             Audiodev *dev)
 {
-    switch (nb) {
-    case 0:
-        printf ("Does not support %s\n", typ);
-        break;
-    case 1:
-        printf ("One %s voice\n", typ);
-        break;
-    case INT_MAX:
-        printf ("Theoretically supports many %s voices\n", typ);
-        break;
-    default:
-        printf ("Theoretically supports up to %d %s voices\n", nb, typ);
-        break;
-    }
-
-}
-
-void AUD_help (void)
-{
-    size_t i;
-
-    audio_process_options ("AUDIO", audio_options);
-    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-        struct audio_driver *d = drvtab[i];
-        if (d->options) {
-            audio_process_options (d->name, d->options);
-        }
-    }
-
-    printf ("Audio options:\n");
-    audio_print_options ("AUDIO", audio_options);
-    printf ("\n");
-
-    printf ("Available drivers:\n");
-
-    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
-        struct audio_driver *d = drvtab[i];
-
-        printf ("Name: %s\n", d->name);
-        printf ("Description: %s\n", d->descr);
-
-        audio_pp_nb_voices ("playback", d->max_voices_out);
-        audio_pp_nb_voices ("capture", d->max_voices_in);
-
-        if (d->options) {
-            printf ("Options:\n");
-            audio_print_options (d->name, d->options);
-        }
-        else {
-            printf ("No options\n");
-        }
-        printf ("\n");
-    }
-
-    printf (
-        "Options are settable through environment variables.\n"
-        "Example:\n"
-#ifdef _WIN32
-        "  set QEMU_AUDIO_DRV=wav\n"
-        "  set QEMU_WAV_PATH=c:\\tune.wav\n"
-#else
-        "  export QEMU_AUDIO_DRV=wav\n"
-        "  export QEMU_WAV_PATH=$HOME/tune.wav\n"
-        "(for csh replace export with setenv in the above)\n"
-#endif
-        "  qemu ...\n\n"
-        );
-}
-
-static int audio_driver_init (AudioState *s, struct audio_driver *drv)
-{
-    if (drv->options) {
-        audio_process_options (drv->name, drv->options);
-    }
-    s->drv_opaque = drv->init ();
+    s->drv_opaque = drv->init(dev);
 
     if (s->drv_opaque) {
         audio_init_nb_voices_out (drv);
@@ -1743,11 +1245,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
 
     s->vm_running = running;
     while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
-        hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
+        hwo->pcm_ops->ctl_out (hwo, op);
     }
 
     while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
-        hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
+        hwi->pcm_ops->ctl_in (hwi, op);
     }
     audio_reset_timer (s);
 }
@@ -1786,6 +1288,8 @@ static void audio_atexit (void)
     if (s->drv) {
         s->drv->fini (s->drv_opaque);
     }
+
+    qapi_free_Audiodev(s->dev);
 }
 
 static const VMStateDescription vmstate_audio = {
@@ -1797,18 +1301,37 @@ static const VMStateDescription vmstate_audio = {
     }
 };
 
-static void audio_init (void)
+static Audiodev *parse_option(QemuOpts *opts, Error **errp);
+static int audio_init(Audiodev *dev)
 {
     size_t i;
     int done = 0;
-    const char *drvname;
+    const char *drvname = NULL;
     VMChangeStateEntry *e;
     AudioState *s = &glob_audio_state;
+    QemuOptsList *list = NULL; /* silence gcc warning about uninitialized
+                                * variable */
 
     if (s->drv) {
-        return;
+        if (dev) {
+            dolog("Cannot create more than one audio backend, sorry\n");
+            qapi_free_Audiodev(dev);
+        }
+        return -1;
     }
 
+    if (dev) {
+        drvname = AudiodevDriver_lookup[dev->kind];
+    } else {
+        audio_handle_legacy_opts();
+        list = qemu_find_opts("audiodev");
+        dev = parse_option(QTAILQ_FIRST(&list->head), &error_abort);
+        if (!dev) {
+            exit(1);
+        }
+    }
+    s->dev = dev;
+
     QLIST_INIT (&s->hw_head_out);
     QLIST_INIT (&s->hw_head_in);
     QLIST_INIT (&s->cap_head);
@@ -1819,10 +1342,8 @@ static void audio_init (void)
         hw_error("Could not create audio timer\n");
     }
 
-    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;
+    s->nb_hw_voices_out = dev->out->voices;
+    s->nb_hw_voices_in = dev->in->voices;
 
     if (s->nb_hw_voices_out <= 0) {
         dolog ("Bogus number of playback voices %d, setting to 1\n",
@@ -1836,17 +1357,12 @@ static void audio_init (void)
         s->nb_hw_voices_in = 0;
     }
 
-    {
-        int def;
-        drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
-    }
-
     if (drvname) {
         int found = 0;
 
-        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+        for (i = 0; drvtab[i]; i++) {
             if (!strcmp (drvname, drvtab[i]->name)) {
-                done = !audio_driver_init (s, drvtab[i]);
+                done = !audio_driver_init (s, drvtab[i], dev);
                 found = 1;
                 break;
             }
@@ -1854,20 +1370,24 @@ static void audio_init (void)
 
         if (!found) {
             dolog ("Unknown audio driver `%s'\n", drvname);
-            dolog ("Run with -audio-help to list available drivers\n");
         }
-    }
-
-    if (!done) {
-        for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
-            if (drvtab[i]->can_be_default) {
-                done = !audio_driver_init (s, drvtab[i]);
+    } else {
+        for (i = 0; !done && drvtab[i]; i++) {
+            QemuOpts *opts = qemu_opts_find(list, drvtab[i]->name);
+            if (opts) {
+                qapi_free_Audiodev(dev);
+                dev = parse_option(opts, &error_abort);
+                if (!dev) {
+                    exit(1);
+                }
+                s->dev = dev;
+                done = !audio_driver_init(s, drvtab[i], dev);
             }
         }
     }
 
     if (!done) {
-        done = !audio_driver_init (s, &no_audio_driver);
+        done = !audio_driver_init (s, &no_audio_driver, dev);
         if (!done) {
             hw_error("Could not initialize audio subsystem\n");
         }
@@ -1876,16 +1396,16 @@ static void audio_init (void)
         }
     }
 
-    if (conf.period.hertz <= 0) {
-        if (conf.period.hertz < 0) {
-            dolog ("warning: Timer period is negative - %d "
-                   "treating as zero\n",
-                   conf.period.hertz);
+    if (dev->timer_period <= 0) {
+        if (dev->timer_period < 0) {
+            dolog ("warning: Timer period is negative - %" PRId64
+                   " treating as zero\n",
+                   dev->timer_period);
         }
-        conf.period.ticks = 1;
+        s->period_ticks = 1;
     } else {
-        conf.period.ticks =
-            muldiv64 (1, get_ticks_per_sec (), conf.period.hertz);
+        s->period_ticks =
+            muldiv64(dev->timer_period, get_ticks_per_sec(), 1000000);
     }
 
     e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
@@ -1896,11 +1416,12 @@ static void audio_init (void)
 
     QLIST_INIT (&s->card_head);
     vmstate_register (NULL, 0, &vmstate_audio, s);
+    return 0;
 }
 
 void AUD_register_card (const char *name, QEMUSoundCard *card)
 {
-    audio_init ();
+    audio_init(NULL);
     card->name = g_strdup (name);
     memset (&card->entries, 0, sizeof (card->entries));
     QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
@@ -2070,3 +1591,156 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
         }
     }
 }
+
+QemuOptsList qemu_audiodev_opts = {
+    .name = "audiodev",
+    .head = QTAILQ_HEAD_INITIALIZER(qemu_audiodev_opts.head),
+    .implied_opt_name = "driver",
+    .desc = {
+        /*
+         * no elements => accept any params
+         * sanity checking will happen later
+         */
+        { /* end of list */ }
+    },
+};
+
+static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo,
+                                        Error **errp)
+{
+    if (!pdo->has_fixed_settings) {
+        pdo->has_fixed_settings = true;
+        pdo->fixed_settings = true;
+    }
+    if (!pdo->fixed_settings &&
+        (pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
+        error_setg(errp,
+                   "You can't use frequency, channels or format with fixed-settings=off");
+        return;
+    }
+
+    if (!pdo->has_frequency) {
+        pdo->has_frequency = true;
+        pdo->frequency = 44100;
+    }
+    if (!pdo->has_channels) {
+        pdo->has_channels = true;
+        pdo->channels = 2;
+    }
+    if (!pdo->has_voices) {
+        pdo->has_voices = true;
+        pdo->voices = 1;
+    }
+    if (!pdo->has_format) {
+        pdo->has_format = true;
+        pdo->format = AUDIO_FORMAT_S16;
+    }
+}
+
+static Audiodev *parse_option(QemuOpts *opts, Error **errp)
+{
+    Error *local_err = NULL;
+    OptsVisitor *ov = opts_visitor_new(opts);
+    Audiodev *dev = NULL;
+    visit_type_Audiodev(opts_get_visitor(ov), &dev, NULL, &local_err);
+    opts_visitor_cleanup(ov);
+
+    if (local_err) {
+        goto err2;
+    }
+
+    validate_per_direction_opts(dev->in, &local_err);
+    if (local_err) {
+        goto err;
+    }
+    validate_per_direction_opts(dev->out, &local_err);
+    if (local_err) {
+        goto err;
+    }
+
+    if (!dev->has_timer_period) {
+        dev->has_timer_period = true;
+        dev->timer_period = 10000; /* 100Hz -> 10ms */
+    }
+
+    return dev;
+
+err:
+    qapi_free_Audiodev(dev);
+err2:
+    error_propagate(errp, local_err);
+    return NULL;
+}
+
+static int each_option(void *opaque, QemuOpts *opts, Error **errp)
+{
+    Audiodev *dev = parse_option(opts, errp);
+    if (!dev) {
+        return -1;
+    }
+    return audio_init(dev);
+}
+
+void audio_set_options(void)
+{
+    if (qemu_opts_foreach(qemu_find_opts("audiodev"), each_option, NULL,
+                          &error_abort)) {
+        exit(1);
+    }
+}
+
+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
+{
+    return (audsettings) {
+        .freq = pdo->frequency,
+        .nchannels = pdo->channels,
+        .fmt = pdo->format,
+        .endianness = AUDIO_HOST_ENDIANNESS,
+    };
+}
+
+int audioformat_bytes_per_sample(AudioFormat fmt)
+{
+    switch (fmt) {
+    case AUDIO_FORMAT_U8:
+    case AUDIO_FORMAT_S8:
+        return 1;
+
+    case AUDIO_FORMAT_U16:
+    case AUDIO_FORMAT_S16:
+        return 2;
+
+    case AUDIO_FORMAT_U32:
+    case AUDIO_FORMAT_S32:
+        return 4;
+
+    case AUDIO_FORMAT_MAX:
+        ;
+    }
+    abort();
+}
+
+
+/* frames = freq * usec / 1e6 */
+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
+                        audsettings *as, int def_usecs)
+{
+    uint64_t usecs = pdo->has_buffer_len ? pdo->buffer_len : def_usecs;
+    return (as->freq * usecs + 500000) / 1000000;
+}
+
+/* samples = channels * frames = channels * freq * usec / 1e6 */
+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
+                         audsettings *as, int def_usecs)
+{
+    return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);
+}
+
+/* bytes = bytes_per_sample * samples =
+ *   bytes_per_sample * channels * freq * usec / 1e6 */
+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
+                       audsettings *as, int def_usecs)
+{
+    return audio_buffer_samples(pdo, as, def_usecs) *
+        audioformat_bytes_per_sample(as->fmt);
+}
diff --git a/audio/audio.h b/audio/audio.h
index e300511..177a673 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -24,7 +24,10 @@
 #ifndef QEMU_AUDIO_H
 #define QEMU_AUDIO_H
 
+#include <stdarg.h>
 #include "config-host.h"
+#include "qapi-types.h"
+#include "qemu/option.h"
 #include "qemu/queue.h"
 
 typedef void (*audio_callback_fn) (void *opaque, int avail);
@@ -35,12 +38,21 @@ typedef void (*audio_callback_fn) (void *opaque, int avail);
 #define AUDIO_HOST_ENDIANNESS 0
 #endif
 
-struct audsettings {
+typedef struct audsettings {
     int freq;
     int nchannels;
     AudioFormat fmt;
     int endianness;
-};
+} audsettings;
+
+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
+int audioformat_bytes_per_sample(AudioFormat fmt);
+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
+                        audsettings *as, int def_usecs);
+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
+                         audsettings *as, int def_usecs);
+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
+                       audsettings *as, int def_usecs);
 
 typedef enum {
     AUD_CNOTIFY_ENABLE,
@@ -77,10 +89,11 @@ typedef struct QEMUAudioTimeStamp {
     uint64_t old_ts;
 } QEMUAudioTimeStamp;
 
+extern QemuOptsList qemu_audiodev_opts;
+
 void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
 void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
 
-void AUD_help (void);
 void AUD_register_card (const char *name, QEMUSoundCard *card);
 void AUD_remove_card (QEMUSoundCard *card);
 CaptureVoiceOut *AUD_add_capture (
@@ -154,4 +167,8 @@ static inline void *advance (void *p, int incr)
 int wav_start_capture (CaptureState *s, const char *path, int freq,
                        int bits, int nchannels);
 
+void audio_set_options(void);
+void audio_handle_legacy_opts(void);
+void audio_legacy_help(void);
+
 #endif  /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 566df5e..59b2362 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -32,22 +32,6 @@
 
 struct audio_pcm_ops;
 
-typedef enum {
-    AUD_OPT_INT,
-    AUD_OPT_FMT,
-    AUD_OPT_STR,
-    AUD_OPT_BOOL
-} audio_option_tag_e;
-
-struct audio_option {
-    const char *name;
-    audio_option_tag_e tag;
-    void *valp;
-    const char *descr;
-    int *overriddenp;
-    int overridden;
-};
-
 struct audio_callback {
     void *opaque;
     audio_callback_fn fn;
@@ -143,8 +127,7 @@ struct SWVoiceIn {
 struct audio_driver {
     const char *name;
     const char *descr;
-    struct audio_option *options;
-    void *(*init) (void);
+    void *(*init) (Audiodev *);
     void (*fini) (void *);
     struct audio_pcm_ops *pcm_ops;
     int can_be_default;
@@ -190,6 +173,7 @@ struct SWVoiceCap {
 
 struct AudioState {
     struct audio_driver *drv;
+    Audiodev *dev;
     void *drv_opaque;
 
     QEMUTimer *ts;
@@ -200,6 +184,7 @@ struct AudioState {
     int nb_hw_voices_out;
     int nb_hw_voices_in;
     int vm_running;
+    int64_t period_ticks;
 };
 
 extern struct audio_driver no_audio_driver;
@@ -213,6 +198,8 @@ extern struct audio_driver pa_audio_driver;
 extern struct audio_driver spice_audio_driver;
 extern const struct mixeng_volume nominal_volume;
 
+extern struct audio_driver *drvtab[];
+
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
 void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
 
diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c
new file mode 100644
index 0000000..88f577d
--- /dev/null
+++ b/audio/audio_legacy.c
@@ -0,0 +1,328 @@
+#include "audio.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+
+#define AUDIO_CAP "audio-legacy"
+#include "audio_int.h"
+
+typedef enum EnvTransform {
+    ENV_TRANSFORM_NONE,
+    ENV_TRANSFORM_BOOL,
+    ENV_TRANSFORM_FMT,
+    ENV_TRANSFORM_FRAMES_TO_USECS_IN,
+    ENV_TRANSFORM_FRAMES_TO_USECS_OUT,
+    ENV_TRANSFORM_SAMPLES_TO_USECS_IN,
+    ENV_TRANSFORM_SAMPLES_TO_USECS_OUT,
+    ENV_TRANSFORM_BYTES_TO_USECS_IN,
+    ENV_TRANSFORM_BYTES_TO_USECS_OUT,
+    ENV_TRANSFORM_MILLIS_TO_USECS,
+    ENV_TRANSFORM_HZ_TO_USECS,
+} EnvTransform;
+
+typedef struct SimpleEnvMap {
+    const char *name;
+    const char *option;
+    EnvTransform transform;
+} SimpleEnvMap;
+
+SimpleEnvMap global_map[] = {
+    /* DAC/out settings */
+    { "QEMU_AUDIO_DAC_FIXED_SETTINGS", "out.fixed-settings",
+      ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_DAC_FIXED_FREQ", "out.frequency" },
+    { "QEMU_AUDIO_DAC_FIXED_FMT", "out.format", ENV_TRANSFORM_FMT },
+    { "QEMU_AUDIO_DAC_FIXED_CHANNELS", "out.channels" },
+    { "QEMU_AUDIO_DAC_VOICES", "out.voices" },
+
+    /* ADC/in settings */
+    { "QEMU_AUDIO_ADC_FIXED_SETTINGS", "in.fixed-settings",
+      ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_ADC_FIXED_FREQ", "in.frequency" },
+    { "QEMU_AUDIO_ADC_FIXED_FMT", "in.format", ENV_TRANSFORM_FMT },
+    { "QEMU_AUDIO_ADC_FIXED_CHANNELS", "in.channels" },
+    { "QEMU_AUDIO_ADC_VOICES", "in.voices" },
+
+    /* general */
+    { "QEMU_AUDIO_TIMER_PERIOD", "timer-period", ENV_TRANSFORM_HZ_TO_USECS },
+    { /* End of list */ }
+};
+
+SimpleEnvMap alsa_map[] = {
+    { "QEMU_AUDIO_DAC_TRY_POLL", "alsa-out.try-poll", ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_ADC_TRY_POLL", "alsa-in.try-poll", ENV_TRANSFORM_BOOL },
+
+    { "QEMU_ALSA_THRESHOLD", "threshold", ENV_TRANSFORM_MILLIS_TO_USECS },
+    { "QEMU_ALSA_DAC_DEV", "alsa-out.dev" },
+    { "QEMU_ALSA_ADC_DEV", "alsa-in.dev" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap coreaudio_map[] = {
+    { "QEMU_COREAUDIO_BUFFER_SIZE", "buffer-len",
+      ENV_TRANSFORM_FRAMES_TO_USECS_OUT },
+    { "QEMU_COREAUDIO_BUFFER_COUNT", "buffer-count" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap dsound_map[] = {
+    { "QEMU_DSOUND_LATENCY_MILLIS", "latency", ENV_TRANSFORM_MILLIS_TO_USECS },
+    { "QEMU_DSOUND_BUFSIZE_OUT", "out.buffer-len",
+      ENV_TRANSFORM_BYTES_TO_USECS_OUT },
+    { "QEMU_DSOUND_BUFSIZE_IN", "in.buffer-len",
+      ENV_TRANSFORM_BYTES_TO_USECS_IN },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap oss_map[] = {
+    { "QEMU_AUDIO_DAC_TRY_POLL", "oss-out.try-poll", ENV_TRANSFORM_BOOL },
+    { "QEMU_AUDIO_ADC_TRY_POLL", "oss-in.try-poll", ENV_TRANSFORM_BOOL },
+
+    { "QEMU_OSS_FRAGSIZE", "buffer-len", ENV_TRANSFORM_BYTES_TO_USECS_OUT },
+    { "QEMU_OSS_NFRAGS", "buffer-count" },
+    { "QEMU_OSS_MMAP", "try-mmap", ENV_TRANSFORM_BOOL },
+    { "QEMU_OSS_DAC_DEV", "oss-out.dev" },
+    { "QEMU_OSS_ADC_DEV", "oss-in.dev" },
+    { "QEMU_OSS_EXCLUSIVE", "exclusive", ENV_TRANSFORM_BOOL },
+    { "QEMU_OSS_POLICY", "dsp-policy" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap pa_map[] = {
+    { "QEMU_PA_SAMPLES", "buffer", ENV_TRANSFORM_SAMPLES_TO_USECS_OUT },
+    { "QEMU_PA_SERVER", "server" },
+    { "QEMU_PA_SINK", "sink" },
+    { "QEMU_PA_SOURCE", "source" },
+
+    { /* End of list */ }
+};
+
+SimpleEnvMap sdl_map[] = {
+    { "QEMU_SDL_SAMPLES", "buffer-len", ENV_TRANSFORM_SAMPLES_TO_USECS_OUT },
+    { /* End of list */ }
+};
+
+SimpleEnvMap wav_map[] = {
+    { "QEMU_WAV_FREQUENCY", "out.frequency" },
+    { "QEMU_WAV_FORMAT", "out.format", ENV_TRANSFORM_FMT },
+    { "QEMU_WAV_DAC_FIXED_CHANNELS", "out.channels" },
+    { "QEMU_WAV_PATH", "path" },
+    { /* End of list */ }
+};
+
+static unsigned long long toull(const char *str)
+{
+    unsigned long long ret;
+    if (parse_uint_full(str, &ret, 10)) {
+        dolog("Invalid boolean value `%s'\n", str);
+        exit(1);
+    }
+    return ret;
+}
+
+/* non reentrant typesafe or anything, but enough in this small c file */
+static const char *tostr(unsigned long long val)
+{
+    #define LEN ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
+    static char ret[LEN];
+    snprintf(ret, LEN, "%llu", val);
+    return ret;
+}
+
+static uint64_t frames_to_usecs(QemuOpts *opts, uint64_t frames, bool in)
+{
+    const char *opt = in ? "in.frequency" : "out.frequency";
+    uint64_t freq = qemu_opt_get_number(opts, opt, 44100);
+    return (frames * 1000000 + freq/2) / freq;
+}
+
+static uint64_t samples_to_usecs(QemuOpts *opts, uint64_t samples, bool in)
+{
+    const char *opt = in ? "in.channels" : "out.channels";
+    uint64_t channels = qemu_opt_get_number(opts, opt, 2);
+    return frames_to_usecs(opts, samples/channels, in);
+}
+
+static uint64_t bytes_to_usecs(QemuOpts *opts, uint64_t bytes, bool in)
+{
+    const char *opt = in ? "in.format" : "out.format";
+    const char *val = qemu_opt_get(opts, opt);
+    uint64_t bytes_per_sample = (val ? toull(val) : 16) / 8;
+    return samples_to_usecs(opts, bytes * bytes_per_sample, in);
+}
+
+static const char *transform_val(QemuOpts *opts, const char *val,
+                                 EnvTransform transform)
+{
+    switch (transform) {
+    case ENV_TRANSFORM_NONE:
+        return val;
+
+    case ENV_TRANSFORM_BOOL:
+        return toull(val) ? "on" : "off";
+
+    case ENV_TRANSFORM_FMT:
+        if (strcasecmp(val, "u8") == 0) {
+            return "u8";
+        } else if (strcasecmp(val, "u16") == 0) {
+            return "u16";
+        } else if (strcasecmp(val, "u32") == 0) {
+            return "u32";
+        } else if (strcasecmp(val, "s8") == 0) {
+            return "s8";
+        } else if (strcasecmp(val, "s16") == 0) {
+            return "s16";
+        } else if (strcasecmp(val, "s32") == 0) {
+            return "s32";
+        } else {
+            dolog("Invalid audio format `%s'\n", val);
+            exit(1);
+        }
+
+    case ENV_TRANSFORM_FRAMES_TO_USECS_IN:
+        return tostr(frames_to_usecs(opts, toull(val), true));
+    case ENV_TRANSFORM_FRAMES_TO_USECS_OUT:
+        return tostr(frames_to_usecs(opts, toull(val), false));
+
+    case ENV_TRANSFORM_SAMPLES_TO_USECS_IN:
+        return tostr(samples_to_usecs(opts, toull(val), true));
+    case ENV_TRANSFORM_SAMPLES_TO_USECS_OUT:
+        return tostr(samples_to_usecs(opts, toull(val), false));
+
+    case ENV_TRANSFORM_BYTES_TO_USECS_IN:
+        return tostr(bytes_to_usecs(opts, toull(val), true));
+    case ENV_TRANSFORM_BYTES_TO_USECS_OUT:
+        return tostr(bytes_to_usecs(opts, toull(val), false));
+
+    case ENV_TRANSFORM_MILLIS_TO_USECS:
+        return tostr(toull(val) * 1000);
+
+    case ENV_TRANSFORM_HZ_TO_USECS:
+        return tostr(1000000 / toull(val));
+    }
+
+    abort(); /* it's unreachable, gcc */
+}
+
+static void handle_env_opts(QemuOpts *opts, SimpleEnvMap *map)
+{
+    while (map->name) {
+        const char *val = getenv(map->name);
+
+        if (val) {
+            qemu_opt_set(opts, map->option,
+                         transform_val(opts, val, map->transform),
+                         &error_abort);
+        }
+
+        ++map;
+    }
+}
+
+static void handle_alsa_side(QemuOpts *opts, int period, int buffer,
+                             const char *usec_env, const char *period_env,
+                             const char *buffer_env, const char *usec_opt,
+                             const char *count_opt, bool in)
+{
+    char *usec_s, *period_s, *buffer_s;
+    bool usec = false;
+
+    usec_s = getenv(usec_env);
+    if (usec_s) {
+        usec = toull(usec_s);
+    }
+
+    period_s = getenv(period_env);
+    if (period_s) {
+        period = toull(period_s);
+    }
+    if (!usec) {
+        period = frames_to_usecs(opts, period, in);
+    }
+    if (period_s) {
+        qemu_opt_set(opts, usec_opt, tostr(period), &error_abort);
+    }
+
+    buffer_s = getenv(buffer_env);
+    if (buffer_s) {
+        buffer = toull(buffer_s);
+        if (!usec) {
+            buffer = frames_to_usecs(opts, buffer, in);
+        }
+        printf("buffer %d period %d\n", buffer, period);
+        qemu_opt_set(opts, count_opt, tostr((buffer+period/2)/period),
+                     &error_abort);
+    }
+}
+
+static void handle_alsa(QemuOpts *opts)
+{
+    handle_alsa_side(opts, 1024, 4096,
+                     "QEMU_ALSA_DAC_SIZE_IN_USEC", "QEMU_ALSA_DAC_PERIOD_SIZE",
+                     "QEMU_ALSA_DAC_BUFFER_SIZE",
+                     "out.buffer-len", "out.buffer-count", false);
+    handle_alsa_side(opts, 0, 0,
+                     "QEMU_ALSA_ADC_SIZE_IN_USEC", "QEMU_ALSA_ADC_PERIOD_SIZE",
+                     "QEMU_ALSA_ADC_BUFFER_SIZE",
+                     "in.buffer-len", "in.buffer-count", true);
+}
+
+static void legacy_opt(const char *drv)
+{
+    QemuOpts *opts;
+    opts = qemu_opts_create(qemu_find_opts("audiodev"), drv, true,
+                            &error_abort);
+    qemu_opt_set(opts, "driver", drv, &error_abort);
+
+    handle_env_opts(opts, global_map);
+
+    if (strcmp(drv, "alsa") == 0) {
+        handle_env_opts(opts, alsa_map);
+        handle_alsa(opts);
+    } else if (strcmp(drv, "oss") == 0) {
+        handle_env_opts(opts, oss_map);
+    } else if (strcmp(drv, "pa") == 0) {
+        handle_env_opts(opts, pa_map);
+    } else if (strcmp(drv, "sdl") == 0) {
+        handle_env_opts(opts, sdl_map);
+    } else if (strcmp(drv, "wav") == 0) {
+        handle_env_opts(opts, wav_map);
+    }
+}
+
+void audio_handle_legacy_opts(void)
+{
+    const char *drv = getenv("QEMU_AUDIO_DRV");
+
+    if (drv) {
+        legacy_opt(drv);
+    } else {
+        struct audio_driver **drv;
+        for (drv = drvtab; *drv; ++drv) {
+            if ((*drv)->can_be_default) {
+                legacy_opt((*drv)->name);
+            }
+        }
+    }
+}
+
+static int legacy_help_each(void *opaque, QemuOpts *opts, Error **errp)
+{
+    printf("-audiodev ");
+    qemu_opts_print(opts, ",");
+    printf("\n");
+    return 0;
+}
+
+void audio_legacy_help(void)
+{
+    printf("Environment variable based configuration deprecated.\n");
+    printf("Please use the new -audiodev option.\n");
+
+    audio_handle_legacy_opts();
+    printf("\nEquivalent -audiodev to your current environment variables:\n");
+    qemu_opts_foreach(qemu_find_opts("audiodev"), legacy_help_each, NULL, NULL);
+}
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 99b27b2..096b2b3 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -302,8 +302,10 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
 static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
 {
     HW *hw;
+    AudioState *s = &glob_audio_state;
+    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
-    if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+    if (pdo->fixed_settings) {
         hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
         if (hw) {
             return hw;
@@ -331,9 +333,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
     SW *sw;
     HW *hw;
     struct audsettings hw_as;
+    AudioState *s = &glob_audio_state;
+    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
-    if (glue (conf.fixed_, TYPE).enabled) {
-        hw_as = glue (conf.fixed_, TYPE).settings;
+    if (pdo->fixed_settings) {
+        hw_as = audiodev_to_audsettings(pdo);
     }
     else {
         hw_as = *as;
@@ -398,6 +402,7 @@ SW *glue (AUD_open_, TYPE) (
     )
 {
     AudioState *s = &glob_audio_state;
+    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
     if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
         dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -422,7 +427,7 @@ SW *glue (AUD_open_, TYPE) (
         return sw;
     }
 
-    if (!glue (conf.fixed_, TYPE).enabled && sw) {
+    if (!pdo->fixed_settings && sw) {
         glue (AUD_close_, TYPE) (card, sw);
         sw = NULL;
     }
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 6dfd63e..dfa5e79 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -34,11 +34,6 @@
 
 static int isAtexit;
 
-typedef struct {
-    int buffer_frames;
-    int nbuffers;
-} CoreaudioConf;
-
 typedef struct coreaudioVoiceOut {
     HWVoiceOut hw;
     pthread_mutex_t mutex;
@@ -292,7 +287,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
     int err;
     const char *typ = "playback";
     AudioValueRange frameRange;
-    CoreaudioConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
+    AudiodevPerDirectionOptions *pdo = dev->out;
+    int frames;
 
     /* create mutex */
     err = pthread_mutex_init(&core->mutex, NULL);
@@ -334,16 +331,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
         return -1;
     }
 
-    if (frameRange.mMinimum > conf->buffer_frames) {
+    frames = audio_buffer_frames(pdo, as, 11610);
+    if (frameRange.mMinimum > frames) {
         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
     }
-    else if (frameRange.mMaximum < conf->buffer_frames) {
+    else if (frameRange.mMaximum < frames) {
         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
     }
     else {
-        core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
+        core->audioDevicePropertyBufferFrameSize = frames;
     }
 
     /* set Buffer Frame Size */
@@ -377,7 +375,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
                            "Could not get device buffer frame size\n");
         return -1;
     }
-    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
+    hw->samples = (pdo->has_buffer_count ? pdo->buffer_count : 4) *
+        core->audioDevicePropertyBufferFrameSize;
 
     /* get StreamFormat */
     propertySize = sizeof(core->outputStreamBasicDescription);
@@ -497,41 +496,16 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static CoreaudioConf glob_conf = {
-    .buffer_frames = 512,
-    .nbuffers = 4,
-};
-
-static void *coreaudio_audio_init (void)
+static void *coreaudio_audio_init(Audiodev *dev)
 {
-    CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
-    *conf = glob_conf;
-
     atexit(coreaudio_atexit);
-    return conf;
+    return dev;
 }
 
 static void coreaudio_audio_fini (void *opaque)
 {
-    g_free(opaque);
 }
 
-static struct audio_option coreaudio_options[] = {
-    {
-        .name  = "BUFFER_SIZE",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.buffer_frames,
-        .descr = "Size of the buffer in frames"
-    },
-    {
-        .name  = "BUFFER_COUNT",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.nbuffers,
-        .descr = "Number of buffers"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops coreaudio_pcm_ops = {
     .init_out = coreaudio_init_out,
     .fini_out = coreaudio_fini_out,
@@ -543,7 +517,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
 struct audio_driver coreaudio_audio_driver = {
     .name           = "coreaudio",
     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
-    .options        = coreaudio_options,
     .init           = coreaudio_audio_init,
     .fini           = coreaudio_audio_fini,
     .pcm_ops        = &coreaudio_pcm_ops,
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index b439f33..96181ef 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     dsound *s = drv_opaque;
     WAVEFORMATEX wfx;
     struct audsettings obt_as;
-    DSoundConf *conf = &s->conf;
 #ifdef DSBTYPE_IN
     const char *typ = "ADC";
     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
     DSCBUFFERDESC bd;
     DSCBCAPS bc;
+    AudiodevPerDirectionOptions *pdo = s->dev->in;
 #else
     const char *typ = "DAC";
     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
     DSBUFFERDESC bd;
     DSBCAPS bc;
+    AudiodevPerDirectionOptions *pdo = s->dev->out;
 #endif
 
     if (!s->FIELD2) {
@@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     memset (&bd, 0, sizeof (bd));
     bd.dwSize = sizeof (bd);
     bd.lpwfxFormat = &wfx;
+    bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
 #ifdef DSBTYPE_IN
-    bd.dwBufferBytes = conf->bufsize_in;
     hr = IDirectSoundCapture_CreateCaptureBuffer (
         s->dsound_capture,
         &bd,
@@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
         );
 #else
     bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
-    bd.dwBufferBytes = conf->bufsize_out;
     hr = IDirectSound_CreateSoundBuffer (
         s->dsound,
         &bd,
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index e9472c1..7e7b3f2 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -42,16 +42,10 @@
 /* #define DEBUG_DSOUND */
 
 typedef struct {
-    int bufsize_in;
-    int bufsize_out;
-    int latency_millis;
-} DSoundConf;
-
-typedef struct {
     LPDIRECTSOUND dsound;
     LPDIRECTSOUNDCAPTURE dsound_capture;
     struct audsettings settings;
-    DSoundConf conf;
+    Audiodev *dev;
 } dsound;
 
 typedef struct {
@@ -247,9 +241,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
     dsound_log_hresult (hr);
 }
 
-static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
+static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
 {
-    return (millis * info->bytes_per_second) / 1000;
+    return muldiv64(usecs, info->bytes_per_second, 1000000);
 }
 
 #ifdef DEBUG_DSOUND
@@ -477,7 +471,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     LPVOID p1, p2;
     int bufsize;
     dsound *s = ds->s;
-    DSoundConf *conf = &s->conf;
+    AudiodevDsoundOptions *dso = s->dev->dsound;
 
     if (!dsb) {
         dolog ("Attempt to run empty with playback buffer\n");
@@ -500,14 +494,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
     len = live << hwshift;
 
     if (ds->first_time) {
-        if (conf->latency_millis) {
+        if (dso->latency) {
             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;
+                usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
             old_pos %= bufsize;
             old_pos &= ~hw->info.align;
         }
@@ -746,12 +740,6 @@ static int dsound_run_in (HWVoiceIn *hw)
     return decr;
 }
 
-static DSoundConf glob_conf = {
-    .bufsize_in         = 16384,
-    .bufsize_out        = 16384,
-    .latency_millis     = 10
-};
-
 static void dsound_audio_fini (void *opaque)
 {
     HRESULT hr;
@@ -782,13 +770,22 @@ static void dsound_audio_fini (void *opaque)
     g_free(s);
 }
 
-static void *dsound_audio_init (void)
+static void *dsound_audio_init(Audiodev *dev)
 {
     int err;
     HRESULT hr;
     dsound *s = g_malloc0(sizeof(dsound));
+    AudiodevDsoundOptions *dso;
+
+    assert(dev->kind == AUDIODEV_DRIVER_DSOUND);
+    s->dev = dev;
+    dso = dev->dsound;
+
+    if (!dso->has_latency) {
+        dso->has_latency = true;
+        dso->latency = 10000; /* 10 ms */
+    }
 
-    s->conf = glob_conf;
     hr = CoInitialize (NULL);
     if (FAILED (hr)) {
         dsound_logerr (hr, "Could not initialize COM\n");
@@ -853,28 +850,6 @@ static void *dsound_audio_init (void)
     return s;
 }
 
-static struct audio_option dsound_options[] = {
-    {
-        .name  = "LATENCY_MILLIS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.latency_millis,
-        .descr = "(undocumented)"
-    },
-    {
-        .name  = "BUFSIZE_OUT",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.bufsize_out,
-        .descr = "(undocumented)"
-    },
-    {
-        .name  = "BUFSIZE_IN",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.bufsize_in,
-        .descr = "(undocumented)"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops dsound_pcm_ops = {
     .init_out = dsound_init_out,
     .fini_out = dsound_fini_out,
@@ -892,7 +867,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
 struct audio_driver dsound_audio_driver = {
     .name           = "dsound",
     .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
-    .options        = dsound_options,
     .init           = dsound_audio_init,
     .fini           = dsound_audio_fini,
     .pcm_ops        = &dsound_pcm_ops,
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 50db1f3..4c94a26 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -134,7 +134,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static void *no_audio_init (void)
+static void *no_audio_init (Audiodev *dev)
 {
     return &no_audio_init;
 }
@@ -161,7 +161,6 @@ static struct audio_pcm_ops no_pcm_ops = {
 struct audio_driver no_audio_driver = {
     .name           = "none",
     .descr          = "Timer based audio emulation",
-    .options        = NULL,
     .init           = no_audio_init,
     .fini           = no_audio_fini,
     .pcm_ops        = &no_pcm_ops,
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 3ea7d27..a5e7f7c 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -29,6 +29,7 @@
 #include "qemu-common.h"
 #include "qemu/main-loop.h"
 #include "qemu/host-utils.h"
+#include "qapi-visit.h"
 #include "audio.h"
 #include "trace.h"
 
@@ -39,16 +40,6 @@
 #define USE_DSP_POLICY
 #endif
 
-typedef struct OSSConf {
-    int try_mmap;
-    int nfrags;
-    int fragsize;
-    const char *devpath_out;
-    const char *devpath_in;
-    int exclusive;
-    int policy;
-} OSSConf;
-
 typedef struct OSSVoiceOut {
     HWVoiceOut hw;
     void *pcm_buf;
@@ -58,7 +49,7 @@ typedef struct OSSVoiceOut {
     int fragsize;
     int mmapped;
     int pending;
-    OSSConf *conf;
+    Audiodev *dev;
 } OSSVoiceOut;
 
 typedef struct OSSVoiceIn {
@@ -67,12 +58,12 @@ typedef struct OSSVoiceIn {
     int fd;
     int nfrags;
     int fragsize;
-    OSSConf *conf;
+    Audiodev *dev;
 } OSSVoiceIn;
 
 struct oss_params {
     int freq;
-    AudioFormat fmt;
+    int fmt;
     int nchannels;
     int nfrags;
     int fragsize;
@@ -264,19 +255,26 @@ static int oss_get_version (int fd, int *version, const char *typ)
 }
 #endif
 
-static int oss_open (int in, struct oss_params *req,
-                     struct oss_params *obt, int *pfd, OSSConf* conf)
+static int oss_open(int in, struct oss_params *req, audsettings *as,
+                    struct oss_params *obt, int *pfd, Audiodev *dev)
 {
+    AudiodevOssOptions *oopts = dev->oss;
+    AudiodevOssPerDirectionOptions *opdo = in ? oopts->oss_in : oopts->oss_out;
+    AudiodevPerDirectionOptions *pdo = in ? dev->in : dev->out;
     int fd;
-    int oflags = conf->exclusive ? O_EXCL : 0;
+    int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
     audio_buf_info abinfo;
     int fmt, freq, nchannels;
     int setfragment = 1;
-    const char *dspname = in ? conf->devpath_in : conf->devpath_out;
+    const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
     const char *typ = in ? "ADC" : "DAC";
+#ifdef USE_DSP_POLICY
+    int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
+#endif
 
     /* Kludge needed to have working mmap on Linux */
-    oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+    oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
+        O_RDWR : (in ? O_RDONLY : O_WRONLY);
 
     fd = open (dspname, oflags | O_NONBLOCK);
     if (-1 == fd) {
@@ -287,6 +285,8 @@ static int oss_open (int in, struct oss_params *req,
     freq = req->freq;
     nchannels = req->nchannels;
     fmt = req->fmt;
+    req->nfrags = pdo->has_buffer_count ? pdo->buffer_count : 4;
+    req->fragsize = audio_buffer_bytes(pdo, as, 23220);
 
     if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
         oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
@@ -310,18 +310,18 @@ static int oss_open (int in, struct oss_params *req,
     }
 
 #ifdef USE_DSP_POLICY
-    if (conf->policy >= 0) {
+    if (policy >= 0) {
         int version;
 
         if (!oss_get_version (fd, &version, typ)) {
             trace_oss_version(version);
 
             if (version >= 0x040000) {
-                int policy = conf->policy;
-                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+                int policy2 = policy;
+                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy2)) {
                     oss_logerr2 (errno, typ,
                                  "Failed to set timing policy to %d\n",
-                                 conf->policy);
+                                 policy);
                     goto err;
                 }
                 setfragment = 0;
@@ -504,17 +504,16 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     int fd;
     AudioFormat effective_fmt;
     struct audsettings obt_as;
-    OSSConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
+    AudiodevOssOptions *oopts = dev->oss;
 
     oss->fd = -1;
 
     req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.fragsize = conf->fragsize;
-    req.nfrags = conf->nfrags;
 
-    if (oss_open (0, &req, &obt, &fd, conf)) {
+    if (oss_open(0, &req, as, &obt, &fd, dev)) {
         return -1;
     }
 
@@ -541,7 +540,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 
     oss->mmapped = 0;
-    if (conf->try_mmap) {
+    if (oopts->has_try_mmap && oopts->try_mmap) {
         oss->pcm_buf = mmap (
             NULL,
             hw->samples << hw->info.shift,
@@ -601,7 +600,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     oss->fd = fd;
-    oss->conf = conf;
+    oss->dev = dev;
     return 0;
 }
 
@@ -609,16 +608,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     int trig;
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
 
             ldebug ("enabling voice\n");
             if (poll_mode) {
@@ -673,16 +668,14 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     int fd;
     AudioFormat effective_fmt;
     struct audsettings obt_as;
-    OSSConf *conf = drv_opaque;
+    Audiodev *dev = drv_opaque;
 
     oss->fd = -1;
 
     req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
-    req.fragsize = conf->fragsize;
-    req.nfrags = conf->nfrags;
-    if (oss_open (1, &req, &obt, &fd, conf)) {
+    if (oss_open(1, &req, as, &obt, &fd, dev)) {
         return -1;
     }
 
@@ -716,7 +709,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     oss->fd = fd;
-    oss->conf = conf;
+    oss->dev = dev;
     return 0;
 }
 
@@ -807,16 +800,12 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+    AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
 
     switch (cmd) {
     case VOICE_ENABLE:
         {
-            va_list ap;
-            int poll_mode;
-
-            va_start (ap, cmd);
-            poll_mode = va_arg (ap, int);
-            va_end (ap);
+            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
 
             if (poll_mode) {
                 oss_poll_in (hw);
@@ -836,82 +825,25 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static OSSConf glob_conf = {
-    .try_mmap = 0,
-    .nfrags = 4,
-    .fragsize = 4096,
-    .devpath_out = "/dev/dsp",
-    .devpath_in = "/dev/dsp",
-    .exclusive = 0,
-    .policy = 5
-};
-
-static void *oss_audio_init (void)
+static void *oss_audio_init(Audiodev *dev)
 {
-    OSSConf *conf = g_malloc(sizeof(OSSConf));
-    *conf = glob_conf;
+    AudiodevOssOptions *oopts;
+    assert(dev->kind == AUDIODEV_DRIVER_OSS);
 
-    if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
-        access(conf->devpath_out, R_OK | W_OK) < 0) {
-        g_free(conf);
+    oopts = dev->oss;
+    if (access(oopts->oss_in->has_dev ? oopts->oss_in->dev : "/dev/dsp",
+               R_OK | W_OK) < 0 ||
+        access(oopts->oss_out->has_dev ? oopts->oss_out->dev : "/dev/dsp",
+               R_OK | W_OK) < 0) {
         return NULL;
     }
-    return conf;
+    return dev;
 }
 
 static void oss_audio_fini (void *opaque)
 {
-    g_free(opaque);
 }
 
-static struct audio_option oss_options[] = {
-    {
-        .name  = "FRAGSIZE",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.fragsize,
-        .descr = "Fragment size in bytes"
-    },
-    {
-        .name  = "NFRAGS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.nfrags,
-        .descr = "Number of fragments"
-    },
-    {
-        .name  = "MMAP",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &glob_conf.try_mmap,
-        .descr = "Try using memory mapped access"
-    },
-    {
-        .name  = "DAC_DEV",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.devpath_out,
-        .descr = "Path to DAC device"
-    },
-    {
-        .name  = "ADC_DEV",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.devpath_in,
-        .descr = "Path to ADC device"
-    },
-    {
-        .name  = "EXCLUSIVE",
-        .tag   = AUD_OPT_BOOL,
-        .valp  = &glob_conf.exclusive,
-        .descr = "Open device in exclusive mode (vmix wont work)"
-    },
-#ifdef USE_DSP_POLICY
-    {
-        .name  = "POLICY",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.policy,
-        .descr = "Set the timing policy of the device, -1 to use fragment mode",
-    },
-#endif
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops oss_pcm_ops = {
     .init_out = oss_init_out,
     .fini_out = oss_fini_out,
@@ -929,7 +861,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
 struct audio_driver oss_audio_driver = {
     .name           = "oss",
     .descr          = "OSS http://www.opensound.com",
-    .options        = oss_options,
     .init           = oss_audio_init,
     .fini           = oss_audio_fini,
     .pcm_ops        = &oss_pcm_ops,
diff --git a/audio/paaudio.c b/audio/paaudio.c
index cfdbdc6..a53aaf6 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -1,6 +1,7 @@
 /* public domain */
 #include "qemu-common.h"
 #include "audio.h"
+#include "qapi-visit.h"
 
 #include <pulse/pulseaudio.h>
 
@@ -9,14 +10,7 @@
 #include "audio_pt_int.h"
 
 typedef struct {
-    int samples;
-    char *server;
-    char *sink;
-    char *source;
-} PAConf;
-
-typedef struct {
-    PAConf conf;
+    Audiodev *dev;
     pa_threaded_mainloop *mainloop;
     pa_context *context;
 } paaudio;
@@ -31,6 +25,7 @@ typedef struct {
     void *pcm_buf;
     struct audio_pt pt;
     paaudio *g;
+    int samples;
 } PAVoiceOut;
 
 typedef struct {
@@ -45,6 +40,7 @@ typedef struct {
     const void *read_data;
     size_t read_index, read_length;
     paaudio *g;
+    int samples;
 } PAVoiceIn;
 
 static void qpa_audio_fini(void *opaque);
@@ -226,7 +222,7 @@ static void *qpa_thread_out (void *arg)
             }
         }
 
-        decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
+        decr = to_mix = audio_MIN (pa->live, pa->samples >> 2);
         rpos = pa->rpos;
 
         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -318,7 +314,7 @@ static void *qpa_thread_in (void *arg)
             }
         }
 
-        incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
+        incr = to_grab = audio_MIN (pa->dead, pa->samples >> 2);
         wpos = pa->wpos;
 
         if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -545,6 +541,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     struct audsettings obt_as = *as;
     PAVoiceOut *pa = (PAVoiceOut *) hw;
     paaudio *g = pa->g = drv_opaque;
+    AudiodevPaOptions *popts = g->dev->pa;
+    AudiodevPaPerDirectionOptions *ppdo = popts->sink;
 
     ss.format = audfmt_to_pa (as->fmt, as->endianness);
     ss.channels = as->nchannels;
@@ -565,7 +563,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
         g,
         "qemu",
         PA_STREAM_PLAYBACK,
-        g->conf.sink,
+        ppdo->has_name ? ppdo->name : NULL,
         &ss,
         NULL,                   /* channel map */
         &ba,                    /* buffering attributes */
@@ -577,7 +575,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = g->conf.samples;
+    hw->samples = pa->samples = audio_buffer_samples(g->dev->out, &obt_as,
+                                                     46440);
     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     pa->rpos = hw->rpos;
     if (!pa->pcm_buf) {
@@ -611,6 +610,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     struct audsettings obt_as = *as;
     PAVoiceIn *pa = (PAVoiceIn *) hw;
     paaudio *g = pa->g = drv_opaque;
+    AudiodevPaOptions *popts = g->dev->pa;
+    AudiodevPaPerDirectionOptions *ppdo = popts->source;
 
     ss.format = audfmt_to_pa (as->fmt, as->endianness);
     ss.channels = as->nchannels;
@@ -622,7 +623,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
         g,
         "qemu",
         PA_STREAM_RECORD,
-        g->conf.source,
+        ppdo->has_name ? ppdo->name : NULL,
         &ss,
         NULL,                   /* channel map */
         NULL,                   /* buffering attributes */
@@ -634,7 +635,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = g->conf.samples;
+    hw->samples = pa->samples = audio_buffer_samples(g->dev->in, &obt_as,
+                                                     46440);
     pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     pa->wpos = hw->wpos;
     if (!pa->pcm_buf) {
@@ -808,14 +810,19 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
 }
 
 /* common */
-static PAConf glob_conf = {
-    .samples = 4096,
-};
-
-static void *qpa_audio_init (void)
+static void *qpa_audio_init(Audiodev *dev)
 {
-    paaudio *g = g_malloc(sizeof(paaudio));
-    g->conf = glob_conf;
+    paaudio *g;
+    AudiodevPaOptions *popts;
+    const char *server;
+
+    assert(dev->kind == AUDIODEV_DRIVER_PA);
+
+    g = g_malloc(sizeof(paaudio));
+    popts = dev->pa;
+    server = popts->has_server ? popts->server : NULL;
+
+    g->dev = dev;
     g->mainloop = NULL;
     g->context = NULL;
 
@@ -825,14 +832,14 @@ static void *qpa_audio_init (void)
     }
 
     g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
-                                 g->conf.server);
+                                 server);
     if (!g->context) {
         goto fail;
     }
 
     pa_context_set_state_callback (g->context, context_state_cb, g);
 
-    if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
+    if (pa_context_connect (g->context, server, 0, NULL) < 0) {
         qpa_logerr (pa_context_errno (g->context),
                     "pa_context_connect() failed\n");
         goto fail;
@@ -895,34 +902,6 @@ static void qpa_audio_fini (void *opaque)
     g_free(g);
 }
 
-struct audio_option qpa_options[] = {
-    {
-        .name  = "SAMPLES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.samples,
-        .descr = "buffer size in samples"
-    },
-    {
-        .name  = "SERVER",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.server,
-        .descr = "server address"
-    },
-    {
-        .name  = "SINK",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.sink,
-        .descr = "sink device name"
-    },
-    {
-        .name  = "SOURCE",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.source,
-        .descr = "source device name"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops qpa_pcm_ops = {
     .init_out = qpa_init_out,
     .fini_out = qpa_fini_out,
@@ -940,7 +919,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
 struct audio_driver pa_audio_driver = {
     .name           = "pa",
     .descr          = "http://www.pulseaudio.org/",
-    .options        = qpa_options,
     .init           = qpa_audio_init,
     .fini           = qpa_audio_fini,
     .pcm_ops        = &qpa_pcm_ops,
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index db0f95a..796238a 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -44,18 +44,13 @@ typedef struct SDLVoiceOut {
     int decr;
 } SDLVoiceOut;
 
-static struct {
-    int nb_samples;
-} conf = {
-    .nb_samples = 1024
-};
-
 static struct SDLAudioState {
     int exit;
     SDL_mutex *mutex;
     SDL_sem *sem;
     int initialized;
     bool driver_created;
+    Audiodev *dev;
 } glob_sdl;
 typedef struct SDLAudioState SDLAudioState;
 
@@ -347,7 +342,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     req.freq = as->freq;
     req.format = aud_to_sdlfmt (as->fmt);
     req.channels = as->nchannels;
-    req.samples = conf.nb_samples;
+    req.samples = audio_buffer_samples(s->dev->out, as, 11610);
     req.callback = sdl_callback;
     req.userdata = sdl;
 
@@ -391,7 +386,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static void *sdl_audio_init (void)
+static void *sdl_audio_init(Audiodev *dev)
 {
     SDLAudioState *s = &glob_sdl;
     if (s->driver_created) {
@@ -420,6 +415,7 @@ static void *sdl_audio_init (void)
     }
 
     s->driver_created = true;
+    s->dev = dev;
     return s;
 }
 
@@ -431,18 +427,9 @@ static void sdl_audio_fini (void *opaque)
     SDL_DestroyMutex (s->mutex);
     SDL_QuitSubSystem (SDL_INIT_AUDIO);
     s->driver_created = false;
+    s->dev = NULL;
 }
 
-static struct audio_option sdl_options[] = {
-    {
-        .name  = "SAMPLES",
-        .tag   = AUD_OPT_INT,
-        .valp  = &conf.nb_samples,
-        .descr = "Size of SDL buffer in samples"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops sdl_pcm_ops = {
     .init_out = sdl_init_out,
     .fini_out = sdl_fini_out,
@@ -454,7 +441,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
 struct audio_driver sdl_audio_driver = {
     .name           = "sdl",
     .descr          = "SDL http://www.libsdl.org",
-    .options        = sdl_options,
     .init           = sdl_audio_init,
     .fini           = sdl_audio_fini,
     .pcm_ops        = &sdl_pcm_ops,
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index 141fd8d..a9b9a1d 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -75,7 +75,7 @@ static const SpiceRecordInterface record_sif = {
     .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
 };
 
-static void *spice_audio_init (void)
+static void *spice_audio_init(Audiodev *dev)
 {
     if (!using_spice) {
         return NULL;
@@ -371,10 +371,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static struct audio_option audio_options[] = {
-    { /* end of list */ },
-};
-
 static struct audio_pcm_ops audio_callbacks = {
     .init_out = line_out_init,
     .fini_out = line_out_fini,
@@ -392,7 +388,6 @@ static struct audio_pcm_ops audio_callbacks = {
 struct audio_driver spice_audio_driver = {
     .name           = "spice",
     .descr          = "spice audio driver",
-    .options        = audio_options,
     .init           = spice_audio_init,
     .fini           = spice_audio_fini,
     .pcm_ops        = &audio_callbacks,
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 81250e6..1af6d23 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -23,6 +23,7 @@
  */
 #include "hw/hw.h"
 #include "qemu/timer.h"
+#include "qapi-visit.h"
 #include "audio.h"
 
 #define AUDIO_CAP "wav"
@@ -36,11 +37,6 @@ typedef struct WAVVoiceOut {
     int total_samples;
 } WAVVoiceOut;
 
-typedef struct {
-    struct audsettings settings;
-    const char *wav_path;
-} WAVConf;
-
 static int wav_run_out (HWVoiceOut *hw, int live)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
@@ -111,8 +107,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
     };
-    WAVConf *conf = drv_opaque;
-    struct audsettings wav_as = conf->settings;
+    Audiodev *dev = drv_opaque;
+    AudiodevWavOptions *wopts = dev->wav;
+    struct audsettings wav_as = audiodev_to_audsettings(dev->out);
+    const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
 
     stereo = wav_as.nchannels == 2;
     switch (wav_as.fmt) {
@@ -153,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
     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");
+    wav->f = fopen(wav_path, "wb");
     if (!wav->f) {
         dolog ("Failed to open wave file `%s'\nReason: %s\n",
-               conf->wav_path, strerror (errno));
+               wav_path, strerror(errno));
         g_free (wav->pcm_buf);
         wav->pcm_buf = NULL;
         return -1;
@@ -224,54 +222,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static WAVConf glob_conf = {
-    .settings.freq      = 44100,
-    .settings.nchannels = 2,
-    .settings.fmt       = AUDIO_FORMAT_S16,
-    .wav_path           = "qemu.wav"
-};
-
-static void *wav_audio_init (void)
+static void *wav_audio_init(Audiodev *dev)
 {
-    WAVConf *conf = g_malloc(sizeof(WAVConf));
-    *conf = glob_conf;
-    return conf;
+    assert(dev->kind == AUDIODEV_DRIVER_WAV);
+    return dev;
 }
 
 static void wav_audio_fini (void *opaque)
 {
     ldebug ("wav_fini");
-    g_free(opaque);
 }
 
-static struct audio_option wav_options[] = {
-    {
-        .name  = "FREQUENCY",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.settings.freq,
-        .descr = "Frequency"
-    },
-    {
-        .name  = "FORMAT",
-        .tag   = AUD_OPT_FMT,
-        .valp  = &glob_conf.settings.fmt,
-        .descr = "Format"
-    },
-    {
-        .name  = "DAC_FIXED_CHANNELS",
-        .tag   = AUD_OPT_INT,
-        .valp  = &glob_conf.settings.nchannels,
-        .descr = "Number of channels (1 - mono, 2 - stereo)"
-    },
-    {
-        .name  = "PATH",
-        .tag   = AUD_OPT_STR,
-        .valp  = &glob_conf.wav_path,
-        .descr = "Path to wave file"
-    },
-    { /* End of list */ }
-};
-
 static struct audio_pcm_ops wav_pcm_ops = {
     .init_out = wav_init_out,
     .fini_out = wav_fini_out,
@@ -283,7 +244,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
 struct audio_driver wav_audio_driver = {
     .name           = "wav",
     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
-    .options        = wav_options,
     .init           = wav_audio_init,
     .fini           = wav_audio_fini,
     .pcm_ops        = &wav_pcm_ops,
diff --git a/qemu-options.hx b/qemu-options.hx
index 77f5853..efd57e6 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -185,8 +185,8 @@ Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.:
 qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if=ide,index=0,media=disk
 @end example
 
-In particular, you can use this to set driver properties for devices which are 
-created automatically by the machine model. To create a device which is not 
+In particular, you can use this to set driver properties for devices which are
+created automatically by the machine model. To create a device which is not
 created automatically and set properties on it, use -@option{device}.
 
 -global @var{driver}.@var{prop}=@var{value} is shorthand for -global
@@ -313,14 +313,230 @@ The default is @code{en-us}.
 ETEXI
 
 
+HXCOMM Deprecated by -audiodev
 DEF("audio-help", 0, QEMU_OPTION_audio_help,
-    "-audio-help     print list of audio drivers and their options\n",
+    "-audio-help     show -audiodev equivalent of the current audio settings\n",
     QEMU_ARCH_ALL)
 STEXI
 @item -audio-help
 @findex -audio-help
-Will show the audio subsystem help: list of drivers, tunable
-parameters.
+Will show the -audiodev equivalent of the currently specified
+(deprecated) environment variables.
+ETEXI
+
+DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
+    "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n"
+    "                specifies the audio backend to use\n"
+    "                id= identifier of the backend\n"
+    "                timer-period= timer period in microseconds\n"
+    "                in|out.fixed-settings= use fixed settings for host audio\n"
+    "                in|out.frequency= frequency to use with fixed settings\n"
+    "                in|out.channels= number of channels to use with fixed settings\n"
+    "                in|out.format= sample format to use with fixed settings\n"
+    "                valid values: s8, s16, s32, u8, u16, u32\n"
+    "                in|out.voices= number of voices to use\n"
+    "                in|out.buffer-len= size of buffer in microseconds\n"
+    "                in|out.buffer-count= number of buffers\n"
+    "-audiodev none,id=id,[,prop[=value][,...]]\n"
+    "                dummy driver that discards all output\n"
+#ifdef CONFIG_ALSA
+    "-audiodev alsa,id=id[,prop[=value][,...]]\n"
+    "                alsa-in|alsa-out.dev= name of the audio device to use\n"
+    "                alsa-in|alsa-out.try-poll= attempt to use poll mode\n"
+    "                threshold= threshold (in microseconds) when playback starts\n"
+#endif
+#ifdef CONFIG_COREAUDIO
+    "-audiodev coreaudio,id=id[,prop[=value][,...]]\n"
+#endif
+#ifdef CONFIG_DSOUND
+    "-audiodev dsound,id=id[,prop[=value][,...]]\n"
+    "                latency= add extra latency to playback in microseconds\n"
+#endif
+#ifdef CONFIG_OSS
+    "-audiodev oss,id=id[,prop[=value][,...]]\n"
+    "                oss-in|oss-out.dev= path of the audio device to use\n"
+    "                oss-in|oss-out.try-poll= attempt to use poll mode\n"
+    "                try-mmap= try using memory mapped access\n"
+    "                exclusive= open device in exclusive mode\n"
+    "                dsp-policy= set timing policy (0..10), -1 to use fragment mode\n"
+#endif
+#ifdef CONFIG_PA
+    "-audiodev pa,id=id[,prop[=value][,...]]\n"
+    "                server= PulseAudio server address\n"
+    "                sink|source.name= sink/source device name\n"
+#endif
+#ifdef CONFIG_SDL
+    "-audiodev sdl,id=id[,prop[=value][,...]]\n"
+#endif
+#ifdef CONFIG_SPICE
+    "-audiodev spice,id=id[,prop[=value][,...]]\n"
+#endif
+    "-audiodev wav,id=id[,prop[=value][,...]]\n"
+    "                path= path of wav file to record\n",
+    QEMU_ARCH_ALL)
+STEXI
+@item -audiodev [driver=]@var{driver},id=@var{id}[,@var{prop}[=@var{value}][,...]]
+@findex -audiodev
+Adds a new audio backend @var{driver} identified by @var{id}.  There are
+global and driver specific properties.  Some values can be set
+differently for input and output, they're marked with @code{in|out.}.
+You can set the input's property with @code{in.@var{prop}} and the
+output's property with @code{out.@var{prop}}. For example:
+@example
+-audiodev alsa,in.frequency=44110,out.frequency=8000
+-audiodev alsa,out.channels=1 # leaves in.channels unspecified
+@end example
+
+Valid global options are:
+
+@table @option
+@item id=@var{identifier}
+Identifies the audio backend.
+
+@item timer-period=@var{period}
+Sets the timer @var{period} used by the audio subsystem in microseconds.
+Default is 10000 (10 ms).
+
+@item in|out.fixed-settings=on|off
+Use fixed settings for host audio.  When off, it will change based on
+how the guest opens the sound card.  In this case you must not specify
+@var{frequency}, @var{channels} or @var{format}.  Default is on.
+
+@item in|out.frequency=@var{frequency}
+Specify the @var{frequency} to use when using @var{fixed-settings}.
+Default is 44100Hz.
+
+@item in|out.channels=@var{channels}
+Specify the number of @var{channels} to use when using
+@var{fixed-settings}. Default is 2 (stereo).
+
+@item in|out.format=@var{format}
+Specify the sample @var{format} to use when using @var{fixed-settings}.
+Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8},
+@code{u16}, @code{u32}. Default is @code{s16}.
+
+@item in|out.voices=@var{voices}
+Specify the number of @var{voices} to use.  Default is 1.
+
+@item in|out.buffer=@var{usecs}
+Sets the size of the buffer in microseconds.
+
+@item in|out.buffer-count=@var{count}
+Sets the @var{count} of the buffers.
+
+@end table
+
+@item -audiodev none,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a dummy backend that discards all outputs.  This backend has no
+backend specific properties.
+
+@item -audiodev alsa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates backend using the ALSA.  This backend is only available on
+Linux.
+
+ALSA specific options are:
+
+@table @option
+@item alsa-in|alsa-out.dev=@var{device}
+Specify the ALSA @var{device} to use for input and/or output.  Default
+is @code{default}.
+
+@item alsa-in|alsa-out.try-poll=on|off
+Attempt to use poll mode with the device.  Default is on.
+
+@item threshold=@var{threshold}
+Threshold (in microseconds) when playback starts.  Default is 0.
+
+@end table
+
+@item -audiodev coreaudio,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using Apple's Core Audio.  This backend is only
+available on Mac OS and only supports playback.  This backend has no
+backend specific properties.
+
+@item -audiodev dsound,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using Microsoft's DirectSound.  This backend is only
+available on Windows and only supports playback.
+
+Backend specific options are:
+
+@table @option
+
+@item latency=@var{usecs}
+Add extra @var{usecs} microseconds latency to playback.  Default is
+10000 (10 ms).
+
+@end table
+
+@item -audiodev oss,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using OSS.  This backend is available on most
+Unix-like systems.
+
+OSS specific options are:
+
+@table @option
+
+@item oss-in|oss-out.dev=@var{device}
+Specify the file name of the OSS @var{device} to use.  Default is
+@code{/dev/dsp}.
+
+@item oss-in|oss-out.try-poll=on|of
+Attempt to use poll mode with the device.  Default is on.
+
+@item try-mmap=on|off
+Try using memory mapped device access.  Default is off.
+
+@item exclusive=on|off
+Open the device in exclusive mode (vmix won't work in this case).
+Default is off.
+
+@item dsp-policy=@var{policy}
+Sets the timing policy (between 0 and 10, where smaller number means
+smaller latency but higher CPU usage).  Use -1 to use buffer sizes
+specified by @code{buffer} and @code{buffer-count}.  This option is
+ignored if you do not have OSS 4. Default is 5.
+
+@end table
+
+@item -audiodev pa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using PulseAudio.  This backend is available on most
+systems.
+
+PulseAudio specific options are:
+
+@table @option
+
+@item server=@var{server}
+Sets the PulseAudio @var{server} to connect to.
+
+@item sink|source.name=@var{sink}
+Use the specified sink/source for playback/recording.
+
+@end table
+
+@item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend using SDL.  This backend is available on most systems,
+but you should use your platform's native backend if possible.  This
+backend has no backend specific properties.
+
+@item -audiodev spice,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend that sends audio through SPICE.  This backend requires
+@code{-spice} and automatically selected in that case, so usually you
+can ignore this option.  This backend has no backend specific
+properties.
+
+@item -audiodev wav,id=@var{id}[,@var{prop}[=@var{value}][,...]]
+Creates a backend that writes audio to a WAV file.
+
+Backend specific options are:
+
+@table @option
+
+@item path=@var{path}
+Write recorded audio into the specified file.  Default is
+@code{qemu.wav}.
+
+@end table
 ETEXI
 
 DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
diff --git a/vl.c b/vl.c
index 0adbbd6..f1aa1df 100644
--- a/vl.c
+++ b/vl.c
@@ -3004,6 +3004,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_trace_opts);
     qemu_add_opts(&qemu_option_rom_opts);
     qemu_add_opts(&qemu_machine_opts);
+    qemu_add_opts(&qemu_audiodev_opts);
     qemu_add_opts(&qemu_mem_opts);
     qemu_add_opts(&qemu_smp_opts);
     qemu_add_opts(&qemu_boot_opts);
@@ -3309,9 +3310,15 @@ int main(int argc, char **argv, char **envp)
                 add_device_config(DEV_BT, optarg);
                 break;
             case QEMU_OPTION_audio_help:
-                AUD_help ();
+                audio_legacy_help();
                 exit (0);
                 break;
+            case QEMU_OPTION_audiodev:
+                if (!qemu_opts_parse_noisily(qemu_find_opts("audiodev"),
+                                             optarg, true)) {
+                    exit(1);
+                }
+                break;
             case QEMU_OPTION_soundhw:
                 select_soundhw (optarg);
                 break;
@@ -4511,6 +4518,7 @@ int main(int argc, char **argv, char **envp)
 
     realtime_init();
 
+    audio_set_options();
     audio_init();
 
     cpu_synchronize_all_post_init();
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 11/25] audio: reduce glob_audio_state usage
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (9 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 10/25] audio: -audiodev command line option Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 17:39   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio Kővágó, Zoltán
                   ` (13 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.c          | 41 ++++++++++++++++++++---------------------
 audio/audio_int.h      |  4 ++++
 audio/audio_template.h | 46 ++++++++++++++++++++++------------------------
 3 files changed, 46 insertions(+), 45 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 08ac15e..05b24dc 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -367,12 +367,10 @@ static void noop_conv (struct st_sample *dst, const void *src, int samples)
     (void) samples;
 }
 
-static CaptureVoiceOut *audio_pcm_capture_find_specific (
-    struct audsettings *as
-    )
+static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
+                                                        struct audsettings *as)
 {
     CaptureVoiceOut *cap;
-    AudioState *s = &glob_audio_state;
 
     for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
         if (audio_pcm_info_eq (&cap->hw.info, as)) {
@@ -449,7 +447,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
 
 static int audio_attach_capture (HWVoiceOut *hw)
 {
-    AudioState *s = &glob_audio_state;
+    AudioState *s = hw->s;
     CaptureVoiceOut *cap;
 
     audio_detach_capture (hw);
@@ -762,15 +760,15 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
 /*
  * Timer
  */
-static int audio_is_timer_needed (void)
+static int audio_is_timer_needed(AudioState *s)
 {
     HWVoiceIn *hwi = NULL;
     HWVoiceOut *hwo = NULL;
 
-    while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
         if (!hwo->poll_mode) return 1;
     }
-    while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
         if (!hwi->poll_mode) return 1;
     }
     return 0;
@@ -778,7 +776,7 @@ static int audio_is_timer_needed (void)
 
 static void audio_reset_timer (AudioState *s)
 {
-    if (audio_is_timer_needed ()) {
+    if (audio_is_timer_needed(s)) {
         timer_mod (s->ts,
             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
     }
@@ -847,7 +845,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
 
     hw = sw->hw;
     if (sw->active != on) {
-        AudioState *s = &glob_audio_state;
+        AudioState *s = sw->s;
         SWVoiceOut *temp_sw;
         SWVoiceCap *sc;
 
@@ -894,7 +892,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
 
     hw = sw->hw;
     if (sw->active != on) {
-        AudioState *s = &glob_audio_state;
+        AudioState *s = sw->s;
         SWVoiceIn *temp_sw;
 
         if (on) {
@@ -1017,7 +1015,7 @@ static void audio_run_out (AudioState *s)
     HWVoiceOut *hw = NULL;
     SWVoiceOut *sw;
 
-    while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) {
+    while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
         int played;
         int live, free, nb_live, cleanup_required, prev_rpos;
 
@@ -1121,7 +1119,7 @@ static void audio_run_in (AudioState *s)
 {
     HWVoiceIn *hw = NULL;
 
-    while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) {
+    while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
         SWVoiceIn *sw;
         int captured, min;
 
@@ -1224,8 +1222,8 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv,
     s->drv_opaque = drv->init(dev);
 
     if (s->drv_opaque) {
-        audio_init_nb_voices_out (drv);
-        audio_init_nb_voices_in (drv);
+        audio_init_nb_voices_out(s, drv);
+        audio_init_nb_voices_in(s, drv);
         s->drv = drv;
         return 0;
     }
@@ -1244,11 +1242,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
     int op = running ? VOICE_ENABLE : VOICE_DISABLE;
 
     s->vm_running = running;
-    while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
         hwo->pcm_ops->ctl_out (hwo, op);
     }
 
-    while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
         hwi->pcm_ops->ctl_in (hwi, op);
     }
     audio_reset_timer (s);
@@ -1260,7 +1258,7 @@ static void audio_atexit (void)
     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))) {
         SWVoiceCap *sc;
 
         if (hwo->enabled) {
@@ -1278,7 +1276,7 @@ static void audio_atexit (void)
         }
     }
 
-    while ((hwi = audio_pcm_hw_find_any_in (hwi))) {
+    while ((hwi = audio_pcm_hw_find_any_in(s, hwi))) {
         if (hwi->enabled) {
             hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
         }
@@ -1459,7 +1457,7 @@ CaptureVoiceOut *AUD_add_capture (
     cb->ops = *ops;
     cb->opaque = cb_opaque;
 
-    cap = audio_pcm_capture_find_specific (as);
+    cap = audio_pcm_capture_find_specific(s, as);
     if (cap) {
         QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
         return cap;
@@ -1476,6 +1474,7 @@ CaptureVoiceOut *AUD_add_capture (
         }
 
         hw = &cap->hw;
+        hw->s = s;
         QLIST_INIT (&hw->sw_head);
         QLIST_INIT (&cap->cb_head);
 
@@ -1509,7 +1508,7 @@ CaptureVoiceOut *AUD_add_capture (
         QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
 
         hw = NULL;
-        while ((hw = audio_pcm_hw_find_any_out (hw))) {
+        while ((hw = audio_pcm_hw_find_any_out(s, hw))) {
             audio_attach_capture (hw);
         }
         return cap;
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 59b2362..101081b 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -51,6 +51,7 @@ struct audio_pcm_info {
 typedef struct SWVoiceCap SWVoiceCap;
 
 typedef struct HWVoiceOut {
+    AudioState *s;
     int enabled;
     int poll_mode;
     int pending_disable;
@@ -72,6 +73,7 @@ typedef struct HWVoiceOut {
 } HWVoiceOut;
 
 typedef struct HWVoiceIn {
+    AudioState *s;
     int enabled;
     int poll_mode;
     struct audio_pcm_info info;
@@ -93,6 +95,7 @@ typedef struct HWVoiceIn {
 
 struct SWVoiceOut {
     QEMUSoundCard *card;
+    AudioState *s;
     struct audio_pcm_info info;
     t_sample *conv;
     int64_t ratio;
@@ -110,6 +113,7 @@ struct SWVoiceOut {
 
 struct SWVoiceIn {
     QEMUSoundCard *card;
+    AudioState *s;
     int active;
     struct audio_pcm_info info;
     int64_t ratio;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 096b2b3..455e50d 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -36,9 +36,9 @@
 #define HWBUF hw->conv_buf
 #endif
 
-static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
+static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
+                                              struct audio_driver *drv)
 {
-    AudioState *s = &glob_audio_state;
     int max_voices = glue (drv->max_voices_, TYPE);
     int voice_size = glue (drv->voice_size_, TYPE);
 
@@ -183,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
 
 static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
 {
-    AudioState *s = &glob_audio_state;
     HW *hw = *hwp;
+    AudioState *s = hw->s;
 
     if (!hw->sw_head.lh_first) {
 #ifdef DAC
@@ -199,15 +199,14 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
     }
 }
 
-static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
+static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
 {
-    AudioState *s = &glob_audio_state;
     return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
 }
 
-static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
+static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
 {
-    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
+    while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
         if (hw->enabled) {
             return hw;
         }
@@ -215,12 +214,10 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
     return NULL;
 }
 
-static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
-    HW *hw,
-    struct audsettings *as
-    )
+static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
+                                                   struct audsettings *as)
 {
-    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
+    while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
         if (audio_pcm_info_eq (&hw->info, as)) {
             return hw;
         }
@@ -228,10 +225,10 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
     return NULL;
 }
 
-static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
+static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
+                                             struct audsettings *as)
 {
     HW *hw;
-    AudioState *s = &glob_audio_state;
     struct audio_driver *drv = s->drv;
 
     if (!glue (s->nb_hw_voices_, TYPE)) {
@@ -255,6 +252,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
         return NULL;
     }
 
+    hw->s = s;
     hw->pcm_ops = drv->pcm_ops;
     hw->ctl_caps = drv->ctl_caps;
 
@@ -299,33 +297,33 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
     return NULL;
 }
 
-static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
+static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
 {
     HW *hw;
-    AudioState *s = &glob_audio_state;
     AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
     if (pdo->fixed_settings) {
-        hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
+        hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
         if (hw) {
             return hw;
         }
     }
 
-    hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
+    hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
     if (hw) {
         return hw;
     }
 
-    hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
+    hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
     if (hw) {
         return hw;
     }
 
-    return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
+    return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
 }
 
-static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
+static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
+    AudioState *s,
     const char *sw_name,
     struct audsettings *as
     )
@@ -333,7 +331,6 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
     SW *sw;
     HW *hw;
     struct audsettings hw_as;
-    AudioState *s = &glob_audio_state;
     AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
     if (pdo->fixed_settings) {
@@ -349,8 +346,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
                sw_name ? sw_name : "unknown", sizeof (*sw));
         goto err1;
     }
+    sw->s = s;
 
-    hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
+    hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
     if (!hw) {
         goto err2;
     }
@@ -447,7 +445,7 @@ SW *glue (AUD_open_, TYPE) (
         }
     }
     else {
-        sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
+        sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
         if (!sw) {
             dolog ("Failed to create voice `%s'\n", name);
             return NULL;
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (10 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 11/25] audio: reduce glob_audio_state usage Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 18:01   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 13/25] audio: add audiodev properties to frontends Kővágó, Zoltán
                   ` (12 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel
  Cc: Markus Armbruster, Paolo Bonzini, Gerd Hoffmann, Luiz Capitulino

Audio functions no longer access glob_audio_state, instead they get an
AudioState as a parameter.  This is required in order to support
multiple backends.

glob_audio_state is also gone, and replaced with a tailq so we can store
more than one states.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.c          | 95 +++++++++++++++++++++++++++++++++++++-------------
 audio/audio.h          | 12 +++++--
 audio/audio_int.h      |  2 ++
 audio/audio_template.h |  2 +-
 audio/wavcapture.c     |  6 ++--
 hmp-commands.hx        | 11 +++---
 monitor.c              | 12 ++++++-
 qemu-options.hx        |  5 +++
 ui/vnc.c               | 15 +++++++-
 ui/vnc.h               |  2 ++
 vl.c                   |  3 +-
 11 files changed, 126 insertions(+), 39 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 05b24dc..10b9871 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -55,7 +55,8 @@ struct audio_driver *drvtab[] = {
     NULL
 };
 
-static AudioState glob_audio_state;
+static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
+    QTAILQ_HEAD_INITIALIZER(audio_states);
 
 const struct mixeng_volume nominal_volume = {
     .mute = 0,
@@ -1193,11 +1194,14 @@ static void audio_run_capture (AudioState *s)
 
 void audio_run (const char *msg)
 {
-    AudioState *s = &glob_audio_state;
+    AudioState *s;
+
+    QTAILQ_FOREACH(s, &audio_states, list) {
+        audio_run_out (s);
+        audio_run_in (s);
+        audio_run_capture (s);
+    }
 
-    audio_run_out (s);
-    audio_run_in (s);
-    audio_run_capture (s);
 #ifdef DEBUG_POLL
     {
         static double prevtime;
@@ -1252,9 +1256,8 @@ static void audio_vm_change_state_handler (void *opaque, int running,
     audio_reset_timer (s);
 }
 
-static void audio_atexit (void)
+static void free_audio_state(AudioState *s)
 {
-    AudioState *s = &glob_audio_state;
     HWVoiceOut *hwo = NULL;
     HWVoiceIn *hwi = NULL;
 
@@ -1288,6 +1291,16 @@ static void audio_atexit (void)
     }
 
     qapi_free_Audiodev(s->dev);
+    g_free(s);
+}
+
+static void audio_atexit(void)
+{
+    while (!QTAILQ_EMPTY(&audio_states)) {
+        AudioState *s = QTAILQ_FIRST(&audio_states);
+        QTAILQ_REMOVE(&audio_states, s, list);
+        free_audio_state(s);
+    }
 }
 
 static const VMStateDescription vmstate_audio = {
@@ -1300,26 +1313,25 @@ static const VMStateDescription vmstate_audio = {
 };
 
 static Audiodev *parse_option(QemuOpts *opts, Error **errp);
-static int audio_init(Audiodev *dev)
+static AudioState *audio_init(Audiodev *dev)
 {
+    static bool atexit_registered;
     size_t i;
     int done = 0;
     const char *drvname = NULL;
     VMChangeStateEntry *e;
-    AudioState *s = &glob_audio_state;
+    AudioState *s;
     QemuOptsList *list = NULL; /* silence gcc warning about uninitialized
                                 * variable */
 
-    if (s->drv) {
-        if (dev) {
-            dolog("Cannot create more than one audio backend, sorry\n");
-            qapi_free_Audiodev(dev);
-        }
-        return -1;
-    }
-
+    /* if we have dev, this function was called because of an -audiodev
+     * argument => initialize a new state with it
+     * if dev == NULL => legacy implicit initialization, return the already
+     * created state or create a new one */
     if (dev) {
         drvname = AudiodevDriver_lookup[dev->kind];
+    } else if (!QTAILQ_EMPTY(&audio_states)) {
+        return QTAILQ_FIRST(&audio_states);
     } else {
         audio_handle_legacy_opts();
         list = qemu_find_opts("audiodev");
@@ -1328,12 +1340,18 @@ static int audio_init(Audiodev *dev)
             exit(1);
         }
     }
+
+    s = g_malloc0(sizeof(AudioState));
     s->dev = dev;
 
     QLIST_INIT (&s->hw_head_out);
     QLIST_INIT (&s->hw_head_in);
     QLIST_INIT (&s->cap_head);
-    atexit (audio_atexit);
+    if (!atexit_registered) {
+        atexit(audio_atexit);
+        atexit_registered = true;
+    }
+    QTAILQ_INSERT_TAIL(&audio_states, s, list);
 
     s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
     if (!s->ts) {
@@ -1414,15 +1432,18 @@ static int audio_init(Audiodev *dev)
 
     QLIST_INIT (&s->card_head);
     vmstate_register (NULL, 0, &vmstate_audio, s);
-    return 0;
+    return s;
 }
 
 void AUD_register_card (const char *name, QEMUSoundCard *card)
 {
-    audio_init(NULL);
+    if (!card->state) {
+        card->state = audio_init(NULL);
+    }
+
     card->name = g_strdup (name);
     memset (&card->entries, 0, sizeof (card->entries));
-    QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
+    QLIST_INSERT_HEAD (&card->state->card_head, card, entries);
 }
 
 void AUD_remove_card (QEMUSoundCard *card)
@@ -1432,16 +1453,20 @@ void AUD_remove_card (QEMUSoundCard *card)
 }
 
 
-CaptureVoiceOut *AUD_add_capture (
+CaptureVoiceOut *AUD_add_capture(
+    AudioState *s,
     struct audsettings *as,
     struct audio_capture_ops *ops,
     void *cb_opaque
     )
 {
-    AudioState *s = &glob_audio_state;
     CaptureVoiceOut *cap;
     struct capture_callback *cb;
 
+    if (!s) { /* todo */
+        s = QTAILQ_FIRST(&audio_states);
+    }
+
     if (audio_validate_settings (as)) {
         dolog ("Invalid settings were passed when trying to add capture\n");
         audio_print_settings (as);
@@ -1677,7 +1702,7 @@ static int each_option(void *opaque, QemuOpts *opts, Error **errp)
     if (!dev) {
         return -1;
     }
-    return audio_init(dev);
+    return audio_init(dev) ? 0 : -1;
 }
 
 void audio_set_options(void)
@@ -1743,3 +1768,25 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
     return audio_buffer_samples(pdo, as, def_usecs) *
         audioformat_bytes_per_sample(as->fmt);
 }
+
+AudioState *audio_state_by_name(const char *name)
+{
+    AudioState *s;
+    QTAILQ_FOREACH(s, &audio_states, list) {
+        assert(s->dev);
+        if (strcmp(name, s->dev->id) == 0) {
+            return s;
+        }
+    }
+    return NULL;
+}
+
+const char *audio_get_id(QEMUSoundCard *card)
+{
+    if (card->state) {
+        assert(card->state->dev);
+        return card->state->dev->id;
+    } else {
+        return "";
+    }
+}
diff --git a/audio/audio.h b/audio/audio.h
index 177a673..0085a07 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -80,8 +80,10 @@ typedef struct SWVoiceOut SWVoiceOut;
 typedef struct CaptureVoiceOut CaptureVoiceOut;
 typedef struct SWVoiceIn SWVoiceIn;
 
+typedef struct AudioState AudioState;
 typedef struct QEMUSoundCard {
     char *name;
+    AudioState *state;
     QLIST_ENTRY (QEMUSoundCard) entries;
 } QEMUSoundCard;
 
@@ -96,7 +98,8 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
 
 void AUD_register_card (const char *name, QEMUSoundCard *card);
 void AUD_remove_card (QEMUSoundCard *card);
-CaptureVoiceOut *AUD_add_capture (
+CaptureVoiceOut *AUD_add_capture(
+    AudioState *s,
     struct audsettings *as,
     struct audio_capture_ops *ops,
     void *opaque
@@ -164,11 +167,14 @@ static inline void *advance (void *p, int incr)
 #define audio_MAX(a, b) ((a)<(b)?(b):(a))
 #endif
 
-int wav_start_capture (CaptureState *s, const char *path, int freq,
-                       int bits, int nchannels);
+int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
+                      int freq, int bits, int nchannels);
 
 void audio_set_options(void);
 void audio_handle_legacy_opts(void);
 void audio_legacy_help(void);
 
+AudioState *audio_state_by_name(const char *name);
+const char *audio_get_id(QEMUSoundCard *card);
+
 #endif  /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 101081b..1d81658 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -189,6 +189,8 @@ struct AudioState {
     int nb_hw_voices_in;
     int vm_running;
     int64_t period_ticks;
+
+    QTAILQ_ENTRY(AudioState) list;
 };
 
 extern struct audio_driver no_audio_driver;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 455e50d..5a3dc90 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -399,7 +399,7 @@ SW *glue (AUD_open_, TYPE) (
     struct audsettings *as
     )
 {
-    AudioState *s = &glob_audio_state;
+    AudioState *s = card->state;
     AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
 
     if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index 798acdd..8b8d3d0 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -104,8 +104,8 @@ static struct capture_ops wav_capture_ops = {
     .info = wav_capture_info
 };
 
-int wav_start_capture (CaptureState *s, const char *path, int freq,
-                       int bits, int nchannels)
+int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
+                      int freq, int bits, int nchannels)
 {
     Monitor *mon = cur_mon;
     WAVState *wav;
@@ -172,7 +172,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
         goto error_free;
     }
 
-    cap = AUD_add_capture (&as, &ops, wav);
+    cap = AUD_add_capture(state, &as, &ops, wav);
     if (!cap) {
         monitor_printf (mon, "Failed to add audio capture\n");
         goto error_free;
diff --git a/hmp-commands.hx b/hmp-commands.hx
index d3b7932..9b49dc3 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -741,16 +741,17 @@ ETEXI
 
     {
         .name       = "wavcapture",
-        .args_type  = "path:F,freq:i?,bits:i?,nchannels:i?",
-        .params     = "path [frequency [bits [channels]]]",
+        .args_type  = "path:F,freq:i?,bits:i?,nchannels:i?,audiodev:s?",
+        .params     = "path [frequency [bits [channels [audiodev]]]]",
         .help       = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
         .mhandler.cmd = hmp_wavcapture,
     },
 STEXI
-@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]]
+@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels} [@var{audiodev}]]]]
 @findex wavcapture
-Capture audio into @var{filename}. Using sample rate @var{frequency}
-bits per sample @var{bits} and number of channels @var{channels}.
+Capture audio into @var{filename} from @var{audiodev}. Using sample rate
+@var{frequency} bits per sample @var{bits} and number of channels
+@var{channels}.
 
 Defaults:
 @itemize @minus
diff --git a/monitor.c b/monitor.c
index a40138b..f527753 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1944,7 +1944,17 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
     int bits = qdict_get_try_int(qdict, "bits", -1);
     int has_channels = qdict_haskey(qdict, "nchannels");
     int nchannels = qdict_get_try_int(qdict, "nchannels", -1);
+    const char *audiodev = qdict_get_try_str(qdict, "audiodev");
     CaptureState *s;
+    AudioState *as = NULL;
+
+    if (audiodev) {
+        as = audio_state_by_name(audiodev);
+        if (!as) {
+            monitor_printf(mon, "Invalid audiodev specified\n");
+            return;
+        }
+    }
 
     s = g_malloc0 (sizeof (*s));
 
@@ -1952,7 +1962,7 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
     bits = has_bits ? bits : 16;
     nchannels = has_channels ? nchannels : 2;
 
-    if (wav_start_capture (s, path, freq, bits, nchannels)) {
+    if (wav_start_capture(as, s, path, freq, bits, nchannels)) {
         monitor_printf(mon, "Failed to add wave capture\n");
         g_free (s);
         return;
diff --git a/qemu-options.hx b/qemu-options.hx
index efd57e6..60a3563 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1538,6 +1538,11 @@ everybody else.  'ignore' completely ignores the shared flag and
 allows everybody connect unconditionally.  Doesn't conform to the rfb
 spec but is traditional QEMU behavior.
 
+@item audiodev=@var{audiodev}
+
+Use the specified @var{audiodev} when the VNC client requests audio
+transmission.
+
 @end table
 ETEXI
 
diff --git a/ui/vnc.c b/ui/vnc.c
index febbad6..feee4ef 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1193,7 +1193,7 @@ static void audio_add(VncState *vs)
     ops.destroy = audio_capture_destroy;
     ops.capture = audio_capture;
 
-    vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs);
+    vs->audio_cap = AUD_add_capture(vs->vd->audio_state, &vs->as, &ops, vs);
     if (!vs->audio_cap) {
         error_report("Failed to add audio capture");
     }
@@ -3295,6 +3295,9 @@ static QemuOptsList qemu_vnc_opts = {
         },{
             .name = "non-adaptive",
             .type = QEMU_OPT_BOOL,
+        },{
+            .name = "audiodev",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
@@ -3455,6 +3458,7 @@ void vnc_display_open(const char *id, Error **errp)
     int acl = 0;
 #endif
     int lock_key_sync = 1;
+    const char *audiodev;
 
     if (!vs) {
         error_setg(errp, "VNC display not active");
@@ -3637,6 +3641,15 @@ void vnc_display_open(const char *id, Error **errp)
 #endif
     vs->lock_key_sync = lock_key_sync;
 
+    audiodev = qemu_opt_get(opts, "audiodev");
+    if (audiodev) {
+        vs->audio_state = audio_state_by_name(audiodev);
+        if (!vs->audio_state) {
+            error_setg(errp, "Audiodev '%s' not found", audiodev);
+            goto fail;
+        }
+    }
+
     device_id = qemu_opt_get(opts, "display");
     if (device_id) {
         DeviceState *dev;
diff --git a/ui/vnc.h b/ui/vnc.h
index 814d720..f73bb94 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -187,6 +187,8 @@ struct VncDisplay
 #ifdef CONFIG_VNC_SASL
     VncDisplaySASL sasl;
 #endif
+
+    AudioState *audio_state;
 };
 
 typedef struct VncTight {
diff --git a/vl.c b/vl.c
index f1aa1df..30bc8be 100644
--- a/vl.c
+++ b/vl.c
@@ -4315,6 +4315,8 @@ int main(int argc, char **argv, char **envp)
         exit(1);
     }
 
+    audio_set_options();
+
     machine_opts = qemu_get_machine_opts();
     if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine,
                          NULL)) {
@@ -4518,7 +4520,6 @@ int main(int argc, char **argv, char **envp)
 
     realtime_init();
 
-    audio_set_options();
     audio_init();
 
     cpu_synchronize_all_post_init();
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 13/25] audio: add audiodev properties to frontends
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (11 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 18:55   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 14/25] audio: audiodev= parameters no longer optional when -audiodev present Kővágó, Zoltán
                   ` (11 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Michael Walle, Gerd Hoffmann

Finally add audiodev= options to audio frontends so users can specify
which backend to use when multiple backends exist.  Not specifying an
audiodev= option currently causes the first audiodev to be used, this is
fixed in the next commit.

Example usage: -audiodev pa,id=foo -device AC97,audiodev=foo

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.h                    |  3 +++
 hw/audio/ac97.c                  |  1 +
 hw/audio/adlib.c                 |  1 +
 hw/audio/cs4231a.c               |  1 +
 hw/audio/es1370.c                |  7 ++++-
 hw/audio/gus.c                   |  1 +
 hw/audio/hda-codec.c             |  1 +
 hw/audio/milkymist-ac97.c        |  6 +++++
 hw/audio/pcspk.c                 |  1 +
 hw/audio/pl041.c                 |  1 +
 hw/audio/sb16.c                  |  1 +
 hw/audio/wm8750.c                |  6 +++++
 hw/core/qdev-properties-system.c | 57 ++++++++++++++++++++++++++++++++++++++++
 hw/usb/dev-audio.c               |  1 +
 include/hw/qdev-properties.h     |  3 +++
 15 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/audio/audio.h b/audio/audio.h
index 0085a07..68545b6 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -177,4 +177,7 @@ void audio_legacy_help(void);
 AudioState *audio_state_by_name(const char *name);
 const char *audio_get_id(QEMUSoundCard *card);
 
+#define DEFINE_AUDIO_PROPERTIES(_s, _f)         \
+    DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
+
 #endif  /* audio.h */
diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c
index fa75f33..a835cb6 100644
--- a/hw/audio/ac97.c
+++ b/hw/audio/ac97.c
@@ -1393,6 +1393,7 @@ static int ac97_init (PCIBus *bus)
 }
 
 static Property ac97_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(AC97LinkState, card),
     DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0),
     DEFINE_PROP_END_OF_LIST (),
 };
diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
index f8f0f55..58de8b0 100644
--- a/hw/audio/adlib.c
+++ b/hw/audio/adlib.c
@@ -352,6 +352,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
 }
 
 static Property adlib_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(AdlibState, card),
     DEFINE_PROP_UINT32 ("iobase",  AdlibState, port, 0x220),
     DEFINE_PROP_UINT32 ("freq",    AdlibState, freq,  44100),
     DEFINE_PROP_END_OF_LIST (),
diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
index 626a173..43a1a39 100644
--- a/hw/audio/cs4231a.c
+++ b/hw/audio/cs4231a.c
@@ -672,6 +672,7 @@ static int cs4231a_init (ISABus *bus)
 }
 
 static Property cs4231a_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(CSState, card),
     DEFINE_PROP_UINT32 ("iobase",  CSState, port, 0x534),
     DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
     DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
index f6e74cb..fe6110d 100644
--- a/hw/audio/es1370.c
+++ b/hw/audio/es1370.c
@@ -1047,6 +1047,11 @@ static int es1370_init (PCIBus *bus)
     return 0;
 }
 
+static Property es1370_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(ES1370State, card),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void es1370_class_init (ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS (klass);
@@ -1061,6 +1066,7 @@ static void es1370_class_init (ObjectClass *klass, void *data)
     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
     dc->desc = "ENSONIQ AudioPCI ES1370";
     dc->vmsd = &vmstate_es1370;
+    dc->props = es1370_properties;
 }
 
 static const TypeInfo es1370_info = {
@@ -1077,4 +1083,3 @@ static void es1370_register_types (void)
 }
 
 type_init (es1370_register_types)
-
diff --git a/hw/audio/gus.c b/hw/audio/gus.c
index 6107824..ee08d50 100644
--- a/hw/audio/gus.c
+++ b/hw/audio/gus.c
@@ -284,6 +284,7 @@ static int GUS_init (ISABus *bus)
 }
 
 static Property gus_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(GUSState, card),
     DEFINE_PROP_UINT32 ("freq",    GUSState, freq,        44100),
     DEFINE_PROP_UINT32 ("iobase",  GUSState, port,        0x240),
     DEFINE_PROP_UINT32 ("irq",     GUSState, emu.gusirq,  7),
diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
index 8693b7a..dba3331 100644
--- a/hw/audio/hda-codec.c
+++ b/hw/audio/hda-codec.c
@@ -613,6 +613,7 @@ static const VMStateDescription vmstate_hda_audio = {
 };
 
 static Property hda_audio_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(HDAAudioState, card),
     DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
     DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
     DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c
index 15169e2..b63fd6f 100644
--- a/hw/audio/milkymist-ac97.c
+++ b/hw/audio/milkymist-ac97.c
@@ -323,6 +323,11 @@ static const VMStateDescription vmstate_milkymist_ac97 = {
     }
 };
 
+static Property milkymist_ac97_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(MilkymistAC97State, card),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -331,6 +336,7 @@ static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
     k->init = milkymist_ac97_init;
     dc->reset = milkymist_ac97_reset;
     dc->vmsd = &vmstate_milkymist_ac97;
+    dc->props = milkymist_ac97_properties;
 }
 
 static const TypeInfo milkymist_ac97_info = {
diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
index 302debf..85d6d00 100644
--- a/hw/audio/pcspk.c
+++ b/hw/audio/pcspk.c
@@ -181,6 +181,7 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp)
 }
 
 static Property pcspk_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(PCSpkState, card),
     DEFINE_PROP_UINT32("iobase", PCSpkState, iobase,  -1),
     DEFINE_PROP_PTR("pit", PCSpkState, pit),
     DEFINE_PROP_END_OF_LIST(),
diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c
index 19982f2..c003b26 100644
--- a/hw/audio/pl041.c
+++ b/hw/audio/pl041.c
@@ -616,6 +616,7 @@ static const VMStateDescription vmstate_pl041 = {
 };
 
 static Property pl041_device_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(PL041State, codec.card),
     /* Non-compact FIFO depth property */
     DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth,
                        DEFAULT_FIFO_DEPTH),
diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
index a159dcc..4bbb718 100644
--- a/hw/audio/sb16.c
+++ b/hw/audio/sb16.c
@@ -1391,6 +1391,7 @@ static int SB16_init (ISABus *bus)
 }
 
 static Property sb16_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(SB16State, card),
     DEFINE_PROP_UINT32 ("version", SB16State, ver,  0x0405), /* 4.5 */
     DEFINE_PROP_UINT32 ("iobase",  SB16State, port, 0x220),
     DEFINE_PROP_UINT32 ("irq",     SB16State, irq,  5),
diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
index 4c4333c..049ddf9 100644
--- a/hw/audio/wm8750.c
+++ b/hw/audio/wm8750.c
@@ -695,6 +695,11 @@ void wm8750_set_bclk_in(void *opaque, int new_hz)
     wm8750_clk_update(s, 1);
 }
 
+static Property wm8750_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(WM8750State, card),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void wm8750_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
@@ -705,6 +710,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
     sc->recv = wm8750_rx;
     sc->send = wm8750_tx;
     dc->vmsd = &vmstate_wm8750;
+    dc->props = wm8750_properties;
 }
 
 static const TypeInfo wm8750_info = {
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 249976e..3807c86 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -10,6 +10,7 @@
  * See the COPYING file in the top-level directory.
  */
 
+#include "audio/audio.h"
 #include "net/net.h"
 #include "hw/qdev.h"
 #include "qapi/qmp/qerror.h"
@@ -357,6 +358,62 @@ PropertyInfo qdev_prop_vlan = {
     .set   = set_vlan,
 };
 
+/* --- audiodev --- */
+static void get_audiodev(Object *obj, Visitor *v, void *opaque,
+                         const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
+    char *p = g_strdup(audio_get_id(card));
+
+    visit_type_str(v, &p, name, errp);
+    g_free(p);
+}
+
+static void set_audiodev(Object *obj, Visitor *v, void *opaque,
+                         const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
+    AudioState *state;
+    Error *local_err = NULL;
+    int err = 0;
+    char *str;
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_str(v, &str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    state = audio_state_by_name(str);
+
+    if (!state) {
+        err = -ENOENT;
+        goto out;
+    }
+    card->state = state;
+
+out:
+    error_set_from_qdev_prop_error(errp, err, dev, prop, str);
+    g_free(str);
+}
+
+PropertyInfo qdev_prop_audiodev = {
+    .name = "str",
+    .description = "ID of an audiodev to use as a backend",
+    /* release done on shutdown */
+    .get = get_audiodev,
+    .set = set_audiodev,
+};
+
 void qdev_prop_set_drive(DeviceState *dev, const char *name,
                          BlockBackend *value, Error **errp)
 {
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 0171579..48ac992 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -662,6 +662,7 @@ static const VMStateDescription vmstate_usb_audio = {
 };
 
 static Property usb_audio_properties[] = {
+    DEFINE_AUDIO_PROPERTIES(USBAudioState, card),
     DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
     DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
                        8 * USBAUDIO_PACKET_SIZE),
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index 77538a8..edebcfa 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -26,6 +26,7 @@ extern PropertyInfo qdev_prop_vlan;
 extern PropertyInfo qdev_prop_pci_devfn;
 extern PropertyInfo qdev_prop_blocksize;
 extern PropertyInfo qdev_prop_pci_host_devaddr;
+extern PropertyInfo qdev_prop_audiodev;
 extern PropertyInfo qdev_prop_arraylen;
 
 #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \
@@ -163,6 +164,8 @@ extern PropertyInfo qdev_prop_arraylen;
     DEFINE_PROP_DEFAULT(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t)
 #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \
     DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress)
+#define DEFINE_PROP_AUDIODEV(_n, _s, _f) \
+    DEFINE_PROP(_n, _s, _f, qdev_prop_audiodev, QEMUSoundCard)
 
 #define DEFINE_PROP_END_OF_LIST()               \
     {}
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 14/25] audio: audiodev= parameters no longer optional when -audiodev present
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (12 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 13/25] audio: add audiodev properties to frontends Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 19:15   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server Kővágó, Zoltán
                   ` (10 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/audio/audio.c b/audio/audio.c
index 10b9871..7468b94 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -69,6 +69,8 @@ const struct mixeng_volume nominal_volume = {
 #endif
 };
 
+static bool legacy_config;
+
 #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
 #error No its not
 #else
@@ -1331,8 +1333,13 @@ static AudioState *audio_init(Audiodev *dev)
     if (dev) {
         drvname = AudiodevDriver_lookup[dev->kind];
     } else if (!QTAILQ_EMPTY(&audio_states)) {
+        if (!legacy_config) {
+            dolog("Must specify audiodev when using -audiodev\n");
+            exit(1);
+        }
         return QTAILQ_FIRST(&audio_states);
     } else {
+        legacy_config = true;
         audio_handle_legacy_opts();
         list = qemu_find_opts("audiodev");
         dev = parse_option(QTAILQ_FIRST(&list->head), &error_abort);
@@ -1463,7 +1470,11 @@ CaptureVoiceOut *AUD_add_capture(
     CaptureVoiceOut *cap;
     struct capture_callback *cb;
 
-    if (!s) { /* todo */
+    if (!s) {
+        if (!legacy_config) {
+            dolog("Must specify audiodev when using -audiodev\n");
+            goto err0;
+        }
         s = QTAILQ_FIRST(&audio_states);
     }
 
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (13 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 14/25] audio: audiodev= parameters no longer optional when -audiodev present Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 19:38   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 16/25] paaudio: properly disconnect streams in fini_* Kővágó, Zoltán
                   ` (9 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/paaudio.c | 301 ++++++++++++++++++++++++++++++++------------------------
 1 file changed, 175 insertions(+), 126 deletions(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index a53aaf6..e3b8207 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -9,10 +9,21 @@
 #include "audio_int.h"
 #include "audio_pt_int.h"
 
-typedef struct {
-    Audiodev *dev;
+typedef struct PAConnection {
+    char *server;
+    int refcount;
+    QTAILQ_ENTRY(PAConnection) list;
+
     pa_threaded_mainloop *mainloop;
     pa_context *context;
+} PAConnection;
+
+static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
+    QTAILQ_HEAD_INITIALIZER(pa_conns);
+
+typedef struct {
+    Audiodev *dev;
+    PAConnection *conn;
 } paaudio;
 
 typedef struct {
@@ -43,7 +54,7 @@ typedef struct {
     int samples;
 } PAVoiceIn;
 
-static void qpa_audio_fini(void *opaque);
+static void qpa_conn_fini(PAConnection *c);
 
 static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
 {
@@ -106,11 +117,11 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
 
 static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
 {
-    paaudio *g = p->g;
+    PAConnection *c = p->g->conn;
 
-    pa_threaded_mainloop_lock (g->mainloop);
+    pa_threaded_mainloop_lock(c->mainloop);
 
-    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+    CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
 
     while (length > 0) {
         size_t l;
@@ -119,11 +130,11 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror
             int r;
 
             r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
-            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
+            CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
 
             if (!p->read_data) {
-                pa_threaded_mainloop_wait (g->mainloop);
-                CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+                pa_threaded_mainloop_wait(c->mainloop);
+                CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
             } else {
                 p->read_index = 0;
             }
@@ -146,53 +157,53 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror
             p->read_length = 0;
             p->read_index = 0;
 
-            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
+            CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
         }
     }
 
-    pa_threaded_mainloop_unlock (g->mainloop);
+    pa_threaded_mainloop_unlock(c->mainloop);
     return 0;
 
 unlock_and_fail:
-    pa_threaded_mainloop_unlock (g->mainloop);
+    pa_threaded_mainloop_unlock(c->mainloop);
     return -1;
 }
 
 static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
 {
-    paaudio *g = p->g;
+    PAConnection *c = p->g->conn;
 
-    pa_threaded_mainloop_lock (g->mainloop);
+    pa_threaded_mainloop_lock(c->mainloop);
 
-    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+    CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
 
     while (length > 0) {
         size_t l;
         int r;
 
         while (!(l = pa_stream_writable_size (p->stream))) {
-            pa_threaded_mainloop_wait (g->mainloop);
-            CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+            pa_threaded_mainloop_wait(c->mainloop);
+            CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
         }
 
-        CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
+        CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
 
         if (l > length) {
             l = length;
         }
 
         r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
-        CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
+        CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
 
         data = (const uint8_t *) data + l;
         length -= l;
     }
 
-    pa_threaded_mainloop_unlock (g->mainloop);
+    pa_threaded_mainloop_unlock(c->mainloop);
     return 0;
 
 unlock_and_fail:
-    pa_threaded_mainloop_unlock (g->mainloop);
+    pa_threaded_mainloop_unlock(c->mainloop);
     return -1;
 }
 
@@ -430,13 +441,13 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
 
 static void context_state_cb (pa_context *c, void *userdata)
 {
-    paaudio *g = userdata;
+    PAConnection *conn = userdata;
 
     switch (pa_context_get_state(c)) {
     case PA_CONTEXT_READY:
     case PA_CONTEXT_TERMINATED:
     case PA_CONTEXT_FAILED:
-        pa_threaded_mainloop_signal (g->mainloop, 0);
+        pa_threaded_mainloop_signal(conn->mainloop, 0);
         break;
 
     case PA_CONTEXT_UNCONNECTED:
@@ -449,14 +460,14 @@ static void context_state_cb (pa_context *c, void *userdata)
 
 static void stream_state_cb (pa_stream *s, void * userdata)
 {
-    paaudio *g = userdata;
+    PAConnection *c = userdata;
 
     switch (pa_stream_get_state (s)) {
 
     case PA_STREAM_READY:
     case PA_STREAM_FAILED:
     case PA_STREAM_TERMINATED:
-        pa_threaded_mainloop_signal (g->mainloop, 0);
+        pa_threaded_mainloop_signal(c->mainloop, 0);
         break;
 
     case PA_STREAM_UNCONNECTED:
@@ -467,13 +478,13 @@ static void stream_state_cb (pa_stream *s, void * userdata)
 
 static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
 {
-    paaudio *g = userdata;
+    PAConnection *c = userdata;
 
-    pa_threaded_mainloop_signal (g->mainloop, 0);
+    pa_threaded_mainloop_signal(c->mainloop, 0);
 }
 
 static pa_stream *qpa_simple_new (
-        paaudio *g,
+        PAConnection *c,
         const char *name,
         pa_stream_direction_t dir,
         const char *dev,
@@ -484,50 +495,52 @@ static pa_stream *qpa_simple_new (
 {
     int r;
     pa_stream *stream;
+    pa_stream_flags_t flags;
 
-    pa_threaded_mainloop_lock (g->mainloop);
+    pa_threaded_mainloop_lock(c->mainloop);
 
-    stream = pa_stream_new (g->context, name, ss, map);
+    stream = pa_stream_new(c->context, name, ss, map);
     if (!stream) {
         goto fail;
     }
 
-    pa_stream_set_state_callback (stream, stream_state_cb, g);
-    pa_stream_set_read_callback (stream, stream_request_cb, g);
-    pa_stream_set_write_callback (stream, stream_request_cb, g);
+    pa_stream_set_state_callback (stream, stream_state_cb, c);
+    pa_stream_set_read_callback (stream, stream_request_cb, c);
+    pa_stream_set_write_callback (stream, stream_request_cb, c);
+
+    flags =
+        PA_STREAM_INTERPOLATE_TIMING
+#ifdef PA_STREAM_ADJUST_LATENCY
+        |PA_STREAM_ADJUST_LATENCY
+#endif
+        |PA_STREAM_AUTO_TIMING_UPDATE;
+    if (dev) {
+        /* don't move the stream if the user specified a sink/source */
+        flags |= PA_STREAM_DONT_MOVE;
+    }
 
     if (dir == PA_STREAM_PLAYBACK) {
-        r = pa_stream_connect_playback (stream, dev, attr,
-                                        PA_STREAM_INTERPOLATE_TIMING
-#ifdef PA_STREAM_ADJUST_LATENCY
-                                        |PA_STREAM_ADJUST_LATENCY
-#endif
-                                        |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+        r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
     } else {
-        r = pa_stream_connect_record (stream, dev, attr,
-                                      PA_STREAM_INTERPOLATE_TIMING
-#ifdef PA_STREAM_ADJUST_LATENCY
-                                      |PA_STREAM_ADJUST_LATENCY
-#endif
-                                      |PA_STREAM_AUTO_TIMING_UPDATE);
+        r = pa_stream_connect_record(stream, dev, attr, flags);
     }
 
     if (r < 0) {
       goto fail;
     }
 
-    pa_threaded_mainloop_unlock (g->mainloop);
+    pa_threaded_mainloop_unlock(c->mainloop);
 
     return stream;
 
 fail:
-    pa_threaded_mainloop_unlock (g->mainloop);
+    pa_threaded_mainloop_unlock(c->mainloop);
 
     if (stream) {
         pa_stream_unref (stream);
     }
 
-    *rerror = pa_context_errno (g->context);
+    *rerror = pa_context_errno(c->context);
 
     return NULL;
 }
@@ -543,6 +556,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     paaudio *g = pa->g = drv_opaque;
     AudiodevPaOptions *popts = g->dev->pa;
     AudiodevPaPerDirectionOptions *ppdo = popts->sink;
+    PAConnection *c = g->conn;
 
     ss.format = audfmt_to_pa (as->fmt, as->endianness);
     ss.channels = as->nchannels;
@@ -560,7 +574,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 
     pa->stream = qpa_simple_new (
-        g,
+        c,
         "qemu",
         PA_STREAM_PLAYBACK,
         ppdo->has_name ? ppdo->name : NULL,
@@ -612,6 +626,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     paaudio *g = pa->g = drv_opaque;
     AudiodevPaOptions *popts = g->dev->pa;
     AudiodevPaPerDirectionOptions *ppdo = popts->source;
+    PAConnection *c = g->conn;
 
     ss.format = audfmt_to_pa (as->fmt, as->endianness);
     ss.channels = as->nchannels;
@@ -620,7 +635,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
 
     pa->stream = qpa_simple_new (
-        g,
+        c,
         "qemu",
         PA_STREAM_RECORD,
         ppdo->has_name ? ppdo->name : NULL,
@@ -708,7 +723,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
     PAVoiceOut *pa = (PAVoiceOut *) hw;
     pa_operation *op;
     pa_cvolume v;
-    paaudio *g = pa->g;
+    PAConnection *c = pa->g->conn;
 
 #ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */
     pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
@@ -728,28 +743,28 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
             v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
             v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
 
-            pa_threaded_mainloop_lock (g->mainloop);
+            pa_threaded_mainloop_lock(c->mainloop);
 
-            op = pa_context_set_sink_input_volume (g->context,
+            op = pa_context_set_sink_input_volume(c->context,
                 pa_stream_get_index (pa->stream),
                 &v, NULL, NULL);
             if (!op)
-                qpa_logerr (pa_context_errno (g->context),
+                qpa_logerr (pa_context_errno(c->context),
                             "set_sink_input_volume() failed\n");
             else
                 pa_operation_unref (op);
 
-            op = pa_context_set_sink_input_mute (g->context,
+            op = pa_context_set_sink_input_mute(c->context,
                 pa_stream_get_index (pa->stream),
                sw->vol.mute, NULL, NULL);
             if (!op) {
-                qpa_logerr (pa_context_errno (g->context),
+                qpa_logerr (pa_context_errno(c->context),
                             "set_sink_input_mute() failed\n");
             } else {
                 pa_operation_unref (op);
             }
 
-            pa_threaded_mainloop_unlock (g->mainloop);
+            pa_threaded_mainloop_unlock(c->mainloop);
         }
     }
     return 0;
@@ -760,7 +775,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
     PAVoiceIn *pa = (PAVoiceIn *) hw;
     pa_operation *op;
     pa_cvolume v;
-    paaudio *g = pa->g;
+    PAConnection *c = pa->g->conn;
 
 #ifdef PA_CHECK_VERSION
     pa_cvolume_init (&v);
@@ -780,123 +795,157 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
             v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
             v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
 
-            pa_threaded_mainloop_lock (g->mainloop);
+            pa_threaded_mainloop_lock(c->mainloop);
 
             /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
-            op = pa_context_set_source_volume_by_index (g->context,
+            op = pa_context_set_source_volume_by_index(c->context,
                 pa_stream_get_device_index (pa->stream),
                 &v, NULL, NULL);
             if (!op) {
-                qpa_logerr (pa_context_errno (g->context),
+                qpa_logerr (pa_context_errno(c->context),
                             "set_source_volume() failed\n");
             } else {
                 pa_operation_unref(op);
             }
 
-            op = pa_context_set_source_mute_by_index (g->context,
+            op = pa_context_set_source_mute_by_index(c->context,
                 pa_stream_get_index (pa->stream),
                 sw->vol.mute, NULL, NULL);
             if (!op) {
-                qpa_logerr (pa_context_errno (g->context),
+                qpa_logerr (pa_context_errno(c->context),
                             "set_source_mute() failed\n");
             } else {
                 pa_operation_unref (op);
             }
 
-            pa_threaded_mainloop_unlock (g->mainloop);
+            pa_threaded_mainloop_unlock(c->mainloop);
         }
     }
     return 0;
 }
 
 /* common */
+static void *qpa_conn_init(const char *server)
+{
+    PAConnection *c = g_malloc0(sizeof(PAConnection));
+    QTAILQ_INSERT_TAIL(&pa_conns, c, list);
+
+    c->mainloop = pa_threaded_mainloop_new();
+    if (!c->mainloop) {
+        goto fail;
+    }
+
+    c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
+                                server);
+    if (!c->context) {
+        goto fail;
+    }
+
+    pa_context_set_state_callback(c->context, context_state_cb, c);
+
+    if (pa_context_connect(c->context, server, 0, NULL) < 0) {
+        qpa_logerr(pa_context_errno(c->context),
+                   "pa_context_connect() failed\n");
+        goto fail;
+    }
+
+    pa_threaded_mainloop_lock(c->mainloop);
+
+    if (pa_threaded_mainloop_start(c->mainloop) < 0) {
+        goto unlock_and_fail;
+    }
+
+    for (;;) {
+        pa_context_state_t state;
+
+        state = pa_context_get_state(c->context);
+
+        if (state == PA_CONTEXT_READY) {
+            break;
+        }
+
+        if (!PA_CONTEXT_IS_GOOD (state)) {
+            qpa_logerr(pa_context_errno(c->context),
+                       "Wrong context state\n");
+            goto unlock_and_fail;
+        }
+
+        /* Wait until the context is ready */
+        pa_threaded_mainloop_wait(c->mainloop);
+    }
+
+    pa_threaded_mainloop_unlock(c->mainloop);
+    return c;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(c->mainloop);
+fail:
+    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
+    qpa_conn_fini(c);
+    return NULL;
+}
+
 static void *qpa_audio_init(Audiodev *dev)
 {
     paaudio *g;
     AudiodevPaOptions *popts;
     const char *server;
+    PAConnection *c;
 
     assert(dev->kind == AUDIODEV_DRIVER_PA);
 
-    g = g_malloc(sizeof(paaudio));
+    g = g_malloc0(sizeof(paaudio));
     popts = dev->pa;
     server = popts->has_server ? popts->server : NULL;
 
     g->dev = dev;
-    g->mainloop = NULL;
-    g->context = NULL;
 
-    g->mainloop = pa_threaded_mainloop_new ();
-    if (!g->mainloop) {
-        goto fail;
-    }
-
-    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
-                                 server);
-    if (!g->context) {
-        goto fail;
-    }
-
-    pa_context_set_state_callback (g->context, context_state_cb, g);
-
-    if (pa_context_connect (g->context, server, 0, NULL) < 0) {
-        qpa_logerr (pa_context_errno (g->context),
-                    "pa_context_connect() failed\n");
-        goto fail;
-    }
-
-    pa_threaded_mainloop_lock (g->mainloop);
-
-    if (pa_threaded_mainloop_start (g->mainloop) < 0) {
-        goto unlock_and_fail;
-    }
-
-    for (;;) {
-        pa_context_state_t state;
-
-        state = pa_context_get_state (g->context);
-
-        if (state == PA_CONTEXT_READY) {
+    QTAILQ_FOREACH(c, &pa_conns, list) {
+        if (server == NULL || c->server == NULL ?
+            server == c->server :
+            strcmp(server, c->server) == 0) {
+            g->conn = c;
             break;
         }
-
-        if (!PA_CONTEXT_IS_GOOD (state)) {
-            qpa_logerr (pa_context_errno (g->context),
-                        "Wrong context state\n");
-            goto unlock_and_fail;
-        }
-
-        /* Wait until the context is ready */
-        pa_threaded_mainloop_wait (g->mainloop);
+    }
+    if (!g->conn) {
+        g->conn = qpa_conn_init(server);
+    }
+    if (!g->conn) {
+        g_free(g);
+        return NULL;
     }
 
-    pa_threaded_mainloop_unlock (g->mainloop);
-
+    ++g->conn->refcount;
     return g;
+}
 
-unlock_and_fail:
-    pa_threaded_mainloop_unlock (g->mainloop);
-fail:
-    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
-    qpa_audio_fini(g);
-    return NULL;
+static void qpa_conn_fini(PAConnection *c)
+{
+    if (c->mainloop) {
+        pa_threaded_mainloop_stop(c->mainloop);
+    }
+
+    if (c->context) {
+        pa_context_disconnect(c->context);
+        pa_context_unref(c->context);
+    }
+
+    if (c->mainloop) {
+        pa_threaded_mainloop_free(c->mainloop);
+    }
+
+    QTAILQ_REMOVE(&pa_conns, c, list);
+    g_free(c);
 }
 
 static void qpa_audio_fini (void *opaque)
 {
     paaudio *g = opaque;
+    PAConnection *c = g->conn;
 
-    if (g->mainloop) {
-        pa_threaded_mainloop_stop (g->mainloop);
-    }
-
-    if (g->context) {
-        pa_context_disconnect (g->context);
-        pa_context_unref (g->context);
-    }
-
-    if (g->mainloop) {
-        pa_threaded_mainloop_free (g->mainloop);
+    if (--c->refcount == 0) {
+        qpa_conn_fini(c);
     }
 
     g_free(g);
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 16/25] paaudio: properly disconnect streams in fini_*
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (14 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 20:29   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX Kővágó, Zoltán
                   ` (8 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Currently this needs a workaround due to bug #74624 in pulseaudio.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/paaudio.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index e3b8207..2aee22f 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -678,6 +678,25 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     return -1;
 }
 
+static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
+{
+    int err;
+
+    pa_threaded_mainloop_lock(c->mainloop);
+    /* wait until actually connects. workaround pa bug #74624
+     * https://bugs.freedesktop.org/show_bug.cgi?id=74624 */
+    while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
+        pa_threaded_mainloop_wait(c->mainloop);
+    }
+
+    err = pa_stream_disconnect(stream);
+    if (err != 0) {
+        dolog("Failed to dissconnect! err=%d\n", err);
+    }
+    pa_stream_unref(stream);
+    pa_threaded_mainloop_unlock(c->mainloop);
+}
+
 static void qpa_fini_out (HWVoiceOut *hw)
 {
     void *ret;
@@ -689,7 +708,7 @@ static void qpa_fini_out (HWVoiceOut *hw)
     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 
     if (pa->stream) {
-        pa_stream_unref (pa->stream);
+        qpa_simple_disconnect(pa->g->conn, pa->stream);
         pa->stream = NULL;
     }
 
@@ -709,7 +728,7 @@ static void qpa_fini_in (HWVoiceIn *hw)
     audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
 
     if (pa->stream) {
-        pa_stream_unref (pa->stream);
+        qpa_simple_disconnect(pa->g->conn, pa->stream);
         pa->stream = NULL;
     }
 
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (15 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 16/25] paaudio: properly disconnect streams in fini_* Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-19 18:17   ` Gerd Hoffmann
  2015-08-20 20:36   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run Kővágó, Zoltán
                   ` (7 subsequent siblings)
  24 siblings, 2 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Currently the gcc specific version only evaluates the arguments once,
while the generic version evaluates one argument twice, which can cause
debugging headaches when an argument has a side effect.  This patch at
least provides consistent behavior between compilers.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.h | 14 --------------
 1 file changed, 14 deletions(-)

diff --git a/audio/audio.h b/audio/audio.h
index 68545b6..3a54e17 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -150,22 +150,8 @@ static inline void *advance (void *p, int incr)
     return (d + incr);
 }
 
-#ifdef __GNUC__
-#define audio_MIN(a, b) ( __extension__ ({      \
-    __typeof (a) ta = a;                        \
-    __typeof (b) tb = b;                        \
-    ((ta)>(tb)?(tb):(ta));                      \
-}))
-
-#define audio_MAX(a, b) ( __extension__ ({      \
-    __typeof (a) ta = a;                        \
-    __typeof (b) tb = b;                        \
-    ((ta)<(tb)?(tb):(ta));                      \
-}))
-#else
 #define audio_MIN(a, b) ((a)>(b)?(b):(a))
 #define audio_MAX(a, b) ((a)<(b)?(b):(a))
-#endif
 
 int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
                       int freq, int bits, int nchannels);
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (16 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 20:49   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches Kővágó, Zoltán
                   ` (6 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

audio_run is called manually by alsa and oss backends when polling.
In this case only the requesting backend should be run, not all of them.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/alsaaudio.c |  7 +++++--
 audio/audio.c     | 17 +++++++----------
 audio/audio_int.h |  2 +-
 audio/ossaudio.c  | 12 ++++++------
 4 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index cfe4aec..0750d0d 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -40,6 +40,7 @@ struct pollhlp {
     struct pollfd *pfds;
     int count;
     int mask;
+    AudioState *s;
 };
 
 typedef struct ALSAVoiceOut {
@@ -200,11 +201,11 @@ static void alsa_poll_handler (void *opaque)
         break;
 
     case SND_PCM_STATE_PREPARED:
-        audio_run ("alsa run (prepared)");
+        audio_run(hlp->s, "alsa run (prepared)");
         break;
 
     case SND_PCM_STATE_RUNNING:
-        audio_run ("alsa run (running)");
+        audio_run (hlp->s, "alsa run (running)");
         break;
 
     default:
@@ -758,6 +759,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
         return -1;
     }
 
+    alsa->pollhlp.s = hw->s;
     alsa->handle = handle;
     alsa->dev = dev;
     return 0;
@@ -859,6 +861,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
         return -1;
     }
 
+    alsa->pollhlp.s = hw->s;
     alsa->handle = handle;
     alsa->dev = dev;
     return 0;
diff --git a/audio/audio.c b/audio/audio.c
index 7468b94..e213deb 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -790,8 +790,9 @@ static void audio_reset_timer (AudioState *s)
 
 static void audio_timer (void *opaque)
 {
-    audio_run ("timer");
-    audio_reset_timer (opaque);
+    AudioState *s = opaque;
+    audio_run(s, "timer");
+    audio_reset_timer(s);
 }
 
 /*
@@ -1194,15 +1195,11 @@ static void audio_run_capture (AudioState *s)
     }
 }
 
-void audio_run (const char *msg)
+void audio_run(AudioState *s, const char *msg)
 {
-    AudioState *s;
-
-    QTAILQ_FOREACH(s, &audio_states, list) {
-        audio_run_out (s);
-        audio_run_in (s);
-        audio_run_capture (s);
-    }
+    audio_run_out(s);
+    audio_run_in(s);
+    audio_run_capture(s);
 
 #ifdef DEBUG_POLL
     {
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 1d81658..c52c656 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -220,7 +220,7 @@ int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
 int audio_bug (const char *funcname, int cond);
 void *audio_calloc (const char *funcname, int nmemb, size_t size);
 
-void audio_run (const char *msg);
+void audio_run(AudioState *s, const char *msg);
 
 #define VOICE_ENABLE 1
 #define VOICE_DISABLE 2
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index a5e7f7c..9eae769 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -112,28 +112,28 @@ static void oss_anal_close (int *fdp)
 
 static void oss_helper_poll_out (void *opaque)
 {
-    (void) opaque;
-    audio_run ("oss_poll_out");
+    AudioState *s = opaque;
+    audio_run(s, "oss_poll_out");
 }
 
 static void oss_helper_poll_in (void *opaque)
 {
-    (void) opaque;
-    audio_run ("oss_poll_in");
+    AudioState *s = opaque;
+    audio_run(s, "oss_poll_in");
 }
 
 static void oss_poll_out (HWVoiceOut *hw)
 {
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 
-    qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
+    qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
 }
 
 static void oss_poll_in (HWVoiceIn *hw)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 
-    qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
+    qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
 }
 
 static int oss_write (SWVoiceOut *sw, void *buf, int len)
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (17 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 22:08   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends Kővágó, Zoltán
                   ` (5 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Pulseaudio normally assumes that when the server wants it, the client
can generate the audio samples and send it right away.  Unfortunately
this is not the case with QEMU -- it's up to the emulated system when
does it generate the samples.  Buffering the samples and sending them
from a background thread is just a workaround, that doesn't work too
well.  Instead enable pa's compatibility support and let pa worry about
the details.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/paaudio.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index 2aee22f..3990a80 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -510,10 +510,8 @@ static pa_stream *qpa_simple_new (
 
     flags =
         PA_STREAM_INTERPOLATE_TIMING
-#ifdef PA_STREAM_ADJUST_LATENCY
-        |PA_STREAM_ADJUST_LATENCY
-#endif
-        |PA_STREAM_AUTO_TIMING_UPDATE;
+        |PA_STREAM_AUTO_TIMING_UPDATE
+        |PA_STREAM_EARLY_REQUESTS;
     if (dev) {
         /* don't move the stream if the user specified a sink/source */
         flags |= PA_STREAM_DONT_MOVE;
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (18 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-19 19:07   ` Gerd Hoffmann
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs Kővágó, Zoltán
                   ` (4 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Backends no longer have to deal with mixeng, they just receive a buffer
in the correct sample format, all mixeng logic is now in the audio.c
(and mixeng.c).  Backends also do not have to deal with soft voices.

Backends now have two way to read/write sound:
* write and read functions: similar to old read/write functions, except
  that they actually read/write the data to the backend instead of
  placing it into the mixeng buffer.  You no longer need run_in/run_out
  afterwards.
* get_buffer_out/put_buffer_out: the first function returns a buffer
  that can hold some audio data.  The caller fills this buffer (maybe
  partially) and calls put_buffer to actually write the data.  This way
  we can save copying the buffer in some cases (for example mmaped
  audio).  Similarly there's get_buffer_in/put_buffer_in for reading.

Backends only have to support one access method, but they can support
both if they have efficient implementation for both cases.

Please note that the read/write functions are not used directly in this
commit yet.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

---

There's one thing I'm not sure about: backends used to return in
hw->samples a buffer size, and mixeng allocated that large buffer for
itself.  The old code pretty much required that the mixeng buffer and
the destination buffer have the same size, but it's no longer required.
So maybe it would be a better idea to drop buffer_size_* functions (the
replacement of old hw->samples) and specify it as a (global) -audiodev
option.

Also please double check that coreaudio backend works.  I do not have a
mac to test.
---
 audio/Makefile.objs     |   1 -
 audio/alsaaudio.c       | 334 ++++++++++++---------------------------
 audio/audio.c           | 412 +++++++++++++++++++++++++++++++++---------------
 audio/audio.h           |   4 +-
 audio/audio_int.h       |  74 +++++----
 audio/audio_pt_int.c    | 173 --------------------
 audio/audio_pt_int.h    |  22 ---
 audio/audio_template.h  |  31 ++--
 audio/coreaudio.c       | 138 ++++++++--------
 audio/dsound_template.h |  53 ++++---
 audio/dsoundaudio.c     | 344 ++++++++++------------------------------
 audio/mixeng.h          |  11 +-
 audio/noaudio.c         |  54 ++-----
 audio/ossaudio.c        | 322 +++++++++++++++----------------------
 audio/paaudio.c         | 401 +++++++---------------------------------------
 audio/rate_template.h   |   2 +-
 audio/sdlaudio.c        | 118 ++++++--------
 audio/spiceaudio.c      | 134 ++++++----------
 audio/wavaudio.c        |  60 +------
 configure               |   5 -
 20 files changed, 938 insertions(+), 1755 deletions(-)
 delete mode 100644 audio/audio_pt_int.c
 delete mode 100644 audio/audio_pt_int.h

diff --git a/audio/Makefile.objs b/audio/Makefile.objs
index 9d8f579..c7216a7 100644
--- a/audio/Makefile.objs
+++ b/audio/Makefile.objs
@@ -6,7 +6,6 @@ common-obj-$(CONFIG_COREAUDIO) += coreaudio.o
 common-obj-$(CONFIG_ALSA) += alsaaudio.o
 common-obj-$(CONFIG_DSOUND) += dsoundaudio.o
 common-obj-$(CONFIG_PA) += paaudio.o
-common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
 common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
 common-obj-y += wavcapture.o
 
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 0750d0d..b7ab540 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -45,10 +45,8 @@ struct pollhlp {
 
 typedef struct ALSAVoiceOut {
     HWVoiceOut hw;
-    int wpos;
-    int pending;
-    void *pcm_buf;
     snd_pcm_t *handle;
+    size_t samples;
     struct pollhlp pollhlp;
     Audiodev *dev;
 } ALSAVoiceOut;
@@ -56,7 +54,7 @@ typedef struct ALSAVoiceOut {
 typedef struct ALSAVoiceIn {
     HWVoiceIn hw;
     snd_pcm_t *handle;
-    void *pcm_buf;
+    size_t samples;
     struct pollhlp pollhlp;
     Audiodev *dev;
 } ALSAVoiceIn;
@@ -271,11 +269,6 @@ static int alsa_poll_in (HWVoiceIn *hw)
     return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
 }
 
-static int alsa_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
 static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
 {
     switch (fmt) {
@@ -616,102 +609,62 @@ static int alsa_open(bool in, struct alsa_params_req *req,
     return -1;
 }
 
-static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
-{
-    snd_pcm_sframes_t avail;
-
-    avail = snd_pcm_avail_update (handle);
-    if (avail < 0) {
-        if (avail == -EPIPE) {
-            if (!alsa_recover (handle)) {
-                avail = snd_pcm_avail_update (handle);
-            }
-        }
-
-        if (avail < 0) {
-            alsa_logerr (avail,
-                         "Could not obtain number of available frames\n");
-            return -1;
-        }
-    }
-
-    return avail;
-}
-
-static void alsa_write_pending (ALSAVoiceOut *alsa)
-{
-    HWVoiceOut *hw = &alsa->hw;
-
-    while (alsa->pending) {
-        int left_till_end_samples = hw->samples - alsa->wpos;
-        int len = audio_MIN (alsa->pending, left_till_end_samples);
-        char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
-
-        while (len) {
-            snd_pcm_sframes_t written;
-
-            written = snd_pcm_writei (alsa->handle, src, len);
-
-            if (written <= 0) {
-                switch (written) {
-                case 0:
-                    trace_alsa_wrote_zero(len);
-                    return;
-
-                case -EPIPE:
-                    if (alsa_recover (alsa->handle)) {
-                        alsa_logerr (written, "Failed to write %d frames\n",
-                                     len);
-                        return;
-                    }
-                    trace_alsa_xrun_out();
-                    continue;
-
-                case -ESTRPIPE:
-                    /* stream is suspended and waiting for an
-                       application recovery */
-                    if (alsa_resume (alsa->handle)) {
-                        alsa_logerr (written, "Failed to write %d frames\n",
-                                     len);
-                        return;
-                    }
-                    trace_alsa_resume_out();
-                    continue;
-
-                case -EAGAIN:
-                    return;
-
-                default:
-                    alsa_logerr (written, "Failed to write %d frames from %p\n",
-                                 len, src);
-                    return;
-                }
-            }
-
-            alsa->wpos = (alsa->wpos + written) % hw->samples;
-            alsa->pending -= written;
-            len -= written;
-        }
-    }
-}
-
-static int alsa_run_out (HWVoiceOut *hw, int live)
+static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
 {
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
-    int decr;
-    snd_pcm_sframes_t avail;
+    size_t pos = 0;
+    size_t len_frames = len >> hw->info.shift;
 
-    avail = alsa_get_avail (alsa->handle);
-    if (avail < 0) {
-        dolog ("Could not get number of available playback frames\n");
-        return 0;
+    while (len_frames) {
+        char *src = advance(buf, pos);
+        snd_pcm_sframes_t written;
+
+        written = snd_pcm_writei(alsa->handle, src, len_frames);
+
+        if (written <= 0) {
+            switch (written) {
+            case 0:
+                trace_alsa_wrote_zero(len_frames);
+                return pos;
+
+            case -EPIPE:
+                if (alsa_recover(alsa->handle)) {
+                    alsa_logerr(written, "Failed to write %zu frames\n",
+                                len_frames);
+                    return pos;
+                }
+                trace_alsa_xrun_out();
+                continue;
+
+            case -ESTRPIPE:
+                /* stream is suspended and waiting for an
+                   application recovery */
+                if (alsa_resume(alsa->handle)) {
+                    alsa_logerr(written, "Failed to write %zu frames\n",
+                                len_frames);
+                    return pos;
+                }
+                trace_alsa_resume_out();
+                continue;
+
+            case -EAGAIN:
+                return pos;
+
+            default:
+                alsa_logerr(written, "Failed to write %zu frames from %p\n",
+                            len, src);
+                return pos;
+            }
+        }
+
+        pos += written << hw->info.shift;
+        if (written < len_frames) {
+            break;
+        }
+        len_frames -= written;
     }
 
-    decr = audio_MIN (live, avail);
-    decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
-    alsa->pending += decr;
-    alsa_write_pending (alsa);
-    return decr;
+    return pos;
 }
 
 static void alsa_fini_out (HWVoiceOut *hw)
@@ -720,9 +673,6 @@ static void alsa_fini_out (HWVoiceOut *hw)
 
     ldebug ("alsa_fini\n");
     alsa_anal_close (&alsa->handle, &alsa->pollhlp);
-
-    g_free(alsa->pcm_buf);
-    alsa->pcm_buf = NULL;
 }
 
 static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
@@ -749,15 +699,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
     obt_as.endianness = obt.endianness;
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = obt.samples;
-
-    alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
-    if (!alsa->pcm_buf) {
-        dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
-               hw->samples, 1 << hw->info.shift);
-        alsa_anal_close1 (&handle);
-        return -1;
-    }
+    alsa->samples = obt.samples;
 
     alsa->pollhlp.s = hw->s;
     alsa->handle = handle;
@@ -765,6 +707,12 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
     return 0;
 }
 
+static size_t alsa_buffer_size_out(HWVoiceOut *hw)
+{
+    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+    return alsa->samples;
+}
+
 #define VOICE_CTL_PAUSE 0
 #define VOICE_CTL_PREPARE 1
 #define VOICE_CTL_START 2
@@ -851,15 +799,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     obt_as.endianness = obt.endianness;
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    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 samples, each %d bytes)\n",
-               hw->samples, 1 << hw->info.shift);
-        alsa_anal_close1 (&handle);
-        return -1;
-    }
+    alsa->samples = obt.samples;
 
     alsa->pollhlp.s = hw->s;
     alsa->handle = handle;
@@ -867,139 +807,59 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     return 0;
 }
 
+static size_t alsa_buffer_size_in(HWVoiceIn *hw)
+{
+    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+    return alsa->samples;
+}
+
 static void alsa_fini_in (HWVoiceIn *hw)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
 
     alsa_anal_close (&alsa->handle, &alsa->pollhlp);
-
-    g_free(alsa->pcm_buf);
-    alsa->pcm_buf = NULL;
 }
 
-static int alsa_run_in (HWVoiceIn *hw)
+static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
-    int hwshift = hw->info.shift;
-    int i;
-    int live = audio_pcm_hw_get_live_in (hw);
-    int dead = hw->samples - live;
-    int decr;
-    struct {
-        int add;
-        int len;
-    } bufs[2] = {
-        { .add = hw->wpos, .len = 0 },
-        { .add = 0,        .len = 0 }
-    };
-    snd_pcm_sframes_t avail;
-    snd_pcm_uframes_t read_samples = 0;
+    size_t pos = 0;
 
-    if (!dead) {
-        return 0;
-    }
-
-    avail = alsa_get_avail (alsa->handle);
-    if (avail < 0) {
-        dolog ("Could not get number of captured frames\n");
-        return 0;
-    }
-
-    if (!avail) {
-        snd_pcm_state_t state;
-
-        state = snd_pcm_state (alsa->handle);
-        switch (state) {
-        case SND_PCM_STATE_PREPARED:
-            avail = hw->samples;
-            break;
-        case SND_PCM_STATE_SUSPENDED:
-            /* stream is suspended and waiting for an application recovery */
-            if (alsa_resume (alsa->handle)) {
-                dolog ("Failed to resume suspended input stream\n");
-                return 0;
-            }
-            trace_alsa_resume_in();
-            break;
-        default:
-            trace_alsa_no_frames(state);
-            return 0;
-        }
-    }
-
-    decr = audio_MIN (dead, avail);
-    if (!decr) {
-        return 0;
-    }
-
-    if (hw->wpos + decr > hw->samples) {
-        bufs[0].len = (hw->samples - hw->wpos);
-        bufs[1].len = (decr - (hw->samples - hw->wpos));
-    }
-    else {
-        bufs[0].len = decr;
-    }
-
-    for (i = 0; i < 2; ++i) {
-        void *src;
-        struct st_sample *dst;
+    while (len) {
+        void *dst = advance(buf, pos);
         snd_pcm_sframes_t nread;
-        snd_pcm_uframes_t len;
 
-        len = bufs[i].len;
+        nread = snd_pcm_readi(alsa->handle, dst, len >> hw->info.shift);
 
-        src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
-        dst = hw->conv_buf + bufs[i].add;
+        if (nread <= 0) {
+            switch (nread) {
+            case 0:
+                trace_alsa_read_zero(len);
+                return pos;;
 
-        while (len) {
-            nread = snd_pcm_readi (alsa->handle, src, len);
-
-            if (nread <= 0) {
-                switch (nread) {
-                case 0:
-                    trace_alsa_read_zero(len);
-                    goto exit;
-
-                case -EPIPE:
-                    if (alsa_recover (alsa->handle)) {
-                        alsa_logerr (nread, "Failed to read %ld frames\n", len);
-                        goto exit;
-                    }
-                    trace_alsa_xrun_in();
-                    continue;
-
-                case -EAGAIN:
-                    goto exit;
-
-                default:
-                    alsa_logerr (
-                        nread,
-                        "Failed to read %ld frames from %p\n",
-                        len,
-                        src
-                        );
-                    goto exit;
+            case -EPIPE:
+                if (alsa_recover(alsa->handle)) {
+                    alsa_logerr(nread, "Failed to read %zu frames\n", len);
+                    return pos;
                 }
+                trace_alsa_xrun_in();
+                continue;
+
+            case -EAGAIN:
+                return pos;
+
+            default:
+                alsa_logerr(nread, "Failed to read %zu frames to %p\n",
+                            len, dst);
+                return pos;;
             }
-
-            hw->conv (dst, src, nread);
-
-            src = advance (src, nread << hwshift);
-            dst += nread;
-
-            read_samples += nread;
-            len -= nread;
         }
+
+        pos += nread << hw->info.shift;
+        len -= nread << hw->info.shift;
     }
 
- exit:
-    hw->wpos = (hw->wpos + read_samples) % hw->samples;
-    return read_samples;
-}
-
-static int alsa_read (SWVoiceIn *sw, void *buf, int size)
-{
-    return audio_pcm_sw_read (sw, buf, size);
+    return pos;
 }
 
 static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
@@ -1065,13 +925,13 @@ static void alsa_audio_fini (void *opaque)
 static struct audio_pcm_ops alsa_pcm_ops = {
     .init_out = alsa_init_out,
     .fini_out = alsa_fini_out,
-    .run_out  = alsa_run_out,
+    .buffer_size_out = alsa_buffer_size_out,
     .write    = alsa_write,
     .ctl_out  = alsa_ctl_out,
 
     .init_in  = alsa_init_in,
     .fini_in  = alsa_fini_in,
-    .run_in   = alsa_run_in,
+    .buffer_size_in = alsa_buffer_size_in,
     .read     = alsa_read,
     .ctl_in   = alsa_ctl_in,
 };
diff --git a/audio/audio.c b/audio/audio.c
index e213deb..6ef40e9 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -499,10 +499,10 @@ static int audio_attach_capture (HWVoiceOut *hw)
 /*
  * Hard voice (capture)
  */
-static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
+static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
 {
     SWVoiceIn *sw;
-    int m = hw->total_samples_captured;
+    size_t m = hw->total_samples_captured;
 
     for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
         if (sw->active) {
@@ -512,72 +512,70 @@ static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
     return m;
 }
 
-int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
+static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
 {
-    int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
-    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
-        dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+    size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
+    if (audio_bug (AUDIO_FUNC, live > hw->conv_buf->size)) {
+        dolog("live=%zu samples=%zu\n", live, hw->conv_buf->size);
         return 0;
     }
     return live;
 }
 
-int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
-                           int live, int pending)
+static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
+                                  size_t len)
 {
-    int left = hw->samples - pending;
-    int len = audio_MIN (left, live);
-    int clipped = 0;
+    size_t clipped = 0;
+    size_t pos = hw->mix_buf->pos;
 
     while (len) {
-        struct st_sample *src = hw->mix_buf + hw->rpos;
-        uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
-        int samples_till_end_of_buf = hw->samples - hw->rpos;
-        int samples_to_clip = audio_MIN (len, samples_till_end_of_buf);
+        st_sample *src = hw->mix_buf->samples + pos;
+        uint8_t *dst = advance (pcm_buf, clipped << hw->info.shift);
+        size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
+        size_t samples_to_clip = audio_MIN(len, samples_till_end_of_buf);
 
         hw->clip (dst, src, samples_to_clip);
 
-        hw->rpos = (hw->rpos + samples_to_clip) % hw->samples;
+        pos = (pos + samples_to_clip) % hw->mix_buf->size;
         len -= samples_to_clip;
         clipped += samples_to_clip;
     }
-    return clipped;
 }
 
 /*
  * Soft voice (capture)
  */
-static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
+static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
 {
     HWVoiceIn *hw = sw->hw;
-    int live = hw->total_samples_captured - sw->total_hw_samples_acquired;
-    int rpos;
+    ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+    ssize_t rpos;
 
-    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
-        dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+    if (audio_bug(AUDIO_FUNC, live < 0 || live > hw->conv_buf->size)) {
+        dolog("live=%zd samples=%zu\n", live, hw->conv_buf->size);
         return 0;
     }
 
-    rpos = hw->wpos - live;
+    rpos = hw->conv_buf->pos - live;
     if (rpos >= 0) {
         return rpos;
     }
     else {
-        return hw->samples + rpos;
+        return hw->conv_buf->size + rpos;
     }
 }
 
-int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
+static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
 {
     HWVoiceIn *hw = sw->hw;
-    int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
+    size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
     struct st_sample *src, *dst = sw->buf;
 
-    rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
+    rpos = audio_pcm_sw_get_rpos_in (sw) % hw->conv_buf->size;
 
     live = hw->total_samples_captured - sw->total_hw_samples_acquired;
-    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
-        dolog ("live_in=%d hw->samples=%d\n", live, hw->samples);
+    if (audio_bug (AUDIO_FUNC, live > hw->conv_buf->size)) {
+        dolog("live_in=%zu samples=%zu\n", live, hw->conv_buf->size);
         return 0;
     }
 
@@ -590,11 +588,11 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
     swlim = audio_MIN (swlim, samples);
 
     while (swlim) {
-        src = hw->conv_buf + rpos;
-        isamp = hw->wpos - rpos;
-        /* XXX: <= ? */
-        if (isamp <= 0) {
-            isamp = hw->samples - rpos;
+        src = hw->conv_buf->samples + rpos;
+        if (hw->conv_buf->pos > rpos) {
+            isamp = hw->conv_buf->pos - rpos;
+        } else {
+            isamp = hw->conv_buf->size - rpos;
         }
 
         if (!isamp) {
@@ -602,14 +600,9 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
         }
         osamp = swlim;
 
-        if (audio_bug (AUDIO_FUNC, osamp < 0)) {
-            dolog ("osamp=%d\n", osamp);
-            return 0;
-        }
-
         st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
         swlim -= osamp;
-        rpos = (rpos + isamp) % hw->samples;
+        rpos = (rpos + isamp) % hw->conv_buf->size;
         dst += osamp;
         ret += osamp;
         total += isamp;
@@ -627,10 +620,10 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
 /*
  * Hard voice (playback)
  */
-static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
+static size_t audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
 {
     SWVoiceOut *sw;
-    int m = INT_MAX;
+    size_t m = SIZE_MAX;
     int nb_live = 0;
 
     for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
@@ -644,9 +637,9 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
     return m;
 }
 
-static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
+static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
 {
-    int smin;
+    size_t smin;
     int nb_live1;
 
     smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
@@ -655,10 +648,10 @@ static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
     }
 
     if (nb_live1) {
-        int live = smin;
+        size_t live = smin;
 
-        if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
-            dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+        if (audio_bug(AUDIO_FUNC, live > hw->mix_buf->size)) {
+            dolog("live=%zu hw->samples=%zu\n", live, hw->mix_buf->size);
             return 0;
         }
         return live;
@@ -669,20 +662,21 @@ static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
 /*
  * Soft voice (playback)
  */
-int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
+static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
 {
-    int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
-    int ret = 0, pos = 0, total = 0;
+    size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
+    int ret = 0;
+    size_t pos = 0, total = 0;
 
     if (!sw) {
         return size;
     }
 
-    hwsamples = sw->hw->samples;
+    hwsamples = sw->hw->mix_buf->size;
 
     live = sw->total_hw_samples_mixed;
-    if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){
-        dolog ("live=%d hw->samples=%d\n", live, hwsamples);
+    if (audio_bug (AUDIO_FUNC, live > hwsamples)){
+        dolog("live=%zu samples=%zu\n", live, hwsamples);
         return 0;
     }
 
@@ -693,7 +687,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
         return 0;
     }
 
-    wpos = (sw->hw->rpos + live) % hwsamples;
+    wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
     samples = size >> sw->info.shift;
 
     dead = hwsamples - live;
@@ -719,7 +713,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
         st_rate_flow_mix (
             sw->rate,
             sw->buf + pos,
-            sw->hw->mix_buf + wpos,
+            sw->hw->mix_buf->samples + wpos,
             &isamp,
             &osamp
             );
@@ -798,10 +792,8 @@ static void audio_timer (void *opaque)
 /*
  * Public API
  */
-int AUD_write (SWVoiceOut *sw, void *buf, int size)
+size_t AUD_write(SWVoiceOut *sw, void *buf, size_t size)
 {
-    int bytes;
-
     if (!sw) {
         /* XXX: Consider options */
         return size;
@@ -812,14 +804,11 @@ int AUD_write (SWVoiceOut *sw, void *buf, int size)
         return 0;
     }
 
-    bytes = sw->hw->pcm_ops->write (sw, buf, size);
-    return bytes;
+    return audio_pcm_sw_write(sw, buf, size);
 }
 
-int AUD_read (SWVoiceIn *sw, void *buf, int size)
+size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
 {
-    int bytes;
-
     if (!sw) {
         /* XXX: Consider options */
         return size;
@@ -830,13 +819,12 @@ int AUD_read (SWVoiceIn *sw, void *buf, int size)
         return 0;
     }
 
-    bytes = sw->hw->pcm_ops->read (sw, buf, size);
-    return bytes;
+    return audio_pcm_sw_read(sw, buf, size);
 }
 
 int AUD_get_buffer_size_out (SWVoiceOut *sw)
 {
-    return sw->hw->samples << sw->hw->info.shift;
+    return sw->hw->mix_buf->size << sw->hw->info.shift;
 }
 
 void AUD_set_active_out (SWVoiceOut *sw, int on)
@@ -928,17 +916,17 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
     }
 }
 
-static int audio_get_avail (SWVoiceIn *sw)
+static size_t audio_get_avail (SWVoiceIn *sw)
 {
-    int live;
+    size_t live;
 
     if (!sw) {
         return 0;
     }
 
     live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
-    if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
-        dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+    if (audio_bug (AUDIO_FUNC, live > sw->hw->conv_buf->size)) {
+        dolog("live=%zu samples=%zu\n", live, sw->hw->conv_buf->size);
         return 0;
     }
 
@@ -951,9 +939,9 @@ static int audio_get_avail (SWVoiceIn *sw)
     return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
 }
 
-static int audio_get_free (SWVoiceOut *sw)
+static size_t audio_get_free(SWVoiceOut *sw)
 {
-    int live, dead;
+    size_t live, dead;
 
     if (!sw) {
         return 0;
@@ -961,12 +949,12 @@ static int audio_get_free (SWVoiceOut *sw)
 
     live = sw->total_hw_samples_mixed;
 
-    if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
-        dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+    if (audio_bug (AUDIO_FUNC, live > sw->hw->mix_buf->size)) {
+        dolog("live=%zu samples=%zu\n", live, sw->hw->mix_buf->size);
         return 0;
     }
 
-    dead = sw->hw->samples - live;
+    dead = sw->hw->mix_buf->size - live;
 
 #ifdef DEBUG_OUT
     dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
@@ -977,9 +965,10 @@ static int audio_get_free (SWVoiceOut *sw)
     return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
 }
 
-static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
+static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
+                                        size_t samples)
 {
-    int n;
+    size_t n;
 
     if (hw->enabled) {
         SWVoiceCap *sc;
@@ -990,28 +979,53 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
 
             n = samples;
             while (n) {
-                int till_end_of_hw = hw->samples - rpos2;
-                int to_write = audio_MIN (till_end_of_hw, n);
-                int bytes = to_write << hw->info.shift;
-                int written;
+                size_t till_end_of_hw = hw->mix_buf->size - rpos2;
+                size_t to_write = audio_MIN (till_end_of_hw, n);
+                size_t bytes = to_write << hw->info.shift;
+                size_t written;
 
-                sw->buf = hw->mix_buf + rpos2;
+                sw->buf = hw->mix_buf->samples + rpos2;
                 written = audio_pcm_sw_write (sw, NULL, bytes);
                 if (written - bytes) {
-                    dolog ("Could not mix %d bytes into a capture "
-                           "buffer, mixed %d\n",
-                           bytes, written);
+                    dolog("Could not mix %zu bytes into a capture "
+                          "buffer, mixed %zu\n",
+                          bytes, written);
                     break;
                 }
                 n -= to_write;
-                rpos2 = (rpos2 + to_write) % hw->samples;
+                rpos2 = (rpos2 + to_write) % hw->mix_buf->size;
             }
         }
     }
 
-    n = audio_MIN (samples, hw->samples - rpos);
-    mixeng_clear (hw->mix_buf + rpos, n);
-    mixeng_clear (hw->mix_buf, samples - n);
+    n = audio_MIN(samples, hw->mix_buf->size - rpos);
+    mixeng_clear(hw->mix_buf->samples + rpos, n);
+    mixeng_clear(hw->mix_buf->samples, samples - n);
+}
+
+static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
+{
+    size_t clipped = 0;
+
+    while (live) {
+        size_t size, decr, proc;
+        void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
+
+        decr = audio_MIN(size >> hw->info.shift, live);
+        audio_pcm_hw_clip_out(hw, buf, decr);
+        proc = hw->pcm_ops->put_buffer_out(hw, buf, decr << hw->info.shift) >>
+            hw->info.shift;
+
+        live -= proc;
+        clipped += proc;
+        hw->mix_buf->pos = (hw->mix_buf->pos + proc) % hw->mix_buf->size;
+
+        if (proc == 0 || proc < decr) {
+            break;
+        }
+    }
+
+    return clipped;
 }
 
 static void audio_run_out (AudioState *s)
@@ -1020,16 +1034,16 @@ static void audio_run_out (AudioState *s)
     SWVoiceOut *sw;
 
     while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
-        int played;
-        int live, free, nb_live, cleanup_required, prev_rpos;
+        size_t played, live, prev_rpos, free;
+        int nb_live, cleanup_required;
 
         live = audio_pcm_hw_get_live_out (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);
+        if (audio_bug(AUDIO_FUNC, live > hw->mix_buf->size)) {
+            dolog("live=%zu samples=%zu\n", live, hw->mix_buf->size);
             continue;
         }
 
@@ -1060,16 +1074,16 @@ static void audio_run_out (AudioState *s)
             continue;
         }
 
-        prev_rpos = hw->rpos;
-        played = hw->pcm_ops->run_out (hw, live);
-        if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
-            dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
-                   hw->rpos, hw->samples, played);
-            hw->rpos = 0;
+        prev_rpos = hw->mix_buf->pos;
+        played = audio_pcm_hw_run_out(hw, live);
+        if (audio_bug(AUDIO_FUNC, hw->mix_buf->pos >= hw->mix_buf->size)) {
+            dolog("rpos=%zu samples=%zu played=%zu\n",
+                  hw->mix_buf->pos, hw->mix_buf->size, played);
+            hw->mix_buf->pos = 0;
         }
 
 #ifdef DEBUG_OUT
-        dolog ("played=%d\n", played);
+        dolog ("played=%zu\n", played);
 #endif
 
         if (played) {
@@ -1084,7 +1098,7 @@ static void audio_run_out (AudioState *s)
             }
 
             if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) {
-                dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
+                dolog ("played=%zu sw->total_hw_samples_mixed=%zu\n",
                        played, sw->total_hw_samples_mixed);
                 played = sw->total_hw_samples_mixed;
             }
@@ -1119,15 +1133,46 @@ static void audio_run_out (AudioState *s)
     }
 }
 
+static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
+{
+    size_t conv = 0;
+    STSampleBuffer *conv_buf = hw->conv_buf;
+
+    while (samples) {
+        size_t proc;
+        size_t size = samples * hw->info.bytes_per_frame;
+        void *buf = hw->pcm_ops->get_buffer_in(hw, &size);
+
+        assert(size % hw->info.bytes_per_frame == 0);
+        if (size == 0) {
+            hw->pcm_ops->put_buffer_in(hw, buf, size);
+            break;
+        }
+
+        proc = audio_MIN(size / hw->info.bytes_per_frame,
+                         conv_buf->size - conv_buf->pos);
+
+        hw->conv(conv_buf->samples + conv_buf->pos, buf, proc);
+        conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
+
+        samples -= proc;
+        conv += proc;
+        hw->pcm_ops->put_buffer_in(hw, buf, proc * hw->info.bytes_per_frame);
+    }
+
+    return conv;
+}
+
 static void audio_run_in (AudioState *s)
 {
     HWVoiceIn *hw = NULL;
 
     while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
         SWVoiceIn *sw;
-        int captured, min;
+        size_t captured, min;
 
-        captured = hw->pcm_ops->run_in (hw);
+        captured = audio_pcm_hw_run_in(
+            hw, hw->conv_buf->size -  audio_pcm_hw_get_live_in(hw));
 
         min = audio_pcm_hw_find_min_in (hw);
         hw->total_samples_captured += captured - min;
@@ -1137,7 +1182,7 @@ static void audio_run_in (AudioState *s)
             sw->total_hw_samples_acquired -= min;
 
             if (sw->active) {
-                int avail;
+                size_t avail;
 
                 avail = audio_get_avail (sw);
                 if (avail > 0) {
@@ -1153,19 +1198,19 @@ static void audio_run_capture (AudioState *s)
     CaptureVoiceOut *cap;
 
     for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
-        int live, rpos, captured;
+        size_t live, rpos, captured;
         HWVoiceOut *hw = &cap->hw;
         SWVoiceOut *sw;
 
         captured = live = audio_pcm_hw_get_live_out (hw, NULL);
-        rpos = hw->rpos;
+        rpos = hw->mix_buf->pos;
         while (live) {
-            int left = hw->samples - rpos;
-            int to_capture = audio_MIN (live, left);
+            size_t left = hw->mix_buf->size - rpos;
+            size_t to_capture = audio_MIN (live, left);
             struct st_sample *src;
             struct capture_callback *cb;
 
-            src = hw->mix_buf + rpos;
+            src = hw->mix_buf->samples + rpos;
             hw->clip (cap->buf, src, to_capture);
             mixeng_clear (src, to_capture);
 
@@ -1173,10 +1218,10 @@ static void audio_run_capture (AudioState *s)
                 cb->ops.capture (cb->opaque, cap->buf,
                                  to_capture << hw->info.shift);
             }
-            rpos = (rpos + to_capture) % hw->samples;
+            rpos = (rpos + to_capture) % hw->mix_buf->size;
             live -= to_capture;
         }
-        hw->rpos = rpos;
+        hw->mix_buf->pos = rpos;
 
         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
             if (!sw->active && sw->empty) {
@@ -1184,8 +1229,8 @@ static void audio_run_capture (AudioState *s)
             }
 
             if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
-                dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
-                       captured, sw->total_hw_samples_mixed);
+                dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n",
+                      captured, sw->total_hw_samples_mixed);
                 captured = sw->total_hw_samples_mixed;
             }
 
@@ -1219,12 +1264,135 @@ void audio_run(AudioState *s, const char *msg)
 #endif
 }
 
+void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
+{
+    ssize_t start;
+
+    if (unlikely(!hw->buf_emul)) {
+        size_t calc_size = hw->conv_buf->size << hw->info.shift;
+        hw->buf_emul = g_malloc(calc_size);
+        hw->size_emul = calc_size;
+        hw->pos_emul = hw->pending_emul = 0;
+    }
+
+    while (hw->pending_emul < hw->size_emul) {
+        size_t read_len = audio_MIN(hw->size_emul - hw->pos_emul,
+                                    hw->size_emul - hw->pending_emul);
+        size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,
+                                        read_len);
+        hw->pending_emul += read;
+        if (read < read_len) {
+            break;
+        }
+    }
+
+    start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+    if (start < 0) {
+        start += hw->size_emul;
+    }
+    assert(start >= 0 && start < hw->size_emul);
+
+    *size = audio_MIN(hw->pending_emul, hw->size_emul - start);
+    return hw->buf_emul + start;
+}
+
+void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
+{
+    assert(size <= hw->pending_emul);
+    hw->pending_emul -= size;
+}
+
+void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+    if (unlikely(!hw->buf_emul)) {
+        size_t calc_size = hw->mix_buf->size << hw->info.shift;
+
+        hw->buf_emul = g_malloc(calc_size);
+        hw->size_emul = calc_size;
+        hw->pos_emul = hw->pending_emul = 0;
+    }
+
+    *size = audio_MIN(hw->size_emul - hw->pending_emul,
+                      hw->size_emul - hw->pos_emul);
+    return hw->buf_emul + hw->pos_emul;
+}
+
+size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
+                                            size_t size)
+{
+    assert(buf == hw->buf_emul + hw->pos_emul &&
+           size + hw->pending_emul <= hw->size_emul);
+
+    hw->pending_emul += size;
+    hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
+
+    return size;
+}
+
+size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
+{
+    audio_generic_put_buffer_out_nowrite(hw, buf, size);
+
+    while (hw->pending_emul) {
+        size_t write_len, written;
+        ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+        if (start < 0) {
+            start += hw->size_emul;
+        }
+        assert(start >= 0 && start < hw->size_emul);
+
+        write_len = audio_MIN(hw->pending_emul, hw->size_emul - start);
+
+        written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
+        hw->pending_emul -= written;
+
+        if (written < write_len) {
+            break;
+        }
+    }
+
+    /* fake we have written everything. non-written data remain in pending_emul,
+     * so we do not have to clip them multiple times */
+    return size;
+}
+
+size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
+{
+    size_t dst_size, copy_size;
+    void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);
+    copy_size = audio_MIN(size, dst_size);
+
+    memcpy(dst, buf, copy_size);
+    return hw->pcm_ops->put_buffer_out(hw, buf, copy_size);
+}
+
+size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
+{
+    size_t dst_size, copy_size;
+    void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size);
+    copy_size = audio_MIN(size, dst_size);
+
+    memcpy(dst, buf, copy_size);
+    hw->pcm_ops->put_buffer_in(hw, buf, copy_size);
+    return copy_size;
+}
+
+
 static int audio_driver_init(AudioState *s, struct audio_driver *drv,
                              Audiodev *dev)
 {
     s->drv_opaque = drv->init(dev);
 
     if (s->drv_opaque) {
+        if (!drv->pcm_ops->get_buffer_in) {
+            drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
+            drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
+        }
+        if (!drv->pcm_ops->get_buffer_out) {
+            drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
+            drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
+        }
+
         audio_init_nb_voices_out(s, drv);
         audio_init_nb_voices_in(s, drv);
         s->drv = drv;
@@ -1511,23 +1679,16 @@ CaptureVoiceOut *AUD_add_capture(
         QLIST_INIT (&hw->sw_head);
         QLIST_INIT (&cap->cb_head);
 
-        /* XXX find a more elegant way */
-        hw->samples = 4096 * 4;
-        hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
-                                    sizeof (struct st_sample));
-        if (!hw->mix_buf) {
-            dolog ("Could not allocate capture mix buffer (%d samples)\n",
-                   hw->samples);
-            goto err2;
-        }
+        audio_pcm_hw_alloc_resources_out(hw);
 
         audio_pcm_init_info (&hw->info, as);
 
-        cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+        cap->buf = audio_calloc(AUDIO_FUNC, hw->mix_buf->size,
+                                1 << hw->info.shift);
         if (!cap->buf) {
             dolog ("Could not allocate capture buffer "
-                   "(%d samples, each %d bytes)\n",
-                   hw->samples, 1 << hw->info.shift);
+                   "(%zu samples, each %d bytes)\n",
+                   hw->mix_buf->size, 1 << hw->info.shift);
             goto err3;
         }
 
@@ -1548,7 +1709,6 @@ CaptureVoiceOut *AUD_add_capture(
 
     err3:
         g_free (cap->hw.mix_buf);
-    err2:
         g_free (cap);
     err1:
         g_free (cb);
diff --git a/audio/audio.h b/audio/audio.h
index 3a54e17..2e3d0db 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -116,7 +116,7 @@ SWVoiceOut *AUD_open_out (
     );
 
 void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
-int  AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
+size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t 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);
@@ -137,7 +137,7 @@ SWVoiceIn *AUD_open_in (
     );
 
 void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
-int  AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
+size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
 void AUD_set_active_in (SWVoiceIn *sw, int on);
 int  AUD_is_active_in (SWVoiceIn *sw);
 
diff --git a/audio/audio_int.h b/audio/audio_int.h
index c52c656..1250012 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -50,6 +50,11 @@ struct audio_pcm_info {
 
 typedef struct SWVoiceCap SWVoiceCap;
 
+typedef struct STSampleBuffer {
+    size_t pos, size;
+    st_sample samples[];
+} STSampleBuffer;
+
 typedef struct HWVoiceOut {
     AudioState *s;
     int enabled;
@@ -58,13 +63,12 @@ typedef struct HWVoiceOut {
     struct audio_pcm_info info;
 
     f_sample *clip;
-
-    int rpos;
     uint64_t ts_helper;
 
-    struct st_sample *mix_buf;
+    STSampleBuffer *mix_buf;
+    void *buf_emul;
+    size_t pos_emul, pending_emul, size_emul;
 
-    int samples;
     QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
     QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
     int ctl_caps;
@@ -80,13 +84,13 @@ typedef struct HWVoiceIn {
 
     t_sample *conv;
 
-    int wpos;
-    int total_samples_captured;
+    size_t total_samples_captured;
     uint64_t ts_helper;
 
-    struct st_sample *conv_buf;
+    STSampleBuffer *conv_buf;
+    void *buf_emul;
+    size_t pos_emul, pending_emul, size_emul;
 
-    int samples;
     QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
     int ctl_caps;
     struct audio_pcm_ops *pcm_ops;
@@ -101,7 +105,7 @@ struct SWVoiceOut {
     int64_t ratio;
     struct st_sample *buf;
     void *rate;
-    int total_hw_samples_mixed;
+    size_t total_hw_samples_mixed;
     int active;
     int empty;
     HWVoiceOut *hw;
@@ -118,7 +122,7 @@ struct SWVoiceIn {
     struct audio_pcm_info info;
     int64_t ratio;
     void *rate;
-    int total_hw_samples_acquired;
+    size_t total_hw_samples_acquired;
     struct st_sample *buf;
     f_sample *clip;
     HWVoiceIn *hw;
@@ -143,19 +147,39 @@ struct audio_driver {
 };
 
 struct audio_pcm_ops {
-    int  (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
-    void (*fini_out)(HWVoiceOut *hw);
-    int  (*run_out) (HWVoiceOut *hw, int live);
-    int  (*write)   (SWVoiceOut *sw, void *buf, int size);
-    int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
+    int    (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
+    void   (*fini_out)(HWVoiceOut *hw);
+    size_t (*write)   (HWVoiceOut *hw, void *buf, size_t size);
+    /* get the optimal buffer size in samples; optional */
+    size_t (*buffer_size_out)(HWVoiceOut *hw);
+    /* get a buffer that after later can be passed to put_buffer_out; optional
+     * returns the buffer, and writes it's size to size (in bytes)
+     * this is unrelated to the above buffer_size_out function */
+    void  *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
+    /* put back the buffer returned by get_buffer_out; optional
+     * buf must be equal the pointer returned by get_buffer_out,
+     * size may be smaller */
+    size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
+    int    (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
 
-    int  (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
-    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, ...);
+    int    (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
+    void   (*fini_in) (HWVoiceIn *hw);
+    size_t (*read)    (HWVoiceIn *hw, void *buf, size_t size);
+    size_t (*buffer_size_in)(HWVoiceIn *hw);
+    void  *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
+    void   (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
+    int    (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
 };
 
+void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
+void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
+void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
+size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
+size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
+                                            size_t size);
+size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
+size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
+
 struct capture_callback {
     struct audio_capture_ops ops;
     void *opaque;
@@ -209,14 +233,6 @@ extern struct audio_driver *drvtab[];
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
 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);
-int  audio_pcm_hw_get_live_in (HWVoiceIn *hw);
-
-int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
-
-int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
-                           int live, int pending);
-
 int audio_bug (const char *funcname, int cond);
 void *audio_calloc (const char *funcname, int nmemb, size_t size);
 
@@ -228,7 +244,7 @@ void audio_run(AudioState *s, const char *msg);
 
 #define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
 
-static inline int audio_ring_dist (int dst, int src, int len)
+static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
 {
     return (dst >= src) ? (dst - src) : (len - src + dst);
 }
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
deleted file mode 100644
index 9a9c306..0000000
--- a/audio/audio_pt_int.c
+++ /dev/null
@@ -1,173 +0,0 @@
-#include "qemu-common.h"
-#include "audio.h"
-
-#define AUDIO_CAP "audio-pt"
-
-#include "audio_int.h"
-#include "audio_pt_int.h"
-
-static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
-                                       const char *fmt, ...)
-{
-    va_list ap;
-
-    va_start (ap, fmt);
-    AUD_vlog (pt->drv, fmt, ap);
-    va_end (ap);
-
-    AUD_log (NULL, "\n");
-    AUD_log (pt->drv, "Reason: %s\n", strerror (err));
-}
-
-int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
-                   void *opaque, const char *drv, const char *cap)
-{
-    int err, err2;
-    const char *efunc;
-    sigset_t set, old_set;
-
-    p->drv = drv;
-
-    err = sigfillset (&set);
-    if (err) {
-        logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-
-    err = pthread_mutex_init (&p->mutex, NULL);
-    if (err) {
-        efunc = "pthread_mutex_init";
-        goto err0;
-    }
-
-    err = pthread_cond_init (&p->cond, NULL);
-    if (err) {
-        efunc = "pthread_cond_init";
-        goto err1;
-    }
-
-    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
-    if (err) {
-        efunc = "pthread_sigmask";
-        goto err2;
-    }
-
-    err = pthread_create (&p->thread, NULL, func, opaque);
-
-    err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
-    if (err2) {
-        logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed",
-                cap, AUDIO_FUNC);
-        /* We have failed to restore original signal mask, all bets are off,
-           so terminate the process */
-        exit (EXIT_FAILURE);
-    }
-
-    if (err) {
-        efunc = "pthread_create";
-        goto err2;
-    }
-
-    return 0;
-
- err2:
-    err2 = pthread_cond_destroy (&p->cond);
-    if (err2) {
-        logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
-    }
-
- err1:
-    err2 = pthread_mutex_destroy (&p->mutex);
-    if (err2) {
-        logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
-    }
-
- err0:
-    logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
-    return -1;
-}
-
-int audio_pt_fini (struct audio_pt *p, const char *cap)
-{
-    int err, ret = 0;
-
-    err = pthread_cond_destroy (&p->cond);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
-        ret = -1;
-    }
-
-    err = pthread_mutex_destroy (&p->mutex);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
-        ret = -1;
-    }
-    return ret;
-}
-
-int audio_pt_lock (struct audio_pt *p, const char *cap)
-{
-    int err;
-
-    err = pthread_mutex_lock (&p->mutex);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-    return 0;
-}
-
-int audio_pt_unlock (struct audio_pt *p, const char *cap)
-{
-    int err;
-
-    err = pthread_mutex_unlock (&p->mutex);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-    return 0;
-}
-
-int audio_pt_wait (struct audio_pt *p, const char *cap)
-{
-    int err;
-
-    err = pthread_cond_wait (&p->cond, &p->mutex);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-    return 0;
-}
-
-int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
-{
-    int err;
-
-    err = pthread_mutex_unlock (&p->mutex);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-    err = pthread_cond_signal (&p->cond);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-    return 0;
-}
-
-int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
-{
-    int err;
-    void *ret;
-
-    err = pthread_join (p->thread, &ret);
-    if (err) {
-        logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
-        return -1;
-    }
-    *arg = ret;
-    return 0;
-}
diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h
deleted file mode 100644
index 0dfff76..0000000
--- a/audio/audio_pt_int.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef QEMU_AUDIO_PT_INT_H
-#define QEMU_AUDIO_PT_INT_H
-
-#include <pthread.h>
-
-struct audio_pt {
-    const char *drv;
-    pthread_t thread;
-    pthread_cond_t cond;
-    pthread_mutex_t mutex;
-};
-
-int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
-                   const char *, const char *);
-int audio_pt_fini (struct audio_pt *, const char *);
-int audio_pt_lock (struct audio_pt *, const char *);
-int audio_pt_unlock (struct audio_pt *, const char *);
-int audio_pt_wait (struct audio_pt *, const char *);
-int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
-int audio_pt_join (struct audio_pt *, void **, const char *);
-
-#endif /* audio_pt_int.h */
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 5a3dc90..473eac9 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -71,20 +71,22 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
 
 static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
 {
+    g_free(hw->buf_emul);
     g_free (HWBUF);
     HWBUF = NULL;
 }
 
-static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
+static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
 {
-    HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (struct st_sample));
-    if (!HWBUF) {
-        dolog ("Could not allocate " NAME " buffer (%d samples)\n",
-               hw->samples);
-        return -1;
+    size_t samples;
+    if (hw->pcm_ops->glue(buffer_size_, TYPE)) {
+        samples = hw->pcm_ops->glue(buffer_size_, TYPE)(hw);
+    } else {
+        samples = 1024; /* todo better default */
     }
 
-    return 0;
+    HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample)*samples);
+    HWBUF->size = samples;
 }
 
 static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@@ -103,7 +105,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
 {
     int samples;
 
-    samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
+    samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
 
     sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
     if (!sw->buf) {
@@ -264,11 +266,6 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
         goto err0;
     }
 
-    if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
-        dolog ("hw->samples=%d\n", hw->samples);
-        goto err1;
-    }
-
 #ifdef DAC
     hw->clip = mixeng_clip
 #else
@@ -279,9 +276,7 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
         [hw->info.swap_endianness]
         [audio_bits_to_index (hw->info.bits)];
 
-    if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
-        goto err1;
-    }
+    glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
 
     QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
     glue (s->nb_hw_voices_, TYPE) -= 1;
@@ -290,9 +285,7 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
 #endif
     return hw;
 
- err1:
-    glue (hw->pcm_ops->fini_, TYPE) (hw);
- err0:
+err0:
     g_free (hw);
     return NULL;
 }
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index dfa5e79..d6c7636 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -40,9 +40,7 @@ typedef struct coreaudioVoiceOut {
     AudioDeviceID outputDeviceID;
     UInt32 audioDevicePropertyBufferFrameSize;
     AudioStreamBasicDescription outputStreamBasicDescription;
-    int live;
-    int decr;
-    int rpos;
+    size_t samples;
 } coreaudioVoiceOut;
 
 static void coreaudio_logstatus (OSStatus status)
@@ -181,31 +179,29 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
     return 0;
 }
 
-static int coreaudio_run_out (HWVoiceOut *hw, int live)
-{
-    int decr;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
-
-    if (coreaudio_lock (core, "coreaudio_run_out")) {
-        return 0;
+#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
+    static ret_type glue(coreaudio_, name)args_decl             \
+    {                                                           \
+        coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \
+        ret_type ret;                                           \
+                                                                \
+        if (coreaudio_lock(core, "coreaudio_" #name)) {         \
+            return 0;                                           \
+        }                                                       \
+                                                                \
+        ret = glue(audio_generic_, name)args;                   \
+                                                                \
+        coreaudio_unlock(core, "coreaudio_" #name);             \
+        return ret;                                             \
     }
-
-    if (core->decr > live) {
-        ldebug ("core->decr %d live %d core->live %d\n",
-                core->decr,
-                live,
-                core->live);
-    }
-
-    decr = audio_MIN (core->decr, live);
-    core->decr -= decr;
-
-    core->live = live - decr;
-    hw->rpos = core->rpos;
-
-    coreaudio_unlock (core, "coreaudio_run_out");
-    return decr;
-}
+COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVocieOut *hw, size_t *size),
+                       (hw, size))
+COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
+                       (HWVoiceOut *hw, void *buf, size_t size),
+                       (hw, buf, size))
+COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
+                       (hw, buf, size))
+#undef COREAUDIO_WRAPPER_FUNC
 
 /* callback to feed audiooutput buffer */
 static OSStatus audioDeviceIOProc(
@@ -217,19 +213,11 @@ static OSStatus audioDeviceIOProc(
     const AudioTimeStamp* inOutputTime,
     void* hwptr)
 {
-    UInt32 frame, frameCount;
-    float *out = outOutputData->mBuffers[0].mData;
+    UInt32 frameCount, pending_frames;
+    void *out = outOutputData->mBuffers[0].mData;
     HWVoiceOut *hw = hwptr;
     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
-    int rpos, live;
-    struct st_sample *src;
-#ifndef FLOAT_MIXENG
-#ifdef RECIPROCAL
-    const float scale = 1.f / UINT_MAX;
-#else
-    const float scale = UINT_MAX;
-#endif
-#endif
+    size_t len;
 
     if (coreaudio_lock (core, "audioDeviceIOProc")) {
         inInputTime = 0;
@@ -237,45 +225,49 @@ static OSStatus audioDeviceIOProc(
     }
 
     frameCount = core->audioDevicePropertyBufferFrameSize;
-    live = core->live;
+    pending_frames = hw->pending_emul >> hw->info.shift;
 
     /* if there are not enough samples, set signal and return */
-    if (live < frameCount) {
+    if (pending_frames < frameCount) {
         inInputTime = 0;
         coreaudio_unlock (core, "audioDeviceIOProc(empty)");
         return 0;
     }
 
-    rpos = core->rpos;
-    src = hw->mix_buf + rpos;
+    len = frameCount << hw->info.shift;
+    while (len) {
+        size_t write_len;
+        ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+        if (start < 0) {
+            start += hw->size_emul;
+        }
+        assert(start >= 0 && start < hw->size_emul);
 
-    /* fill buffer */
-    for (frame = 0; frame < frameCount; frame++) {
-#ifdef FLOAT_MIXENG
-        *out++ = src[frame].l; /* left channel */
-        *out++ = src[frame].r; /* right channel */
-#else
-#ifdef RECIPROCAL
-        *out++ = src[frame].l * scale; /* left channel */
-        *out++ = src[frame].r * scale; /* right channel */
-#else
-        *out++ = src[frame].l / scale; /* left channel */
-        *out++ = src[frame].r / scale; /* right channel */
-#endif
-#endif
+        write_len = audio_MIN(audio_MIN(hw->pending_emul, len),
+                              hw->size_emul - start);
+
+        memcpy(out, hw->buf_emul + start, write_len);
+        hw->pending_emul -= write_len;
+        len -= write_len;
+        out += write_len;
     }
 
-    rpos = (rpos + frameCount) % hw->samples;
-    core->decr += frameCount;
-    core->rpos = rpos;
-
     coreaudio_unlock (core, "audioDeviceIOProc");
     return 0;
 }
 
-static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
+static UInt32 coreaudio_get_flags(struct audio_pcm_info *info,
+                                  struct audsettings *as)
 {
-    return audio_pcm_sw_write (sw, buf, len);
+    UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0;
+    if (as->endianness) { /* 0 = little, 1 = big */
+        flags |= kAudioFormatFlagIsBigEndian;
+    }
+
+    if (flags == 0) { /* must not be 0 */
+        flags = kAudioFormatFlagsAreAllClear;
+    }
+    return flags;
 }
 
 static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
@@ -375,7 +367,7 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
                            "Could not get device buffer frame size\n");
         return -1;
     }
-    hw->samples = (pdo->has_buffer_count ? pdo->buffer_count : 4) *
+    core->samples = (pdo->has_buffer_count ? pdo->buffer_count : 4) *
         core->audioDevicePropertyBufferFrameSize;
 
     /* get StreamFormat */
@@ -396,6 +388,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 
     /* set Samplerate */
     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
+    core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
+    core->outputStreamBasicDescription.mFormatFlags =
+        coreaudio_get_flags(hw->info, as);
+    core->outputStreamBasicDescription.mBytesPerPacket =
+        core->outputStreamBasicDescription.mBytesPerFrame =
+        hw->info.nchannels * hw->info.bits / 8;
+    core->outputStreamBasicDescription.mFramesPerPacket = 1;
+    core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels;
+    core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits;
+
     propertySize = sizeof(core->outputStreamBasicDescription);
     status = AudioDeviceSetProperty(
         core->outputDeviceID,
@@ -434,6 +436,12 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
     return 0;
 }
 
+static size_t coreaudio_buffer_size_out(HWVoiceOut *hw)
+{
+    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+    return core->samples;
+}
+
 static void coreaudio_fini_out (HWVoiceOut *hw)
 {
     OSStatus status;
@@ -509,8 +517,10 @@ static void coreaudio_audio_fini (void *opaque)
 static struct audio_pcm_ops coreaudio_pcm_ops = {
     .init_out = coreaudio_init_out,
     .fini_out = coreaudio_fini_out,
-    .run_out  = coreaudio_run_out,
     .write    = coreaudio_write,
+    .buffer_size_out = coreaudio_buffer_size_out,
+    .get_buffer_out = coreaudio_get_buffer_out,
+    .put_buffer_out = coreaudio_put_buffer_out_nowrite,
     .ctl_out  = coreaudio_ctl_out
 };
 
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 96181ef..6a10b67 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -29,6 +29,8 @@
 #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
 #define FIELD dsound_capture_buffer
 #define FIELD2 dsound_capture
+#define HWVOICE HWVoiceIn
+#define DSOUNDVOICE DSoundVoiceIn
 #else
 #define NAME "playback buffer"
 #define NAME2 "DirectSound"
@@ -37,6 +39,8 @@
 #define BUFPTR LPDIRECTSOUNDBUFFER
 #define FIELD dsound_buffer
 #define FIELD2 dsound
+#define HWVOICE HWVoiceOut
+#define DSOUNDVOICE DSoundVoiceOut
 #endif
 
 static int glue (dsound_unlock_, TYPE) (
@@ -72,8 +76,6 @@ static int glue (dsound_lock_, TYPE) (
     )
 {
     HRESULT hr;
-    LPVOID p1 = NULL, p2 = NULL;
-    DWORD blen1 = 0, blen2 = 0;
     DWORD flag;
 
 #ifdef DSBTYPE_IN
@@ -81,7 +83,7 @@ static int glue (dsound_lock_, TYPE) (
 #else
     flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
 #endif
-    hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
+    hr = glue(IFACE, _Lock)(buf, pos, len, p1p, blen1p, p2p, blen2p, flag);
 
     if (FAILED (hr)) {
 #ifndef DSBTYPE_IN
@@ -96,34 +98,34 @@ static int glue (dsound_lock_, TYPE) (
         goto fail;
     }
 
-    if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
+    if ((p1p && *p1p && (*blen1p & info->align)) ||
+        (p2p && *p2p && (*blen2p & info->align))) {
         dolog ("DirectSound returned misaligned buffer %ld %ld\n",
-               blen1, blen2);
-        glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
+               *blen1p, *blen2p);
+        glue (dsound_unlock_, TYPE) (buf, *p1p, p2p ? *p2p : NULL, *blen1p,
+                                     blen2p ? *blen2p : 0);
         goto fail;
     }
 
-    if (!p1 && blen1) {
-        dolog ("warning: !p1 && blen1=%ld\n", blen1);
-        blen1 = 0;
+    if (p1p && !*p1p && *blen1p) {
+        dolog("warning: !p1 && blen1=%ld\n", *blen1p);
+        *blen1p = 0;
     }
 
-    if (!p2 && blen2) {
-        dolog ("warning: !p2 && blen2=%ld\n", blen2);
-        blen2 = 0;
+    if (p2p && !*p2p && *blen2p) {
+        dolog("warning: !p2 && blen2=%ld\n", *blen2p);
+        *blen2p = 0;
     }
 
-    *p1p = p1;
-    *p2p = p2;
-    *blen1p = blen1;
-    *blen2p = blen2;
     return 0;
 
  fail:
     *p1p = NULL - 1;
-    *p2p = NULL - 1;
     *blen1p = -1;
-    *blen2p = -1;
+    if (p2p) {
+        *p2p = NULL - 1;
+        *blen2p = -1;
+    }
     return -1;
 }
 
@@ -242,7 +244,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
         goto fail0;
     }
 
-    ds->first_time = 1;
     obt_as.endianness = 0;
     audio_pcm_init_info (&hw->info, &obt_as);
 
@@ -252,15 +253,13 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
             bc.dwBufferBytes, hw->info.align + 1
             );
     }
-    hw->samples = bc.dwBufferBytes >> hw->info.shift;
+    hw->size_emul = bc.dwBufferBytes;
+    ds->samples = bc.dwBufferBytes >> hw->info.shift;
     ds->s = s;
 
 #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, settings.freq, settings.nchannels, settings.fmt);
 #endif
     return 0;
 
@@ -269,6 +268,12 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
     return -1;
 }
 
+static size_t glue(dsound_buffer_size_, TYPE)(HWVOICE *hw)
+{
+    DSOUNDVOICE *ds = (DSOUNDVOICE *) hw;
+    return ds->samples;
+}
+
 #undef NAME
 #undef NAME2
 #undef TYPE
@@ -276,3 +281,5 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
 #undef BUFPTR
 #undef FIELD
 #undef FIELD2
+#undef HWVOICE
+#undef DSOUNDVOICE
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 7e7b3f2..fcffef7 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -51,21 +51,15 @@ typedef struct {
 typedef struct {
     HWVoiceOut hw;
     LPDIRECTSOUNDBUFFER dsound_buffer;
-    DWORD old_pos;
-    int first_time;
     dsound *s;
-#ifdef DEBUG_DSOUND
-    DWORD old_ppos;
-    DWORD played;
-    DWORD mixed;
-#endif
+    size_t samples;
 } DSoundVoiceOut;
 
 typedef struct {
     HWVoiceIn hw;
-    int first_time;
     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
     dsound *s;
+    size_t samples;
 } DSoundVoiceIn;
 
 static void dsound_log_hresult (HRESULT hr)
@@ -241,11 +235,6 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
     dsound_log_hresult (hr);
 }
 
-static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
-{
-    return muldiv64(usecs, info->bytes_per_second, 1000000);
-}
-
 #ifdef DEBUG_DSOUND
 static void print_wave_format (WAVEFORMATEX *wfx)
 {
@@ -310,33 +299,6 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
     return 0;
 }
 
-static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
-{
-    int src_len1 = dst_len;
-    int src_len2 = 0;
-    int pos = hw->rpos + dst_len;
-    struct st_sample *src1 = hw->mix_buf + hw->rpos;
-    struct st_sample *src2 = NULL;
-
-    if (pos > hw->samples) {
-        src_len1 = hw->samples - hw->rpos;
-        src2 = hw->mix_buf;
-        src_len2 = dst_len - src_len1;
-        pos = src_len2;
-    }
-
-    if (src_len1) {
-        hw->clip (dst, src1, src_len1);
-    }
-
-    if (src_len2) {
-        dst = advance (dst, src_len1 << hw->info.shift);
-        hw->clip (dst, src2, src_len2);
-    }
-
-    hw->rpos = pos % hw->samples;
-}
-
 static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
                                  dsound *s)
 {
@@ -348,7 +310,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
         dsb,
         &hw->info,
         0,
-        hw->samples << hw->info.shift,
+        hw->size_emul,
         &p1, &p2,
         &blen1, &blen2,
         1,
@@ -452,143 +414,51 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
-static int dsound_write (SWVoiceOut *sw, void *buf, int len)
+static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
 {
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
-static int dsound_run_out (HWVoiceOut *hw, int live)
-{
-    int err;
+    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
     HRESULT hr;
+    DWORD ppos, act_size;
+    size_t req_size;
+    int err;
+    void *ret;
+
+    hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL);
+    if (FAILED(hr)) {
+        dsound_logerr(hr, "Could not get playback buffer position\n");
+        *size = 0;
+        return NULL;
+    }
+
+    req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
+    req_size = audio_MIN(req_size, hw->size_emul - hw->pos_emul);
+
+    err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
+                          &act_size, NULL, false, ds->s);
+    if (err) {
+        dolog("Failed to lock buffer\n");
+        *size = 0;
+        return NULL;
+    }
+
+    *size = act_size;
+    return ret;
+}
+
+static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
+{
     DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
     LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
-    int len, hwshift;
-    DWORD blen1, blen2;
-    DWORD len1, len2;
-    DWORD decr;
-    DWORD wpos, ppos, old_pos;
-    LPVOID p1, p2;
-    int bufsize;
-    dsound *s = ds->s;
-    AudiodevDsoundOptions *dso = s->dev->dsound;
+    int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
 
-    if (!dsb) {
-        dolog ("Attempt to run empty with playback buffer\n");
-        return 0;
-    }
-
-    hwshift = hw->info.shift;
-    bufsize = hw->samples << hwshift;
-
-    hr = IDirectSoundBuffer_GetCurrentPosition (
-        dsb,
-        &ppos,
-        ds->first_time ? &wpos : NULL
-        );
-    if (FAILED (hr)) {
-        dsound_logerr (hr, "Could not get playback buffer position\n");
-        return 0;
-    }
-
-    len = live << hwshift;
-
-    if (ds->first_time) {
-        if (dso->latency) {
-            DWORD cur_blat;
-
-            cur_blat = audio_ring_dist (wpos, ppos, bufsize);
-            ds->first_time = 0;
-            old_pos = wpos;
-            old_pos +=
-                usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
-            old_pos %= bufsize;
-            old_pos &= ~hw->info.align;
-        }
-        else {
-            old_pos = wpos;
-        }
-#ifdef DEBUG_DSOUND
-        ds->played = 0;
-        ds->mixed = 0;
-#endif
-    }
-    else {
-        if (ds->old_pos == ppos) {
-#ifdef DEBUG_DSOUND
-            dolog ("old_pos == ppos\n");
-#endif
-            return 0;
-        }
-
-#ifdef DEBUG_DSOUND
-        ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
-#endif
-        old_pos = ds->old_pos;
-    }
-
-    if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
-        len = ppos - old_pos;
-    }
-    else {
-        if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
-            len = 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;
-    }
-
-    len &= ~hw->info.align;
-    if (!len) {
-        return 0;
-    }
-
-#ifdef DEBUG_DSOUND
-    ds->old_ppos = ppos;
-#endif
-    err = dsound_lock_out (
-        dsb,
-        &hw->info,
-        old_pos,
-        len,
-        &p1, &p2,
-        &blen1, &blen2,
-        0,
-        s
-        );
     if (err) {
+        dolog("Failed to unlock buffer!!\n");
         return 0;
     }
+    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
 
-    len1 = blen1 >> hwshift;
-    len2 = blen2 >> hwshift;
-    decr = len1 + len2;
-
-    if (p1 && len1) {
-        dsound_write_sample (hw, p1, len1);
-    }
-
-    if (p2 && len2) {
-        dsound_write_sample (hw, p2, len2);
-    }
-
-    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
-    ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
-
-#ifdef DEBUG_DSOUND
-    ds->mixed += decr << hwshift;
-
-    dolog ("played %lu mixed %lu diff %ld sec %f\n",
-           ds->played,
-           ds->mixed,
-           ds->mixed - ds->played,
-           abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
-#endif
-    return decr;
+    return len;
 }
 
 static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
@@ -643,101 +513,49 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
     return 0;
 }
 
-static int dsound_read (SWVoiceIn *sw, void *buf, int len)
+static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
 {
-    return audio_pcm_sw_read (sw, buf, len);
-}
-
-static int dsound_run_in (HWVoiceIn *hw)
-{
-    int err;
+    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
     HRESULT hr;
+    DWORD cpos, act_size;
+    size_t req_size;
+    int err;
+    void *ret;
+
+    hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL);
+    if (FAILED(hr)) {
+        dsound_logerr(hr, "Could not get capture buffer position\n");
+        *size = 0;
+        return NULL;
+    }
+
+    req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
+    req_size = audio_MIN(req_size, hw->size_emul - hw->pos_emul);
+
+    err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
+                         &act_size, NULL, false, ds->s);
+    if (err) {
+        dolog("Failed to lock buffer\n");
+        *size = 0;
+        return NULL;
+    }
+
+    *size = act_size;
+    return ret;
+}
+
+static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
+{
     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
     LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
-    int live, len, dead;
-    DWORD blen1, blen2;
-    DWORD len1, len2;
-    DWORD decr;
-    DWORD cpos, rpos;
-    LPVOID p1, p2;
-    int hwshift;
-    dsound *s = ds->s;
+    int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
 
-    if (!dscb) {
-        dolog ("Attempt to run without capture buffer\n");
-        return 0;
-    }
-
-    hwshift = hw->info.shift;
-
-    live = audio_pcm_hw_get_live_in (hw);
-    dead = hw->samples - live;
-    if (!dead) {
-        return 0;
-    }
-
-    hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
-        dscb,
-        &cpos,
-        ds->first_time ? &rpos : NULL
-        );
-    if (FAILED (hr)) {
-        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",
-                    rpos, hw->info.align);
-        }
-        hw->wpos = rpos >> hwshift;
-    }
-
-    if (cpos & hw->info.align) {
-        ldebug ("warning: Misaligned capture position %ld(%d)\n",
-                cpos, hw->info.align);
-    }
-    cpos >>= hwshift;
-
-    len = audio_ring_dist (cpos, hw->wpos, hw->samples);
-    if (!len) {
-        return 0;
-    }
-    len = audio_MIN (len, dead);
-
-    err = dsound_lock_in (
-        dscb,
-        &hw->info,
-        hw->wpos << hwshift,
-        len << hwshift,
-        &p1,
-        &p2,
-        &blen1,
-        &blen2,
-        0,
-        s
-        );
     if (err) {
-        return 0;
+        dolog("Failed to unlock buffer!!\n");
+        return;
     }
-
-    len1 = blen1 >> hwshift;
-    len2 = blen2 >> hwshift;
-    decr = len1 + len2;
-
-    if (p1 && len1) {
-        hw->conv (hw->conv_buf + hw->wpos, p1, len1);
-    }
-
-    if (p2 && len2) {
-        hw->conv (hw->conv_buf, p2, len2);
-    }
-
-    dsound_unlock_in (dscb, p1, p2, blen1, blen2);
-    hw->wpos = (hw->wpos + decr) % hw->samples;
-    return decr;
+    hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
 }
 
 static void dsound_audio_fini (void *opaque)
@@ -853,14 +671,18 @@ static void *dsound_audio_init(Audiodev *dev)
 static struct audio_pcm_ops dsound_pcm_ops = {
     .init_out = dsound_init_out,
     .fini_out = dsound_fini_out,
-    .run_out  = dsound_run_out,
-    .write    = dsound_write,
+    .write    = audio_generic_write,
+    .buffer_size_out = dsound_buffer_size_out,
+    .get_buffer_out = dsound_get_buffer_out,
+    .put_buffer_out = dsound_put_buffer_out,
     .ctl_out  = dsound_ctl_out,
 
     .init_in  = dsound_init_in,
     .fini_in  = dsound_fini_in,
-    .run_in   = dsound_run_in,
-    .read     = dsound_read,
+    .read     = audio_generic_read,
+    .buffer_size_in = dsound_buffer_size_in,
+    .get_buffer_in = dsound_get_buffer_in,
+    .put_buffer_in = dsound_put_buffer_in,
     .ctl_in   = dsound_ctl_in
 };
 
diff --git a/audio/mixeng.h b/audio/mixeng.h
index 9de443b..013d894 100644
--- a/audio/mixeng.h
+++ b/audio/mixeng.h
@@ -24,6 +24,8 @@
 #ifndef QEMU_MIXENG_H
 #define QEMU_MIXENG_H
 
+#include <stddef.h>
+
 #ifdef FLOAT_MIXENG
 typedef float mixeng_real;
 struct mixeng_volume { int mute; mixeng_real r; mixeng_real l; };
@@ -32,6 +34,7 @@ struct st_sample { mixeng_real l; mixeng_real r; };
 struct mixeng_volume { int mute; int64_t r; int64_t l; };
 struct st_sample { int64_t l; int64_t r; };
 #endif
+typedef struct st_sample st_sample;
 
 typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
 typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
@@ -40,10 +43,10 @@ extern t_sample *mixeng_conv[2][2][2][3];
 extern f_sample *mixeng_clip[2][2][2][3];
 
 void *st_rate_start (int inrate, int outrate);
-void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
-                   int *isamp, int *osamp);
-void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
-                       int *isamp, int *osamp);
+void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
+                  size_t *isamp, size_t *osamp);
+void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
+                      size_t *isamp, size_t *osamp);
 void st_rate_stop (void *opaque);
 void mixeng_clear (struct st_sample *buf, int len);
 void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 4c94a26..13c1263 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -38,10 +38,9 @@ typedef struct NoVoiceIn {
     int64_t old_ticks;
 } NoVoiceIn;
 
-static int no_run_out (HWVoiceOut *hw, int live)
+static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
 {
     NoVoiceOut *no = (NoVoiceOut *) hw;
-    int decr, samples;
     int64_t now;
     int64_t ticks;
     int64_t bytes;
@@ -49,24 +48,14 @@ static int no_run_out (HWVoiceOut *hw, int live)
     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     ticks = now - no->old_ticks;
     bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
-    bytes = audio_MIN (bytes, INT_MAX);
-    samples = bytes >> hw->info.shift;
 
     no->old_ticks = now;
-    decr = audio_MIN (live, samples);
-    hw->rpos = (hw->rpos + decr) % hw->samples;
-    return decr;
-}
-
-static int no_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
+    return audio_MIN(len, bytes);
 }
 
 static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
 {
     audio_pcm_init_info (&hw->info, as);
-    hw->samples = 1024;
     return 0;
 }
 
@@ -85,7 +74,6 @@ static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
 static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     audio_pcm_init_info (&hw->info, as);
-    hw->samples = 1024;
     return 0;
 }
 
@@ -94,37 +82,21 @@ static void no_fini_in (HWVoiceIn *hw)
     (void) hw;
 }
 
-static int no_run_in (HWVoiceIn *hw)
+static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
 {
+    size_t to_clear;
     NoVoiceIn *no = (NoVoiceIn *) hw;
-    int live = audio_pcm_hw_get_live_in (hw);
-    int dead = hw->samples - live;
-    int samples = 0;
 
-    if (dead) {
-        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-        int64_t ticks = now - no->old_ticks;
-        int64_t bytes =
-            muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
+    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    int64_t ticks = now - no->old_ticks;
+    int64_t bytes =
+        muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
 
-        no->old_ticks = now;
-        bytes = audio_MIN (bytes, INT_MAX);
-        samples = bytes >> hw->info.shift;
-        samples = audio_MIN (samples, dead);
-    }
-    return samples;
-}
+    no->old_ticks = now;
+    to_clear = audio_MIN(bytes, size);
 
-static int no_read (SWVoiceIn *sw, void *buf, int size)
-{
-    /* use custom code here instead of audio_pcm_sw_read() to avoid
-     * useless resampling/mixing */
-    int samples = size >> sw->info.shift;
-    int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
-    int to_clear = audio_MIN (samples, total);
-    sw->total_hw_samples_acquired += total;
-    audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
-    return to_clear << sw->info.shift;
+    audio_pcm_info_clear_buf(&hw->info, buf, to_clear >> hw->info.shift);
+    return to_clear;
 }
 
 static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
@@ -147,13 +119,11 @@ static void no_audio_fini (void *opaque)
 static struct audio_pcm_ops no_pcm_ops = {
     .init_out = no_init_out,
     .fini_out = no_fini_out,
-    .run_out  = no_run_out,
     .write    = no_write,
     .ctl_out  = no_ctl_out,
 
     .init_in  = no_init_in,
     .fini_in  = no_fini_in,
-    .run_in   = no_run_in,
     .read     = no_read,
     .ctl_in   = no_ctl_in
 };
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 9eae769..de37645 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -42,22 +42,20 @@
 
 typedef struct OSSVoiceOut {
     HWVoiceOut hw;
-    void *pcm_buf;
     int fd;
-    int wpos;
     int nfrags;
     int fragsize;
     int mmapped;
-    int pending;
+    size_t samples;
     Audiodev *dev;
 } OSSVoiceOut;
 
 typedef struct OSSVoiceIn {
     HWVoiceIn hw;
-    void *pcm_buf;
     int fd;
     int nfrags;
     int fragsize;
+    size_t samples;
     Audiodev *dev;
 } OSSVoiceIn;
 
@@ -136,11 +134,6 @@ static void oss_poll_in (HWVoiceIn *hw)
     qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
 }
 
-static int oss_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
 static int aud_to_ossfmt (AudioFormat fmt, int endianness)
 {
     switch (fmt) {
@@ -378,97 +371,88 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
     return -1;
 }
 
-static void oss_write_pending (OSSVoiceOut *oss)
+static size_t oss_get_available_bytes(OSSVoiceOut *oss)
 {
-    HWVoiceOut *hw = &oss->hw;
+    int err;
+    struct count_info cntinfo;
+    assert(oss->mmapped);
 
+    err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
+    if (err < 0) {
+        oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
+        return 0;
+    }
+
+    return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
+}
+
+static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    if (oss->mmapped) {
+        *size = audio_MIN(oss_get_available_bytes(oss),
+                          hw->size_emul - hw->pos_emul);
+        return hw->buf_emul + hw->pos_emul;
+    } else {
+        return audio_generic_get_buffer_out(hw, size);
+    }
+}
+
+static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
     if (oss->mmapped) {
-        return;
+        assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
+
+        hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
+        return size;
+    } else {
+        return audio_generic_put_buffer_out(hw, buf, size);
+    }
+}
+
+static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    size_t pos;
+
+    if (oss->mmapped) {
+        size_t total_len;
+        len = audio_MIN(len, oss_get_available_bytes(oss));
+
+        total_len = len;
+        while (len) {
+            size_t to_copy = audio_MIN(len, hw->size_emul - hw->pos_emul);
+            memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
+
+            hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul;
+            buf += to_copy;
+            len -= to_copy;
+        }
+        return total_len;
     }
 
-    while (oss->pending) {
-        int samples_written;
+    pos = 0;
+    while (len) {
         ssize_t bytes_written;
-        int samples_till_end = hw->samples - oss->wpos;
-        int samples_to_write = audio_MIN (oss->pending, samples_till_end);
-        int bytes_to_write = samples_to_write << hw->info.shift;
-        void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
+        void *pcm = advance(buf, pos);
 
-        bytes_written = write (oss->fd, pcm, bytes_to_write);
+        bytes_written = write(oss->fd, pcm, len);
         if (bytes_written < 0) {
             if (errno != EAGAIN) {
-                oss_logerr (errno, "failed to write %d bytes\n",
-                            bytes_to_write);
+                oss_logerr(errno, "failed to write %zu bytes\n",
+                           len);
             }
-            break;
-        }
-
-        if (bytes_written & hw->info.align) {
-            dolog ("misaligned write asked for %d, but got %zd\n",
-                   bytes_to_write, bytes_written);
-            return;
+            return pos;
         }
 
-        samples_written = bytes_written >> hw->info.shift;
-        oss->pending -= samples_written;
-        oss->wpos = (oss->wpos + samples_written) % hw->samples;
-        if (bytes_written - bytes_to_write) {
+        pos += bytes_written;
+        if (bytes_written < len) {
             break;
         }
+        len -= bytes_written;
     }
-}
-
-static int oss_run_out (HWVoiceOut *hw, int live)
-{
-    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
-    int err, decr;
-    struct audio_buf_info abinfo;
-    struct count_info cntinfo;
-    int bufsize;
-
-    bufsize = hw->samples << hw->info.shift;
-
-    if (oss->mmapped) {
-        int bytes, pos;
-
-        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
-        if (err < 0) {
-            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
-            return 0;
-        }
-
-        pos = hw->rpos << hw->info.shift;
-        bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
-        decr = audio_MIN (bytes >> hw->info.shift, live);
-    }
-    else {
-        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
-        if (err < 0) {
-            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
-            return 0;
-        }
-
-        if (abinfo.bytes > bufsize) {
-            trace_oss_invalid_available_size(abinfo.bytes, bufsize);
-            abinfo.bytes = bufsize;
-        }
-
-        if (abinfo.bytes < 0) {
-            trace_oss_invalid_available_size(abinfo.bytes, bufsize);
-            return 0;
-        }
-
-        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
-        if (!decr) {
-            return 0;
-        }
-    }
-
-    decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
-    oss->pending += decr;
-    oss_write_pending (oss);
-
-    return decr;
+    return pos;
 }
 
 static void oss_fini_out (HWVoiceOut *hw)
@@ -479,18 +463,13 @@ static void oss_fini_out (HWVoiceOut *hw)
     ldebug ("oss_fini\n");
     oss_anal_close (&oss->fd);
 
-    if (oss->pcm_buf) {
-        if (oss->mmapped) {
-            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->samples << hw->info.shift);
-            }
+    if (oss->mmapped && hw->buf_emul) {
+        err = munmap(hw->buf_emul, hw->size_emul);
+        if (err) {
+            oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
+                       hw->buf_emul, hw->size_emul);
         }
-        else {
-            g_free (oss->pcm_buf);
-        }
-        oss->pcm_buf = NULL;
+        hw->buf_emul = NULL;
     }
 }
 
@@ -537,23 +516,24 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
                obt.nfrags * obt.fragsize, hw->info.align + 1);
     }
 
-    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+    oss->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 
     oss->mmapped = 0;
     if (oopts->has_try_mmap && oopts->try_mmap) {
-        oss->pcm_buf = mmap (
+        hw->size_emul = oss->samples << hw->info.shift;
+        hw->buf_emul = mmap (
             NULL,
-            hw->samples << hw->info.shift,
+            hw->size_emul,
             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->samples << hw->info.shift);
-        }
-        else {
+        if (hw->buf_emul == MAP_FAILED) {
+            oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
+                       hw->size_emul);
+            hw->buf_emul = NULL;
+        } else {
             int err;
             int trig = 0;
             if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@@ -573,37 +553,27 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
             }
 
             if (!oss->mmapped) {
-                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+                err = munmap(hw->buf_emul, hw->size_emul);
                 if (err) {
-                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
-                                oss->pcm_buf, hw->samples << hw->info.shift);
+                    oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
+                               hw->buf_emul, hw->size_emul);
                 }
+                hw->buf_emul = NULL;
             }
         }
     }
 
-    if (!oss->mmapped) {
-        oss->pcm_buf = audio_calloc (
-            AUDIO_FUNC,
-            hw->samples,
-            1 << hw->info.shift
-            );
-        if (!oss->pcm_buf) {
-            dolog (
-                "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
-                hw->samples,
-                1 << hw->info.shift
-                );
-            oss_anal_close (&fd);
-            return -1;
-        }
-    }
-
     oss->fd = fd;
     oss->dev = dev;
     return 0;
 }
 
+static size_t oss_buffer_size_out(HWVoiceOut *hw)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    return oss->samples;
+}
+
 static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     int trig;
@@ -626,7 +596,7 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
                 return 0;
             }
 
-            audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+            audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, oss->samples);
             trig = PCM_ENABLE_OUTPUT;
             if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
                 oss_logerr (
@@ -699,102 +669,54 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
                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 samples, each %d bytes)\n",
-               hw->samples, 1 << hw->info.shift);
-        oss_anal_close (&fd);
-        return -1;
-    }
+    oss->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 
     oss->fd = fd;
     oss->dev = dev;
     return 0;
 }
 
+static size_t oss_buffer_size_in(HWVoiceIn *hw)
+{
+    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+    return oss->samples;
+}
+
 static void oss_fini_in (HWVoiceIn *hw)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 
     oss_anal_close (&oss->fd);
-
-    g_free(oss->pcm_buf);
-    oss->pcm_buf = NULL;
 }
 
-static int oss_run_in (HWVoiceIn *hw)
+static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
-    int hwshift = hw->info.shift;
-    int i;
-    int live = audio_pcm_hw_get_live_in (hw);
-    int dead = hw->samples - live;
-    size_t read_samples = 0;
-    struct {
-        int add;
-        int len;
-    } bufs[2] = {
-        { .add = hw->wpos, .len = 0 },
-        { .add = 0,        .len = 0 }
-    };
+    size_t pos = 0;
 
-    if (!dead) {
-        return 0;
-    }
-
-    if (hw->wpos + dead > hw->samples) {
-        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
-        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
-    }
-    else {
-        bufs[0].len = dead << hwshift;
-    }
-
-    for (i = 0; i < 2; ++i) {
+    while (len) {
         ssize_t nread;
 
-        if (bufs[i].len) {
-            void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
-            nread = read (oss->fd, p, bufs[i].len);
+        void *dst = advance(buf, pos);
+        nread = read(oss->fd, dst, len);
 
-            if (nread > 0) {
-                if (nread & hw->info.align) {
-                    dolog ("warning: Misaligned read %zd (requested %d), "
-                           "alignment %d\n", nread, bufs[i].add << hwshift,
-                           hw->info.align + 1);
-                }
-                read_samples += nread >> hwshift;
-                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
-            }
-
-            if (bufs[i].len - nread) {
-                if (nread == -1) {
-                    switch (errno) {
-                    case EINTR:
-                    case EAGAIN:
-                        break;
-                    default:
-                        oss_logerr (
-                            errno,
-                            "Failed to read %d bytes of audio (to %p)\n",
-                            bufs[i].len, p
-                            );
-                        break;
-                    }
-                }
+        if (nread == -1) {
+            switch (errno) {
+            case EINTR:
+            case EAGAIN:
+                break;
+            default:
+                oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
+                           len, dst);
                 break;
             }
         }
+
+        pos += nread;
+        len -= nread;
     }
 
-    hw->wpos = (hw->wpos + read_samples) % hw->samples;
-    return read_samples;
-}
-
-static int oss_read (SWVoiceIn *sw, void *buf, int size)
-{
-    return audio_pcm_sw_read (sw, buf, size);
+    return pos;
 }
 
 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
@@ -847,14 +769,16 @@ static void oss_audio_fini (void *opaque)
 static struct audio_pcm_ops oss_pcm_ops = {
     .init_out = oss_init_out,
     .fini_out = oss_fini_out,
-    .run_out  = oss_run_out,
     .write    = oss_write,
+    .buffer_size_out = oss_buffer_size_out,
+    .get_buffer_out = oss_get_buffer_out,
+    .put_buffer_out = oss_put_buffer_out,
     .ctl_out  = oss_ctl_out,
 
     .init_in  = oss_init_in,
     .fini_in  = oss_fini_in,
-    .run_in   = oss_run_in,
     .read     = oss_read,
+    .buffer_size_in = oss_buffer_size_in,
     .ctl_in   = oss_ctl_in
 };
 
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 3990a80..a0b17c8 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -7,7 +7,6 @@
 
 #define AUDIO_CAP "pulseaudio"
 #include "audio_int.h"
-#include "audio_pt_int.h"
 
 typedef struct PAConnection {
     char *server;
@@ -28,28 +27,16 @@ typedef struct {
 
 typedef struct {
     HWVoiceOut hw;
-    int done;
-    int live;
-    int decr;
-    int rpos;
     pa_stream *stream;
-    void *pcm_buf;
-    struct audio_pt pt;
     paaudio *g;
     int samples;
 } PAVoiceOut;
 
 typedef struct {
     HWVoiceIn hw;
-    int done;
-    int dead;
-    int incr;
-    int wpos;
     pa_stream *stream;
-    void *pcm_buf;
-    struct audio_pt pt;
     const void *read_data;
-    size_t read_index, read_length;
+    size_t read_length;
     paaudio *g;
     int samples;
 } PAVoiceIn;
@@ -87,308 +74,96 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
 }
 #endif
 
-#define CHECK_SUCCESS_GOTO(c, rerror, expression, label)        \
+#define CHECK_SUCCESS_GOTO(c, expression, label, msg)           \
     do {                                                        \
         if (!(expression)) {                                    \
-            if (rerror) {                                       \
-                *(rerror) = pa_context_errno ((c)->context);    \
-            }                                                   \
+            qpa_logerr(pa_context_errno((c)->context), msg);    \
             goto label;                                         \
         }                                                       \
     } while (0);
 
-#define CHECK_DEAD_GOTO(c, stream, rerror, label)                       \
+#define CHECK_DEAD_GOTO(c, stream, label, msg)                          \
     do {                                                                \
         if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
             !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
             if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
                 ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
-                if (rerror) {                                           \
-                    *(rerror) = pa_context_errno ((c)->context);        \
-                }                                                       \
+                qpa_logerr(pa_context_errno((c)->context), msg);        \
             } else {                                                    \
-                if (rerror) {                                           \
-                    *(rerror) = PA_ERR_BADSTATE;                        \
-                }                                                       \
+                qpa_logerr(PA_ERR_BADSTATE, msg);                       \
             }                                                           \
             goto label;                                                 \
         }                                                               \
     } while (0);
 
-static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
+static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
 {
+    PAVoiceIn *p = (PAVoiceIn *) hw;
     PAConnection *c = p->g->conn;
+    size_t l;
+    int r;
 
     pa_threaded_mainloop_lock(c->mainloop);
 
-    CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
+    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+                    "pa_threaded_mainloop_lock failed\n");
 
-    while (length > 0) {
-        size_t l;
-
-        while (!p->read_data) {
-            int r;
-
-            r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
-            CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
-
-            if (!p->read_data) {
-                pa_threaded_mainloop_wait(c->mainloop);
-                CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
-            } else {
-                p->read_index = 0;
-            }
-        }
-
-        l = p->read_length < length ? p->read_length : length;
-        memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
-
-        data = (uint8_t *) data + l;
-        length -= l;
-
-        p->read_index += l;
-        p->read_length -= l;
+    if (!p->read_length) {
+        r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
+        CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
+                           "pa_stream_peek failed\n");
+    }
 
-        if (!p->read_length) {
-            int r;
+    l = audio_MIN(p->read_length, length);
+    memcpy(data, p->read_data, l);
 
-            r = pa_stream_drop (p->stream);
-            p->read_data = NULL;
-            p->read_length = 0;
-            p->read_index = 0;
+    p->read_data += l;
+    p->read_length -= l;
 
-            CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
-        }
+    if (!p->read_length) {
+        r = pa_stream_drop(p->stream);
+        CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
+                           "pa_stream_drop failed\n");
     }
 
     pa_threaded_mainloop_unlock(c->mainloop);
-    return 0;
+    return l;
 
 unlock_and_fail:
     pa_threaded_mainloop_unlock(c->mainloop);
-    return -1;
+    return 0;
 }
 
-static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
+static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
 {
+    PAVoiceOut *p = (PAVoiceOut *) hw;
     PAConnection *c = p->g->conn;
+    size_t l;
+    int r;
 
     pa_threaded_mainloop_lock(c->mainloop);
 
-    CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
-
-    while (length > 0) {
-        size_t l;
-        int r;
+    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+                    "pa_threaded_mainloop_lock failed\n");
 
-        while (!(l = pa_stream_writable_size (p->stream))) {
-            pa_threaded_mainloop_wait(c->mainloop);
-            CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
-        }
+    l = pa_stream_writable_size(p->stream);
 
-        CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
+    CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
+                       "pa_stream_writable_size failed\n");
 
-        if (l > length) {
-            l = length;
-        }
-
-        r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
-        CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
-
-        data = (const uint8_t *) data + l;
-        length -= l;
+    if (l > length) {
+        l = length;
     }
 
+    r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
+    CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
+
     pa_threaded_mainloop_unlock(c->mainloop);
-    return 0;
+    return l;
 
 unlock_and_fail:
     pa_threaded_mainloop_unlock(c->mainloop);
-    return -1;
-}
-
-static void *qpa_thread_out (void *arg)
-{
-    PAVoiceOut *pa = arg;
-    HWVoiceOut *hw = &pa->hw;
-
-    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
-        return NULL;
-    }
-
-    for (;;) {
-        int decr, to_mix, rpos;
-
-        for (;;) {
-            if (pa->done) {
-                goto exit;
-            }
-
-            if (pa->live > 0) {
-                break;
-            }
-
-            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
-                goto exit;
-            }
-        }
-
-        decr = to_mix = audio_MIN (pa->live, pa->samples >> 2);
-        rpos = pa->rpos;
-
-        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        while (to_mix) {
-            int error;
-            int chunk = audio_MIN (to_mix, hw->samples - rpos);
-            struct st_sample *src = hw->mix_buf + rpos;
-
-            hw->clip (pa->pcm_buf, src, chunk);
-
-            if (qpa_simple_write (pa, pa->pcm_buf,
-                                  chunk << hw->info.shift, &error) < 0) {
-                qpa_logerr (error, "pa_simple_write failed\n");
-                return NULL;
-            }
-
-            rpos = (rpos + chunk) % hw->samples;
-            to_mix -= chunk;
-        }
-
-        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        pa->rpos = rpos;
-        pa->live -= decr;
-        pa->decr += decr;
-    }
-
- exit:
-    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
-    return NULL;
-}
-
-static int qpa_run_out (HWVoiceOut *hw, int live)
-{
-    int decr;
-    PAVoiceOut *pa = (PAVoiceOut *) hw;
-
-    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
-        return 0;
-    }
-
-    decr = audio_MIN (live, pa->decr);
-    pa->decr -= decr;
-    pa->live = live - decr;
-    hw->rpos = pa->rpos;
-    if (pa->live > 0) {
-        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
-    }
-    else {
-        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
-    }
-    return decr;
-}
-
-static int qpa_write (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
-/* capture */
-static void *qpa_thread_in (void *arg)
-{
-    PAVoiceIn *pa = arg;
-    HWVoiceIn *hw = &pa->hw;
-
-    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
-        return NULL;
-    }
-
-    for (;;) {
-        int incr, to_grab, wpos;
-
-        for (;;) {
-            if (pa->done) {
-                goto exit;
-            }
-
-            if (pa->dead > 0) {
-                break;
-            }
-
-            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
-                goto exit;
-            }
-        }
-
-        incr = to_grab = audio_MIN (pa->dead, pa->samples >> 2);
-        wpos = pa->wpos;
-
-        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        while (to_grab) {
-            int error;
-            int chunk = audio_MIN (to_grab, hw->samples - wpos);
-            void *buf = advance (pa->pcm_buf, wpos);
-
-            if (qpa_simple_read (pa, buf,
-                                 chunk << hw->info.shift, &error) < 0) {
-                qpa_logerr (error, "pa_simple_read failed\n");
-                return NULL;
-            }
-
-            hw->conv (hw->conv_buf + wpos, buf, chunk);
-            wpos = (wpos + chunk) % hw->samples;
-            to_grab -= chunk;
-        }
-
-        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
-            return NULL;
-        }
-
-        pa->wpos = wpos;
-        pa->dead -= incr;
-        pa->incr += incr;
-    }
-
- exit:
-    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
-    return NULL;
-}
-
-static int qpa_run_in (HWVoiceIn *hw)
-{
-    int live, incr, dead;
-    PAVoiceIn *pa = (PAVoiceIn *) hw;
-
-    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
-        return 0;
-    }
-
-    live = audio_pcm_hw_get_live_in (hw);
-    dead = hw->samples - live;
-    incr = audio_MIN (dead, pa->incr);
-    pa->incr -= incr;
-    pa->dead = dead - incr;
-    hw->wpos = pa->wpos;
-    if (pa->dead > 0) {
-        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
-    }
-    else {
-        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
-    }
-    return incr;
-}
-
-static int qpa_read (SWVoiceIn *sw, void *buf, int len)
-{
-    return audio_pcm_sw_read (sw, buf, len);
+    return 0;
 }
 
 static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
@@ -476,13 +251,6 @@ static void stream_state_cb (pa_stream *s, void * userdata)
     }
 }
 
-static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
-{
-    PAConnection *c = userdata;
-
-    pa_threaded_mainloop_signal(c->mainloop, 0);
-}
-
 static pa_stream *qpa_simple_new (
         PAConnection *c,
         const char *name,
@@ -505,8 +273,6 @@ static pa_stream *qpa_simple_new (
     }
 
     pa_stream_set_state_callback (stream, stream_state_cb, c);
-    pa_stream_set_read_callback (stream, stream_request_cb, c);
-    pa_stream_set_write_callback (stream, stream_request_cb, c);
 
     flags =
         PA_STREAM_INTERPOLATE_TIMING
@@ -587,34 +353,21 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = pa->samples = audio_buffer_samples(g->dev->out, &obt_as,
-                                                     46440);
-    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
-    pa->rpos = hw->rpos;
-    if (!pa->pcm_buf) {
-        dolog ("Could not allocate buffer (%d bytes)\n",
-               hw->samples << hw->info.shift);
-        goto fail2;
-    }
-
-    if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
-        goto fail3;
-    }
+    pa->samples = audio_buffer_samples(g->dev->out, &obt_as, 46440);
 
     return 0;
 
- fail3:
-    g_free (pa->pcm_buf);
-    pa->pcm_buf = NULL;
- fail2:
-    if (pa->stream) {
-        pa_stream_unref (pa->stream);
-        pa->stream = NULL;
-    }
  fail1:
     return -1;
 }
 
+static size_t qpa_buffer_size_out(HWVoiceOut *hw)
+{
+    PAVoiceOut *pa = (PAVoiceOut *) hw;
+    return pa->samples;
+}
+
+
 static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
     int error;
@@ -648,34 +401,20 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     }
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = pa->samples = audio_buffer_samples(g->dev->in, &obt_as,
-                                                     46440);
-    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
-    pa->wpos = hw->wpos;
-    if (!pa->pcm_buf) {
-        dolog ("Could not allocate buffer (%d bytes)\n",
-               hw->samples << hw->info.shift);
-        goto fail2;
-    }
-
-    if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
-        goto fail3;
-    }
+    pa->samples = audio_buffer_samples(g->dev->in, &obt_as, 46440);
 
     return 0;
 
- fail3:
-    g_free (pa->pcm_buf);
-    pa->pcm_buf = NULL;
- fail2:
-    if (pa->stream) {
-        pa_stream_unref (pa->stream);
-        pa->stream = NULL;
-    }
  fail1:
     return -1;
 }
 
+static size_t qpa_buffer_size_in(HWVoiceIn *hw)
+{
+    PAVoiceIn *pa = (PAVoiceIn *) hw;
+    return pa->samples;
+}
+
 static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
 {
     int err;
@@ -697,42 +436,22 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
 
 static void qpa_fini_out (HWVoiceOut *hw)
 {
-    void *ret;
     PAVoiceOut *pa = (PAVoiceOut *) hw;
 
-    audio_pt_lock (&pa->pt, AUDIO_FUNC);
-    pa->done = 1;
-    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
-    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
-
     if (pa->stream) {
         qpa_simple_disconnect(pa->g->conn, pa->stream);
         pa->stream = NULL;
     }
-
-    audio_pt_fini (&pa->pt, AUDIO_FUNC);
-    g_free (pa->pcm_buf);
-    pa->pcm_buf = NULL;
 }
 
 static void qpa_fini_in (HWVoiceIn *hw)
 {
-    void *ret;
     PAVoiceIn *pa = (PAVoiceIn *) hw;
 
-    audio_pt_lock (&pa->pt, AUDIO_FUNC);
-    pa->done = 1;
-    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
-    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
-
     if (pa->stream) {
         qpa_simple_disconnect(pa->g->conn, pa->stream);
         pa->stream = NULL;
     }
-
-    audio_pt_fini (&pa->pt, AUDIO_FUNC);
-    g_free (pa->pcm_buf);
-    pa->pcm_buf = NULL;
 }
 
 static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
@@ -971,14 +690,14 @@ static void qpa_audio_fini (void *opaque)
 static struct audio_pcm_ops qpa_pcm_ops = {
     .init_out = qpa_init_out,
     .fini_out = qpa_fini_out,
-    .run_out  = qpa_run_out,
     .write    = qpa_write,
+    .buffer_size_out = qpa_buffer_size_out,
     .ctl_out  = qpa_ctl_out,
 
     .init_in  = qpa_init_in,
     .fini_in  = qpa_fini_in,
-    .run_in   = qpa_run_in,
     .read     = qpa_read,
+    .buffer_size_in = qpa_buffer_size_in,
     .ctl_in   = qpa_ctl_in
 };
 
diff --git a/audio/rate_template.h b/audio/rate_template.h
index bd4b1c7..dc5e021 100644
--- a/audio/rate_template.h
+++ b/audio/rate_template.h
@@ -28,7 +28,7 @@
  * Return number of samples processed.
  */
 void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
-           int *isamp, int *osamp)
+           size_t *isamp, size_t *osamp)
 {
     struct rate *rate = opaque;
     struct st_sample *istart, *iend;
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index 796238a..ee5ab92 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -39,9 +39,7 @@
 
 typedef struct SDLVoiceOut {
     HWVoiceOut hw;
-    int live;
-    int rpos;
-    int decr;
+    size_t samples;
 } SDLVoiceOut;
 
 static struct SDLAudioState {
@@ -230,16 +228,12 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
     SDLVoiceOut *sdl = opaque;
     SDLAudioState *s = &glob_sdl;
     HWVoiceOut *hw = &sdl->hw;
-    int samples = len >> hw->info.shift;
 
     if (s->exit) {
         return;
     }
 
-    while (samples) {
-        int to_mix, decr;
-
-        /* dolog ("in callback samples=%d\n", samples); */
+    while (len) {
         sdl_wait (s, "sdl_callback");
         if (s->exit) {
             return;
@@ -249,34 +243,23 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
             return;
         }
 
-        if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
-            dolog ("sdl->live=%d hw->samples=%d\n",
-                   sdl->live, hw->samples);
-            return;
-        }
-
-        if (!sdl->live) {
-            goto again;
-        }
+        while (hw->pending_emul && len) {
+            size_t write_len;
+            ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+            if (start < 0) {
+                start += hw->size_emul;
+            }
+            assert(start >= 0 && start < hw->size_emul);
 
-        /* dolog ("in callback live=%d\n", live); */
-        to_mix = audio_MIN (samples, sdl->live);
-        decr = to_mix;
-        while (to_mix) {
-            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
-            struct st_sample *src = hw->mix_buf + hw->rpos;
+            write_len = audio_MIN(audio_MIN(hw->pending_emul, len),
+                                  hw->size_emul - start);
 
-            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
-            hw->clip (buf, src, chunk);
-            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
-            to_mix -= chunk;
-            buf += chunk << hw->info.shift;
+            memcpy(buf, hw->buf_emul + start, write_len);
+            hw->pending_emul -= write_len;
+            len -= write_len;
+            buf += write_len;
         }
-        samples -= decr;
-        sdl->live -= decr;
-        sdl->decr += decr;
 
-    again:
         if (sdl_unlock (s, "sdl_callback")) {
             return;
         }
@@ -284,42 +267,33 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
     /* dolog ("done len=%d\n", len); */
 }
 
-static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
-}
-
-static int sdl_run_out (HWVoiceOut *hw, int live)
-{
-    int decr;
-    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
-    SDLAudioState *s = &glob_sdl;
-
-    if (sdl_lock (s, "sdl_run_out")) {
-        return 0;
-    }
-
-    if (sdl->decr > live) {
-        ldebug ("sdl->decr %d live %d sdl->live %d\n",
-                sdl->decr,
-                live,
-                sdl->live);
+#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \
+    static ret_type glue(sdl_, name)args_decl                           \
+    {                                                                   \
+        SDLAudioState *s = &glob_sdl;                                   \
+        ret_type ret;                                                   \
+                                                                        \
+        if (sdl_lock(s, "sdl_" #name)) {                                \
+            fail;                                                       \
+            return 0; /* implicitly casts to NULL */                    \
+        }                                                               \
+                                                                        \
+        ret = glue(audio_generic_, name)args;                           \
+                                                                        \
+        unlock(s, "sdl_" #name);                                        \
+        return ret;                                                     \
     }
 
-    decr = audio_MIN (sdl->decr, live);
-    sdl->decr -= decr;
+SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
+                 (hw, size), *size = 0, sdl_unlock)
+SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
+                 (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
+                 /*nothing*/, sdl_unlock_and_post)
+SDL_WRAPPER_FUNC(write, size_t,
+                 (HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
+                 /* nothing */, sdl_unlock_and_post)
 
-    sdl->live = live - decr;
-    hw->rpos = sdl->rpos;
-
-    if (sdl->live > 0) {
-        sdl_unlock_and_post (s, "sdl_run_out");
-    }
-    else {
-        sdl_unlock (s, "sdl_run_out");
-    }
-    return decr;
-}
+#undef SDL_WRAPPER_FUNC
 
 static void sdl_fini_out (HWVoiceOut *hw)
 {
@@ -362,7 +336,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     obt_as.endianness = endianness;
 
     audio_pcm_init_info (&hw->info, &obt_as);
-    hw->samples = obt.samples;
+    sdl->samples = obt.samples;
 
     s->initialized = 1;
     s->exit = 0;
@@ -370,6 +344,12 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
     return 0;
 }
 
+static size_t sdl_buffer_size_out(HWVoiceOut *hw)
+{
+    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+    return sdl->samples;
+}
+
 static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     (void) hw;
@@ -433,8 +413,10 @@ static void sdl_audio_fini (void *opaque)
 static struct audio_pcm_ops sdl_pcm_ops = {
     .init_out = sdl_init_out,
     .fini_out = sdl_fini_out,
-    .run_out  = sdl_run_out,
-    .write    = sdl_write_out,
+    .write    = sdl_write,
+    .buffer_size_out = sdl_buffer_size_out,
+    .get_buffer_out = sdl_get_buffer_out,
+    .put_buffer_out = sdl_put_buffer_out_nowrite,
     .ctl_out  = sdl_ctl_out,
 };
 
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index a9b9a1d..cae4b02 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -49,7 +49,7 @@ typedef struct SpiceVoiceOut {
     SpiceRateCtl          rate;
     int                   active;
     uint32_t              *frame;
-    uint32_t              *fpos;
+    uint32_t              fpos;
     uint32_t              fsize;
 } SpiceVoiceOut;
 
@@ -58,7 +58,6 @@ typedef struct SpiceVoiceIn {
     SpiceRecordInstance   sin;
     SpiceRateCtl          rate;
     int                   active;
-    uint32_t              samples[LINE_IN_SAMPLES];
 } SpiceVoiceIn;
 
 static const SpicePlaybackInterface playback_sif = {
@@ -132,7 +131,6 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
     settings.endianness = AUDIO_HOST_ENDIANNESS;
 
     audio_pcm_init_info (&hw->info, &settings);
-    hw->samples = LINE_OUT_SAMPLES;
     out->active = 0;
 
     out->sin.base.sif = &playback_sif.base;
@@ -143,6 +141,11 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
     return 0;
 }
 
+static size_t line_out_buffer_size(HWVoiceOut *hw)
+{
+    return LINE_OUT_SAMPLES;
+}
+
 static void line_out_fini (HWVoiceOut *hw)
 {
     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
@@ -150,49 +153,36 @@ static void line_out_fini (HWVoiceOut *hw)
     spice_server_remove_interface (&out->sin.base);
 }
 
-static int line_out_run (HWVoiceOut *hw, int live)
+static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
 {
-    SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
-    int rpos, decr;
-    int samples;
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+    size_t decr;
 
-    if (!live) {
-        return 0;
+    if (!out->frame) {
+        spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
+        out->fpos = 0;
     }
 
-    decr = rate_get_samples (&hw->info, &out->rate);
-    decr = audio_MIN (live, decr);
+    decr = rate_get_samples(&hw->info, &out->rate);
+    decr = audio_MIN(out->fsize - out->fpos, decr);
 
-    samples = decr;
-    rpos = hw->rpos;
-    while (samples) {
-        int left_till_end_samples = hw->samples - rpos;
-        int len = audio_MIN (samples, left_till_end_samples);
-
-        if (!out->frame) {
-            spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
-            out->fpos = out->frame;
-        }
-        if (out->frame) {
-            len = audio_MIN (len, out->fsize);
-            hw->clip (out->fpos, hw->mix_buf + rpos, len);
-            out->fsize -= len;
-            out->fpos  += len;
-            if (out->fsize == 0) {
-                spice_server_playback_put_samples (&out->sin, out->frame);
-                out->frame = out->fpos = NULL;
-            }
-        }
-        rpos = (rpos + len) % hw->samples;
-        samples -= len;
-    }
-    hw->rpos = rpos;
-    return decr;
+    *size = decr << 2;
+    return out->frame + out->fpos;
 }
 
-static int line_out_write (SWVoiceOut *sw, void *buf, int len)
+static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
 {
-    return audio_pcm_sw_write (sw, buf, len);
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+
+    out->fpos += size >> 2;
+    assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
+
+    if (out->fpos == out->fsize) { /* buffer full */
+        spice_server_playback_put_samples(&out->sin, out->frame);
+        out->frame = NULL;
+    }
+
+    return size;
 }
 
 static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
@@ -214,9 +204,9 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
         }
         out->active = 0;
         if (out->frame) {
-            memset (out->fpos, 0, out->fsize << 2);
+            memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2);
             spice_server_playback_put_samples (&out->sin, out->frame);
-            out->frame = out->fpos = NULL;
+            out->frame = NULL;
         }
         spice_server_playback_stop (&out->sin);
         break;
@@ -260,7 +250,6 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     settings.endianness = AUDIO_HOST_ENDIANNESS;
 
     audio_pcm_init_info (&hw->info, &settings);
-    hw->samples = LINE_IN_SAMPLES;
     in->active = 0;
 
     in->sin.base.sif = &record_sif.base;
@@ -271,6 +260,11 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
     return 0;
 }
 
+static size_t line_in_buffer_size(HWVoiceIn *hw)
+{
+    return LINE_IN_SAMPLES;
+}
+
 static void line_in_fini (HWVoiceIn *hw)
 {
     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
@@ -278,54 +272,20 @@ static void line_in_fini (HWVoiceIn *hw)
     spice_server_remove_interface (&in->sin.base);
 }
 
-static int line_in_run (HWVoiceIn *hw)
+static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
 {
     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
-    int num_samples;
-    int ready;
-    int len[2];
-    uint64_t delta_samp;
-    const uint32_t *samples;
+    uint64_t delta_samp = rate_get_samples(&hw->info, &in->rate);
+    uint64_t to_read = audio_MIN(len >> 2, delta_samp);
+    size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
 
-    if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
-        return 0;
-    }
-
-    delta_samp = rate_get_samples (&hw->info, &in->rate);
-    num_samples = audio_MIN (num_samples, delta_samp);
-
-    ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
-    samples = in->samples;
+    /* XXX: do we need this? */
     if (ready == 0) {
-        static const uint32_t silence[LINE_IN_SAMPLES];
-        samples = silence;
-        ready = LINE_IN_SAMPLES;
+        memset(buf, 0, to_read << 2);
+        ready = to_read;
     }
 
-    num_samples = audio_MIN (ready, num_samples);
-
-    if (hw->wpos + num_samples > hw->samples) {
-        len[0] = hw->samples - hw->wpos;
-        len[1] = num_samples - len[0];
-    } else {
-        len[0] = num_samples;
-        len[1] = 0;
-    }
-
-    hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
-
-    if (len[1]) {
-        hw->conv (hw->conv_buf, samples + len[0], len[1]);
-    }
-
-    hw->wpos = (hw->wpos + num_samples) % hw->samples;
-
-    return num_samples;
-}
-
-static int line_in_read (SWVoiceIn *sw, void *buf, int size)
-{
-    return audio_pcm_sw_read (sw, buf, size);
+    return ready << 2;
 }
 
 static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
@@ -374,14 +334,16 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
 static struct audio_pcm_ops audio_callbacks = {
     .init_out = line_out_init,
     .fini_out = line_out_fini,
-    .run_out  = line_out_run,
-    .write    = line_out_write,
+    .write    = audio_generic_write,
+    .buffer_size_out = line_out_buffer_size,
+    .get_buffer_out = line_out_get_buffer,
+    .put_buffer_out = line_out_put_buffer,
     .ctl_out  = line_out_ctl,
 
     .init_in  = line_in_init,
     .fini_in  = line_in_fini,
-    .run_in   = line_in_run,
     .read     = line_in_read,
+    .buffer_size_in = line_in_buffer_size,
     .ctl_in   = line_in_ctl,
 };
 
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 1af6d23..cabbd76 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -33,57 +33,27 @@ typedef struct WAVVoiceOut {
     HWVoiceOut hw;
     FILE *f;
     int64_t old_ticks;
-    void *pcm_buf;
     int total_samples;
 } WAVVoiceOut;
 
-static int wav_run_out (HWVoiceOut *hw, int live)
+static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
-    int rpos, decr, samples;
-    uint8_t *dst;
-    struct st_sample *src;
     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     int64_t ticks = now - wav->old_ticks;
     int64_t bytes =
         muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
-
-    if (bytes > INT_MAX) {
-        samples = INT_MAX >> hw->info.shift;
-    }
-    else {
-        samples = bytes >> hw->info.shift;
-    }
-
+    bytes = audio_MIN(bytes, len);
+    bytes = bytes >> hw->info.shift << hw->info.shift;
     wav->old_ticks = now;
-    decr = audio_MIN (live, samples);
-    samples = decr;
-    rpos = hw->rpos;
-    while (samples) {
-        int left_till_end_samples = hw->samples - rpos;
-        int convert_samples = audio_MIN (samples, left_till_end_samples);
 
-        src = hw->mix_buf + rpos;
-        dst = advance (wav->pcm_buf, rpos << hw->info.shift);
-
-        hw->clip (dst, src, convert_samples);
-        if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
-            dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
-                   convert_samples << hw->info.shift, strerror (errno));
-        }
-
-        rpos = (rpos + convert_samples) % hw->samples;
-        samples -= convert_samples;
-        wav->total_samples += convert_samples;
+    if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
+        dolog("wav_write_out: fwrite of %zu bytes failed\nReaons: %s\n",
+              bytes, strerror(errno));
     }
 
-    hw->rpos = rpos;
-    return decr;
-}
-
-static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
-{
-    return audio_pcm_sw_write (sw, buf, len);
+    wav->total_samples += bytes >> hw->info.shift;
+    return bytes;
 }
 
 /* VICE code: Store number as little endian. */
@@ -138,14 +108,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
     wav_as.endianness = 0;
     audio_pcm_init_info (&hw->info, &wav_as);
 
-    hw->samples = 1024;
-    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
-    if (!wav->pcm_buf) {
-        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 + stereo), 4);
@@ -155,8 +117,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
     if (!wav->f) {
         dolog ("Failed to open wave file `%s'\nReason: %s\n",
                wav_path, strerror(errno));
-        g_free (wav->pcm_buf);
-        wav->pcm_buf = NULL;
         return -1;
     }
 
@@ -210,9 +170,6 @@ static void wav_fini_out (HWVoiceOut *hw)
                wav->f, strerror (errno));
     }
     wav->f = NULL;
-
-    g_free (wav->pcm_buf);
-    wav->pcm_buf = NULL;
 }
 
 static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
@@ -236,7 +193,6 @@ static void wav_audio_fini (void *opaque)
 static struct audio_pcm_ops wav_pcm_ops = {
     .init_out = wav_init_out,
     .fini_out = wav_fini_out,
-    .run_out  = wav_run_out,
     .write    = wav_write_out,
     .ctl_out  = wav_ctl_out,
 };
diff --git a/configure b/configure
index cd219d8..16bda1d 100755
--- a/configure
+++ b/configure
@@ -205,7 +205,6 @@ block_drv_ro_whitelist=""
 host_cc="cc"
 libs_softmmu=""
 libs_tools=""
-audio_pt_int=""
 audio_win_int=""
 cc_i386=i386-pc-linux-gnu-gcc
 libs_qga=""
@@ -2643,7 +2642,6 @@ for drv in $audio_drv_list; do
     audio_drv_probe $drv pulse/mainloop.h "-lpulse" \
         "pa_mainloop *m = 0; pa_mainloop_free (m); return 0;"
     libs_softmmu="-lpulse $libs_softmmu"
-    audio_pt_int="yes"
     ;;
 
     coreaudio)
@@ -4713,9 +4711,6 @@ for drv in $audio_drv_list; do
     def=CONFIG_`echo $drv | LC_ALL=C tr '[a-z]' '[A-Z]'`
     echo "$def=y" >> $config_host_mak
 done
-if test "$audio_pt_int" = "yes" ; then
-  echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
-fi
 if test "$audio_win_int" = "yes" ; then
   echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
 fi
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (19 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 22:37   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation) Kővágó, Zoltán
                   ` (3 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This commit removes the ad-hoc rate-limiting code from noaudio and
wavaudio, and replaces them with a (slightly modified) code from
spiceaudio.  This way multiple write calls (for example when the
circular buffer wraps around) do not cause problems.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.c      | 30 ++++++++++++++++++++++++++++++
 audio/audio_int.h  |  9 +++++++++
 audio/noaudio.c    | 49 ++++++++++++++++++++++---------------------------
 audio/spiceaudio.c | 49 +++++++------------------------------------------
 audio/wavaudio.c   | 20 ++++++++++----------
 5 files changed, 78 insertions(+), 79 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 6ef40e9..94ffd72 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1958,3 +1958,33 @@ const char *audio_get_id(QEMUSoundCard *card)
         return "";
     }
 }
+
+void audio_rate_start(RateCtl *rate)
+{
+    memset(rate, 0, sizeof(RateCtl));
+    rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
+                            size_t bytes_avail)
+{
+    int64_t now;
+    int64_t ticks;
+    int64_t bytes;
+    int64_t samples;
+    size_t ret;
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    ticks = now - rate->start_ticks;
+    bytes = muldiv64(ticks, info->bytes_per_second, get_ticks_per_sec());
+    samples = (bytes - rate->bytes_sent) >> info->shift;
+    if (samples < 0 || samples > 65536) {
+        AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)", samples);
+        audio_rate_start(rate);
+        samples = 0;
+    }
+
+    ret = audio_MIN(samples << info->shift, bytes_avail);
+    rate->bytes_sent += ret;
+    return ret;
+}
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 1250012..16ffa12 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -238,6 +238,15 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size);
 
 void audio_run(AudioState *s, const char *msg);
 
+typedef struct RateCtl {
+    int64_t start_ticks;
+    int64_t bytes_sent;
+} RateCtl;
+
+void audio_rate_start(RateCtl *rate);
+size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
+                            size_t bytes_avail);
+
 #define VOICE_ENABLE 1
 #define VOICE_DISABLE 2
 #define VOICE_VOLUME 3
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 13c1263..3cb3a89 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -30,32 +30,26 @@
 
 typedef struct NoVoiceOut {
     HWVoiceOut hw;
-    int64_t old_ticks;
+    RateCtl rate;
 } NoVoiceOut;
 
 typedef struct NoVoiceIn {
     HWVoiceIn hw;
-    int64_t old_ticks;
+    RateCtl rate;
 } NoVoiceIn;
 
 static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
 {
     NoVoiceOut *no = (NoVoiceOut *) hw;
-    int64_t now;
-    int64_t ticks;
-    int64_t bytes;
-
-    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    ticks = now - no->old_ticks;
-    bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
-
-    no->old_ticks = now;
-    return audio_MIN(len, bytes);
+    return audio_rate_get_bytes(&hw->info, &no->rate, len);
 }
 
 static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
 {
+    NoVoiceOut *no = (NoVoiceOut *) hw;
+
     audio_pcm_init_info (&hw->info, as);
+    audio_rate_start(&no->rate);
     return 0;
 }
 
@@ -66,14 +60,20 @@ static void no_fini_out (HWVoiceOut *hw)
 
 static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
-    (void) hw;
-    (void) cmd;
+    NoVoiceOut *no = (NoVoiceOut *) hw;
+
+    if (cmd == VOICE_ENABLE) {
+        audio_rate_start(&no->rate);
+    }
     return 0;
 }
 
 static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
 {
+    NoVoiceIn *no = (NoVoiceIn *) hw;
+
     audio_pcm_init_info (&hw->info, as);
+    audio_rate_start(&no->rate);
     return 0;
 }
 
@@ -84,25 +84,20 @@ static void no_fini_in (HWVoiceIn *hw)
 
 static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
 {
-    size_t to_clear;
     NoVoiceIn *no = (NoVoiceIn *) hw;
+    int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
 
-    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    int64_t ticks = now - no->old_ticks;
-    int64_t bytes =
-        muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
-
-    no->old_ticks = now;
-    to_clear = audio_MIN(bytes, size);
-
-    audio_pcm_info_clear_buf(&hw->info, buf, to_clear >> hw->info.shift);
-    return to_clear;
+    audio_pcm_info_clear_buf(&hw->info, buf, bytes >> hw->info.shift);
+    return bytes;
 }
 
 static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
-    (void) hw;
-    (void) cmd;
+    NoVoiceIn *no = (NoVoiceIn *) hw;
+
+    if (cmd == VOICE_ENABLE) {
+        audio_rate_start(&no->rate);
+    }
     return 0;
 }
 
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index cae4b02..a7faed8 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -38,15 +38,10 @@
 #define LINE_IN_SAMPLES (256 * 4)
 #endif
 
-typedef struct SpiceRateCtl {
-    int64_t               start_ticks;
-    int64_t               bytes_sent;
-} SpiceRateCtl;
-
 typedef struct SpiceVoiceOut {
     HWVoiceOut            hw;
     SpicePlaybackInstance sin;
-    SpiceRateCtl          rate;
+    RateCtl               rate;
     int                   active;
     uint32_t              *frame;
     uint32_t              fpos;
@@ -56,7 +51,7 @@ typedef struct SpiceVoiceOut {
 typedef struct SpiceVoiceIn {
     HWVoiceIn             hw;
     SpiceRecordInstance   sin;
-    SpiceRateCtl          rate;
+    RateCtl               rate;
     int                   active;
 } SpiceVoiceIn;
 
@@ -87,32 +82,6 @@ static void spice_audio_fini (void *opaque)
     /* nothing */
 }
 
-static void rate_start (SpiceRateCtl *rate)
-{
-    memset (rate, 0, sizeof (*rate));
-    rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-}
-
-static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
-{
-    int64_t now;
-    int64_t ticks;
-    int64_t bytes;
-    int64_t samples;
-
-    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    ticks = now - rate->start_ticks;
-    bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
-    samples = (bytes - rate->bytes_sent) >> info->shift;
-    if (samples < 0 || samples > 65536) {
-        error_report("Resetting rate control (%" PRId64 " samples)", samples);
-        rate_start (rate);
-        samples = 0;
-    }
-    rate->bytes_sent += samples << info->shift;
-    return samples;
-}
-
 /* playback */
 
 static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
@@ -156,17 +125,14 @@ static void line_out_fini (HWVoiceOut *hw)
 static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
 {
     SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
-    size_t decr;
 
     if (!out->frame) {
         spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
         out->fpos = 0;
     }
 
-    decr = rate_get_samples(&hw->info, &out->rate);
-    decr = audio_MIN(out->fsize - out->fpos, decr);
-
-    *size = decr << 2;
+    *size = audio_rate_get_bytes(&hw->info, &out->rate,
+                                 (out->fsize - out->fpos) << 2);
     return out->frame + out->fpos;
 }
 
@@ -195,7 +161,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
             break;
         }
         out->active = 1;
-        rate_start (&out->rate);
+        audio_rate_start(&out->rate);
         spice_server_playback_start (&out->sin);
         break;
     case VOICE_DISABLE:
@@ -275,8 +241,7 @@ static void line_in_fini (HWVoiceIn *hw)
 static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
 {
     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
-    uint64_t delta_samp = rate_get_samples(&hw->info, &in->rate);
-    uint64_t to_read = audio_MIN(len >> 2, delta_samp);
+    uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
     size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
 
     /* XXX: do we need this? */
@@ -298,7 +263,7 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
             break;
         }
         in->active = 1;
-        rate_start (&in->rate);
+        audio_rate_start(&in->rate);
         spice_server_record_start (&in->sin);
         break;
     case VOICE_DISABLE:
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index cabbd76..14afaaf 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -32,20 +32,15 @@
 typedef struct WAVVoiceOut {
     HWVoiceOut hw;
     FILE *f;
-    int64_t old_ticks;
+    RateCtl rate;
     int total_samples;
 } WAVVoiceOut;
 
 static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
-    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
-    int64_t ticks = now - wav->old_ticks;
-    int64_t bytes =
-        muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
-    bytes = audio_MIN(bytes, len);
-    bytes = bytes >> hw->info.shift << hw->info.shift;
-    wav->old_ticks = now;
+    int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
+    assert(bytes >> hw->info.shift << hw->info.shift == bytes);
 
     if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
         dolog("wav_write_out: fwrite of %zu bytes failed\nReaons: %s\n",
@@ -125,6 +120,8 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
                strerror(errno));
         return -1;
     }
+
+    audio_rate_start(&wav->rate);
     return 0;
 }
 
@@ -174,8 +171,11 @@ static void wav_fini_out (HWVoiceOut *hw)
 
 static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
-    (void) hw;
-    (void) cmd;
+    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+
+    if (cmd == VOICE_ENABLE) {
+        audio_rate_start(&wav->rate);
+    }
     return 0;
 }
 
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation)
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (20 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 22:49   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 23/25] audio: make mixeng optional Kővágó, Zoltán
                   ` (2 subsequent siblings)
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Markus Armbruster

This will allow us to disable mixeng when we use a decent backend.

Disabling mixeng have a few advantages:
* we no longer convert the audio output from one format to another, when
  the underlying audio system would just convert it to a third format.
  We no longer convert, only the underlying system, when needed.
* the underlying system probably has better resampling and sample format
  converting methods anyway...
* we may support formats that the mixeng currently does not support (S24
  or float samples, more than two channels)
* when using an audio server (like pulseaudio) different sound card
  outputs will show up as separate streams, even if we use only one
  backend

Disadvantages:
* audio capturing no longer works (wavcapture, and vnc audio extension)
* some backends only support a single playback stream or very picky
  about the audio format.  In this case we can't disable mixeng.

However mixeng is not removed, only made optional, so this shouldn't be
a big concern.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 qapi/audio.json | 5 +++++
 qemu-options.hx | 6 ++++++
 2 files changed, 11 insertions(+)

diff --git a/qapi/audio.json b/qapi/audio.json
index b57b215..0216a10 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -184,6 +184,10 @@
 #
 # General audio backend options that are used for both playback and recording.
 #
+# @mixeng: #optional use QEMU's mixing engine to mix all streams inside QEMU.
+#          When set to off, fixed-settings must be also off.  Not every backend
+#          compatible with the off setting (default on)
+#
 # @fixed-settings: #optional use fixed settings for host input/output.  When
 #                  off, frequency, channels and format must not be specified
 #                  (default on)
@@ -207,6 +211,7 @@
 ##
 { 'struct': 'AudiodevPerDirectionOptions',
   'data': {
+    '*mixeng':         'bool',
     '*fixed-settings': 'bool',
     '*frequency':      'int',
     '*channels':       'int',
diff --git a/qemu-options.hx b/qemu-options.hx
index 60a3563..bd922bf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -329,6 +329,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
     "                specifies the audio backend to use\n"
     "                id= identifier of the backend\n"
     "                timer-period= timer period in microseconds\n"
+    "                in|out.mixeng= use mixeng to mix streams inside QEMU\n"
     "                in|out.fixed-settings= use fixed settings for host audio\n"
     "                in|out.frequency= frequency to use with fixed settings\n"
     "                in|out.channels= number of channels to use with fixed settings\n"
@@ -397,6 +398,11 @@ Identifies the audio backend.
 Sets the timer @var{period} used by the audio subsystem in microseconds.
 Default is 10000 (10 ms).
 
+@item in|out.mixeng=on|off
+Use QEMU's mixing engine to mix all streams inside QEMU.  When off,
+@var{fixed-settings} must be off too.  Not every backend is fully
+compatible with the off setting.  Default is on.
+
 @item in|out.fixed-settings=on|off
 Use fixed settings for host audio.  When off, it will change based on
 how the guest opens the sound card.  In this case you must not specify
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 23/25] audio: make mixeng optional
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (21 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation) Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 22:56   ` Marc-André Lureau
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 24/25] paaudio: get/put_buffer functions Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 25/25] audio: split ctl_* functions into enable_* and volume_* Kővágó, Zoltán
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

Implementation of the previously added mixeng option.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/audio.c          | 70 +++++++++++++++++++++++++++++++++++++++++++++-----
 audio/audio_template.h | 22 +++++++++++-----
 2 files changed, 79 insertions(+), 13 deletions(-)

diff --git a/audio/audio.c b/audio/audio.c
index 94ffd72..ee02b5c 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -794,32 +794,46 @@ static void audio_timer (void *opaque)
  */
 size_t AUD_write(SWVoiceOut *sw, void *buf, size_t size)
 {
+    HWVoiceOut *hw;
+
     if (!sw) {
         /* XXX: Consider options */
         return size;
     }
+    hw = sw->hw;
 
-    if (!sw->hw->enabled) {
+    if (!hw->enabled) {
         dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
         return 0;
     }
 
-    return audio_pcm_sw_write(sw, buf, size);
+    if (hw->s->dev->out->mixeng) {
+        return audio_pcm_sw_write(sw, buf, size);
+    } else {
+        return hw->pcm_ops->write(hw, buf, size);
+    }
 }
 
 size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
 {
+    HWVoiceIn *hw;
+
     if (!sw) {
         /* XXX: Consider options */
         return size;
     }
+    hw = sw->hw;
 
-    if (!sw->hw->enabled) {
+    if (!hw->enabled) {
         dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
         return 0;
     }
 
-    return audio_pcm_sw_read(sw, buf, size);
+    if (hw->s->dev->in->mixeng) {
+        return audio_pcm_sw_read(sw, buf, size);
+    } else {
+        return hw->pcm_ops->read(hw, buf, size);
+    }
 }
 
 int AUD_get_buffer_size_out (SWVoiceOut *sw)
@@ -1033,6 +1047,26 @@ static void audio_run_out (AudioState *s)
     HWVoiceOut *hw = NULL;
     SWVoiceOut *sw;
 
+    if (!s->dev->out->mixeng) {
+        while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
+            /* there is exactly 1 sw for each hw with no mixeng */
+            sw = hw->sw_head.lh_first;
+
+            if (hw->pending_disable) {
+                hw->enabled = 0;
+                hw->pending_disable = 0;
+                if (hw->pcm_ops->enable_out) {
+                    hw->pcm_ops->enable_out(hw, false);
+                }
+            }
+
+            if (sw->active) {
+                sw->callback.fn(sw->callback.opaque, INT_MAX);
+            }
+        }
+        return;
+    }
+
     while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
         size_t played, live, prev_rpos, free;
         int nb_live, cleanup_required;
@@ -1167,6 +1201,17 @@ static void audio_run_in (AudioState *s)
 {
     HWVoiceIn *hw = NULL;
 
+    if (!s->dev->in->mixeng) {
+        while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
+            /* there is exactly 1 sw for each hw with no mixeng */
+            SWVoiceIn *sw = hw->sw_head.lh_first;
+            if (sw->active) {
+                sw->callback.fn(sw->callback.opaque, INT_MAX);
+            }
+        }
+        return;
+    }
+
     while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
         SWVoiceIn *sw;
         size_t captured, min;
@@ -1643,6 +1688,11 @@ CaptureVoiceOut *AUD_add_capture(
         s = QTAILQ_FIRST(&audio_states);
     }
 
+    if (!s->dev->out->mixeng) {
+        dolog("Can't capture with mixeng disabled\n");
+        goto err0;
+    }
+
     if (audio_validate_settings (as)) {
         dolog ("Invalid settings were passed when trying to add capture\n");
         audio_print_settings (as);
@@ -1800,9 +1850,13 @@ QemuOptsList qemu_audiodev_opts = {
 static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo,
                                         Error **errp)
 {
+    if (!pdo->has_mixeng) {
+        pdo->has_mixeng = true;
+        pdo->mixeng = true;
+    }
     if (!pdo->has_fixed_settings) {
         pdo->has_fixed_settings = true;
-        pdo->fixed_settings = true;
+        pdo->fixed_settings = pdo->mixeng;
     }
     if (!pdo->fixed_settings &&
         (pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
@@ -1810,6 +1864,10 @@ static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo,
                    "You can't use frequency, channels or format with fixed-settings=off");
         return;
     }
+    if (!pdo->mixeng && pdo->fixed_settings) {
+        error_setg(errp, "You can't use fixed-settings without mixeng");
+        return;
+    }
 
     if (!pdo->has_frequency) {
         pdo->has_frequency = true;
@@ -1821,7 +1879,7 @@ static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo,
     }
     if (!pdo->has_voices) {
         pdo->has_voices = true;
-        pdo->voices = 1;
+        pdo->voices = pdo->mixeng ? 1 : INT_MAX;
     }
     if (!pdo->has_format) {
         pdo->has_format = true;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 473eac9..942d9a4 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -78,15 +78,19 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
 
 static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
 {
-    size_t samples;
-    if (hw->pcm_ops->glue(buffer_size_, TYPE)) {
-        samples = hw->pcm_ops->glue(buffer_size_, TYPE)(hw);
+    if (hw->s->dev->TYPE->mixeng) {
+        size_t samples;
+        if (hw->pcm_ops->glue(buffer_size_, TYPE)) {
+            samples = hw->pcm_ops->glue(buffer_size_, TYPE)(hw);
+        } else {
+            samples = 1024; /* todo better default */
+        }
+
+        HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample)*samples);
+        HWBUF->size = samples;
     } else {
-        samples = 1024; /* todo better default */
+        HWBUF = NULL;
     }
-
-    HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample)*samples);
-    HWBUF->size = samples;
 }
 
 static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@@ -105,6 +109,10 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
 {
     int samples;
 
+    if (!sw->s->dev->TYPE->mixeng) {
+        return 0;
+    }
+
     samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
 
     sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 24/25] paaudio: get/put_buffer functions
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (22 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 23/25] audio: make mixeng optional Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 25/25] audio: split ctl_* functions into enable_* and volume_* Kővágó, Zoltán
  24 siblings, 0 replies; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This lets us avoid some buffer copying when using mixeng.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/paaudio.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/audio/paaudio.c b/audio/paaudio.c
index a0b17c8..97e12ba 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -96,6 +96,59 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
         }                                                               \
     } while (0);
 
+static void *qpa_get_buffer_in(HWVoiceIn *hw, size_t *size)
+{
+    PAVoiceIn *p = (PAVoiceIn *) hw;
+    PAConnection *c = p->g->conn;
+    int r;
+
+    pa_threaded_mainloop_lock(c->mainloop);
+
+    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+                    "pa_threaded_mainloop_lock failed\n");
+
+    if (!p->read_length) {
+        r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
+        CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
+                           "pa_stream_peek failed\n");
+    }
+
+    *size = audio_MIN(p->read_length, *size);
+
+    pa_threaded_mainloop_unlock(c->mainloop);
+    return (void *) p->read_data;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(c->mainloop);
+    *size = 0;
+    return NULL;
+}
+
+static void qpa_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
+{
+    PAVoiceIn *p = (PAVoiceIn *) hw;
+    PAConnection *c = p->g->conn;
+    int r;
+
+    pa_threaded_mainloop_lock(c->mainloop);
+
+    CHECK_DEAD_GOTO(c, p->stream, unlock,
+                    "pa_threaded_mainloop_lock failed\n");
+
+    assert(buf == p->read_data && size <= p->read_length);
+
+    p->read_data += size;
+    p->read_length -= size;
+
+    if (size && !p->read_length) {
+        r = pa_stream_drop(p->stream);
+        CHECK_SUCCESS_GOTO(c, r == 0, unlock, "pa_stream_drop failed\n");
+    }
+
+unlock:
+    pa_threaded_mainloop_unlock(c->mainloop);
+}
+
 static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
 {
     PAVoiceIn *p = (PAVoiceIn *) hw;
@@ -134,6 +187,32 @@ unlock_and_fail:
     return 0;
 }
 
+static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+    PAVoiceOut *p = (PAVoiceOut *) hw;
+    PAConnection *c = p->g->conn;
+    void *ret;
+    int r;
+
+    pa_threaded_mainloop_lock(c->mainloop);
+
+    CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
+                    "pa_threaded_mainloop_lock failed\n");
+
+    *size = -1;
+    r = pa_stream_begin_write(p->stream, &ret, size);
+    CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
+                       "pa_stream_begin_write failed\n");
+
+    pa_threaded_mainloop_unlock(c->mainloop);
+    return ret;
+
+unlock_and_fail:
+    pa_threaded_mainloop_unlock(c->mainloop);
+    *size = 0;
+    return NULL;
+}
+
 static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
 {
     PAVoiceOut *p = (PAVoiceOut *) hw;
@@ -692,12 +771,16 @@ static struct audio_pcm_ops qpa_pcm_ops = {
     .fini_out = qpa_fini_out,
     .write    = qpa_write,
     .buffer_size_out = qpa_buffer_size_out,
+    .get_buffer_out = qpa_get_buffer_out,
+    .put_buffer_out = qpa_write, /* pa handles it */
     .ctl_out  = qpa_ctl_out,
 
     .init_in  = qpa_init_in,
     .fini_in  = qpa_fini_in,
     .read     = qpa_read,
     .buffer_size_in = qpa_buffer_size_in,
+    .get_buffer_in = qpa_get_buffer_in,
+    .put_buffer_in = qpa_put_buffer_in,
     .ctl_in   = qpa_ctl_in
 };
 
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* [Qemu-devel] [PATCH 25/25] audio: split ctl_* functions into enable_* and volume_*
  2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
                   ` (23 preceding siblings ...)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 24/25] paaudio: get/put_buffer functions Kővágó, Zoltán
@ 2015-08-06 18:28 ` Kővágó, Zoltán
  2015-08-20 23:05   ` Marc-André Lureau
  24 siblings, 1 reply; 57+ messages in thread
From: Kővágó, Zoltán @ 2015-08-06 18:28 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

This way we no longer need vararg functions, improving compile time
error detection.  Also now it's possible to check actually what commands
are supported, without needing to manually update ctl_caps.

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
---
 audio/alsaaudio.c      |  56 +++++++++-------------
 audio/audio.c          |  45 ++++++++++-------
 audio/audio_int.h      |  15 ++----
 audio/audio_template.h |   1 -
 audio/coreaudio.c      |  13 ++---
 audio/dsoundaudio.c    |  50 ++++++++-----------
 audio/noaudio.c        |  14 +++---
 audio/ossaudio.c       |  78 ++++++++++++------------------
 audio/paaudio.c        | 128 ++++++++++++++++++++-----------------------------
 audio/sdlaudio.c       |  17 ++-----
 audio/spiceaudio.c     | 101 ++++++++++++++++----------------------
 audio/wavaudio.c       |   7 ++-
 12 files changed, 214 insertions(+), 311 deletions(-)

diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index b7ab540..4797863 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -746,34 +746,28 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
     return 0;
 }
 
-static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void alsa_enable_out(HWVoiceOut *hw, bool enable)
 {
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
     AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_out;
 
-    switch (cmd) {
-    case VOICE_ENABLE:
-        {
-            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
+    if (enable) {
+        bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
 
-            ldebug ("enabling voice\n");
-            if (poll_mode && alsa_poll_out (hw)) {
-                poll_mode = 0;
-            }
-            hw->poll_mode = poll_mode;
-            return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
+        ldebug ("enabling voice\n");
+        if (poll_mode && alsa_poll_out (hw)) {
+            poll_mode = 0;
         }
-
-    case VOICE_DISABLE:
+        hw->poll_mode = poll_mode;
+        alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
+    } else {
         ldebug ("disabling voice\n");
         if (hw->poll_mode) {
             hw->poll_mode = 0;
             alsa_fini_poll (&alsa->pollhlp);
         }
-        return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
+        alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
     }
-
-    return -1;
 }
 
 static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -862,35 +856,29 @@ static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
     return pos;
 }
 
-static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static void alsa_enable_in(HWVoiceIn *hw, bool enable)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
     AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_in;
 
-    switch (cmd) {
-    case VOICE_ENABLE:
-        {
-            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
+    if (enable) {
+        bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
 
-            ldebug ("enabling voice\n");
-            if (poll_mode && alsa_poll_in (hw)) {
-                poll_mode = 0;
-            }
-            hw->poll_mode = poll_mode;
-
-            return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
+        ldebug ("enabling voice\n");
+        if (poll_mode && alsa_poll_in (hw)) {
+            poll_mode = 0;
         }
+        hw->poll_mode = poll_mode;
 
-    case VOICE_DISABLE:
+        alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
+    } else {
         ldebug ("disabling voice\n");
         if (hw->poll_mode) {
             hw->poll_mode = 0;
             alsa_fini_poll (&alsa->pollhlp);
         }
-        return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
+        alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
     }
-
-    return -1;
 }
 
 static void *alsa_audio_init(Audiodev *dev)
@@ -927,13 +915,13 @@ static struct audio_pcm_ops alsa_pcm_ops = {
     .fini_out = alsa_fini_out,
     .buffer_size_out = alsa_buffer_size_out,
     .write    = alsa_write,
-    .ctl_out  = alsa_ctl_out,
+    .enable_out = alsa_enable_out,
 
     .init_in  = alsa_init_in,
     .fini_in  = alsa_fini_in,
     .buffer_size_in = alsa_buffer_size_in,
     .read     = alsa_read,
-    .ctl_in   = alsa_ctl_in,
+    .enable_in = alsa_enable_in,
 };
 
 struct audio_driver alsa_audio_driver = {
diff --git a/audio/audio.c b/audio/audio.c
index ee02b5c..7edec5f 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -608,7 +608,7 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
         total += isamp;
     }
 
-    if (!(hw->ctl_caps & VOICE_VOLUME_CAP)) {
+    if (!hw->pcm_ops->volume_in) {
         mixeng_volume (sw->buf, ret, &sw->vol);
     }
 
@@ -696,7 +696,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
     if (swlim) {
         sw->conv (sw->buf, buf, swlim);
 
-        if (!(sw->hw->ctl_caps & VOICE_VOLUME_CAP)) {
+        if (!sw->hw->pcm_ops->volume_out) {
             mixeng_volume (sw->buf, swlim, &sw->vol);
         }
     }
@@ -860,7 +860,9 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
+                    if (hw->pcm_ops->enable_out) {
+                        hw->pcm_ops->enable_out(hw, true);
+                    }
                     audio_reset_timer (s);
                 }
             }
@@ -905,7 +907,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
             if (!hw->enabled) {
                 hw->enabled = 1;
                 if (s->vm_running) {
-                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
+                    if (hw->pcm_ops->enable_in) {
+                        hw->pcm_ops->enable_in(hw, true);
+                    }
                     audio_reset_timer (s);
                 }
             }
@@ -922,7 +926,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
 
                 if (nb_active == 1) {
                     hw->enabled = 0;
-                    hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
+                    if (hw->pcm_ops->enable_in) {
+                        hw->pcm_ops->enable_in(hw, false);
+                    }
                 }
             }
         }
@@ -1088,7 +1094,9 @@ static void audio_run_out (AudioState *s)
 #endif
             hw->enabled = 0;
             hw->pending_disable = 0;
-            hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
+            if (hw->pcm_ops->enable_out) {
+                hw->pcm_ops->enable_out(hw, false);
+            }
             for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
                 sc->sw.active = 0;
                 audio_recalc_and_notify_capture (sc->cap);
@@ -1455,15 +1463,18 @@ static void audio_vm_change_state_handler (void *opaque, int running,
     AudioState *s = opaque;
     HWVoiceOut *hwo = NULL;
     HWVoiceIn *hwi = NULL;
-    int op = running ? VOICE_ENABLE : VOICE_DISABLE;
 
     s->vm_running = running;
     while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
-        hwo->pcm_ops->ctl_out (hwo, op);
+        if (hwo->pcm_ops->enable_out) {
+            hwo->pcm_ops->enable_out(hwo, running);
+        }
     }
 
     while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
-        hwi->pcm_ops->ctl_in (hwi, op);
+        if (hwi->pcm_ops->enable_in) {
+            hwi->pcm_ops->enable_in(hwi, running);
+        }
     }
     audio_reset_timer (s);
 }
@@ -1476,8 +1487,8 @@ static void free_audio_state(AudioState *s)
     while ((hwo = audio_pcm_hw_find_any_out(s, hwo))) {
         SWVoiceCap *sc;
 
-        if (hwo->enabled) {
-            hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
+        if (hwo->enabled && hwo->pcm_ops->enable_out) {
+            hwo->pcm_ops->enable_out(hwo, false);
         }
         hwo->pcm_ops->fini_out (hwo);
 
@@ -1492,8 +1503,8 @@ static void free_audio_state(AudioState *s)
     }
 
     while ((hwi = audio_pcm_hw_find_any_in(s, hwi))) {
-        if (hwi->enabled) {
-            hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
+        if (hwi->enabled && hwi->pcm_ops->enable_in) {
+            hwi->pcm_ops->enable_in(hwi, false);
         }
         hwi->pcm_ops->fini_in (hwi);
     }
@@ -1813,8 +1824,8 @@ void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
         sw->vol.l = nominal_volume.l * lvol / 255;
         sw->vol.r = nominal_volume.r * rvol / 255;
 
-        if (hw->pcm_ops->ctl_out) {
-            hw->pcm_ops->ctl_out (hw, VOICE_VOLUME, sw);
+        if (hw->pcm_ops->volume_out) {
+            hw->pcm_ops->volume_out(hw, &sw->vol);
         }
     }
 }
@@ -1828,8 +1839,8 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
         sw->vol.l = nominal_volume.l * lvol / 255;
         sw->vol.r = nominal_volume.r * rvol / 255;
 
-        if (hw->pcm_ops->ctl_in) {
-            hw->pcm_ops->ctl_in (hw, VOICE_VOLUME, sw);
+        if (hw->pcm_ops->volume_in) {
+            hw->pcm_ops->volume_in(hw, &sw->vol);
         }
     }
 }
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 16ffa12..6f2f309 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -71,7 +71,6 @@ typedef struct HWVoiceOut {
 
     QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
     QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
-    int ctl_caps;
     struct audio_pcm_ops *pcm_ops;
     QLIST_ENTRY (HWVoiceOut) entries;
 } HWVoiceOut;
@@ -92,7 +91,6 @@ typedef struct HWVoiceIn {
     size_t pos_emul, pending_emul, size_emul;
 
     QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
-    int ctl_caps;
     struct audio_pcm_ops *pcm_ops;
     QLIST_ENTRY (HWVoiceIn) entries;
 } HWVoiceIn;
@@ -143,7 +141,6 @@ struct audio_driver {
     int max_voices_in;
     int voice_size_out;
     int voice_size_in;
-    int ctl_caps;
 };
 
 struct audio_pcm_ops {
@@ -160,7 +157,8 @@ struct audio_pcm_ops {
      * buf must be equal the pointer returned by get_buffer_out,
      * size may be smaller */
     size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
-    int    (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
+    void   (*enable_out)(HWVoiceOut *hw, bool enable);
+    void   (*volume_out)(HWVoiceOut *hw, struct mixeng_volume *vol);
 
     int    (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
     void   (*fini_in) (HWVoiceIn *hw);
@@ -168,7 +166,8 @@ struct audio_pcm_ops {
     size_t (*buffer_size_in)(HWVoiceIn *hw);
     void  *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
     void   (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
-    int    (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
+    void   (*enable_in)(HWVoiceIn *hw, bool enable);
+    void   (*volume_in)(HWVoiceIn *hw, struct mixeng_volume *vol);
 };
 
 void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
@@ -247,12 +246,6 @@ void audio_rate_start(RateCtl *rate);
 size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
                             size_t bytes_avail);
 
-#define VOICE_ENABLE 1
-#define VOICE_DISABLE 2
-#define VOICE_VOLUME 3
-
-#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
-
 static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
 {
     return (dst >= src) ? (dst - src) : (len - src + dst);
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 942d9a4..f3cbc66 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -264,7 +264,6 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
 
     hw->s = s;
     hw->pcm_ops = drv->pcm_ops;
-    hw->ctl_caps = drv->ctl_caps;
 
     QLIST_INIT (&hw->sw_head);
 #ifdef DAC
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index d6c7636..bf35b0b 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -473,13 +473,12 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
     }
 }
 
-static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void coreaudio_enable_out (HWVoiceOut *hw, bool enable)
 {
     OSStatus status;
     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 
-    switch (cmd) {
-    case VOICE_ENABLE:
+    if (enable) {
         /* start playback */
         if (!isPlaying(core->outputDeviceID)) {
             status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
@@ -487,9 +486,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
                 coreaudio_logerr (status, "Could not resume playback\n");
             }
         }
-        break;
-
-    case VOICE_DISABLE:
+    } else {
         /* stop playback */
         if (!isAtexit) {
             if (isPlaying(core->outputDeviceID)) {
@@ -499,9 +496,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
                 }
             }
         }
-        break;
     }
-    return 0;
 }
 
 static void *coreaudio_audio_init(Audiodev *dev)
@@ -521,7 +516,7 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
     .buffer_size_out = coreaudio_buffer_size_out,
     .get_buffer_out = coreaudio_get_buffer_out,
     .put_buffer_out = coreaudio_put_buffer_out_nowrite,
-    .ctl_out  = coreaudio_ctl_out
+    .enable_out = coreaudio_enable_out
 };
 
 struct audio_driver coreaudio_audio_driver = {
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index fcffef7..e55dd46 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -361,7 +361,7 @@ static int dsound_open (dsound *s)
     return 0;
 }
 
-static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void dsound_enable_out(HWVoiceOut *hw, bool enable)
 {
     HRESULT hr;
     DWORD status;
@@ -371,18 +371,17 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
 
     if (!dsb) {
         dolog ("Attempt to control voice without a buffer\n");
-        return 0;
+        return;
     }
 
-    switch (cmd) {
-    case VOICE_ENABLE:
+    if (enable) {
         if (dsound_get_status_out (dsb, &status, s)) {
-            return -1;
+            return;
         }
 
         if (status & DSBSTATUS_PLAYING) {
             dolog ("warning: Voice is already playing\n");
-            return 0;
+            return;
         }
 
         dsound_clear_sample (hw, dsb, s);
@@ -390,28 +389,24 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
         if (FAILED (hr)) {
             dsound_logerr (hr, "Could not start playing buffer\n");
-            return -1;
+            return;
         }
-        break;
-
-    case VOICE_DISABLE:
+    } else {
         if (dsound_get_status_out (dsb, &status, s)) {
-            return -1;
+            return;
         }
 
         if (status & DSBSTATUS_PLAYING) {
             hr = IDirectSoundBuffer_Stop (dsb);
             if (FAILED (hr)) {
                 dsound_logerr (hr, "Could not stop playing buffer\n");
-                return -1;
+                return;
             }
         }
         else {
             dolog ("warning: Voice is not playing\n");
         }
-        break;
     }
-    return 0;
 }
 
 static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
@@ -461,7 +456,7 @@ static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
     return len;
 }
 
-static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static void dsound_enable_in(HWVoiceIn *hw, bool enable)
 {
     HRESULT hr;
     DWORD status;
@@ -470,18 +465,17 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
 
     if (!dscb) {
         dolog ("Attempt to control capture voice without a buffer\n");
-        return -1;
+        return;
     }
 
-    switch (cmd) {
-    case VOICE_ENABLE:
+    if (enable) {
         if (dsound_get_status_in (dscb, &status)) {
-            return -1;
+            return;
         }
 
         if (status & DSCBSTATUS_CAPTURING) {
             dolog ("warning: Voice is already capturing\n");
-            return 0;
+            return;
         }
 
         /* clear ?? */
@@ -489,28 +483,24 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
         if (FAILED (hr)) {
             dsound_logerr (hr, "Could not start capturing\n");
-            return -1;
+            return;
         }
-        break;
-
-    case VOICE_DISABLE:
+    } else {
         if (dsound_get_status_in (dscb, &status)) {
-            return -1;
+            return;
         }
 
         if (status & DSCBSTATUS_CAPTURING) {
             hr = IDirectSoundCaptureBuffer_Stop (dscb);
             if (FAILED (hr)) {
                 dsound_logerr (hr, "Could not stop capturing\n");
-                return -1;
+                return;
             }
         }
         else {
             dolog ("warning: Voice is not capturing\n");
         }
-        break;
     }
-    return 0;
 }
 
 static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
@@ -675,7 +665,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
     .buffer_size_out = dsound_buffer_size_out,
     .get_buffer_out = dsound_get_buffer_out,
     .put_buffer_out = dsound_put_buffer_out,
-    .ctl_out  = dsound_ctl_out,
+    .enable_out = dsound_enable_out,
 
     .init_in  = dsound_init_in,
     .fini_in  = dsound_fini_in,
@@ -683,7 +673,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
     .buffer_size_in = dsound_buffer_size_in,
     .get_buffer_in = dsound_get_buffer_in,
     .put_buffer_in = dsound_put_buffer_in,
-    .ctl_in   = dsound_ctl_in
+    .enable_in = dsound_enable_in,
 };
 
 struct audio_driver dsound_audio_driver = {
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 3cb3a89..0618b60 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -58,14 +58,13 @@ static void no_fini_out (HWVoiceOut *hw)
     (void) hw;
 }
 
-static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void no_enable_out(HWVoiceOut *hw, bool enable)
 {
     NoVoiceOut *no = (NoVoiceOut *) hw;
 
-    if (cmd == VOICE_ENABLE) {
+    if (enable) {
         audio_rate_start(&no->rate);
     }
-    return 0;
 }
 
 static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -91,14 +90,13 @@ static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
     return bytes;
 }
 
-static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static void no_enable_in(HWVoiceIn *hw, bool enable)
 {
     NoVoiceIn *no = (NoVoiceIn *) hw;
 
-    if (cmd == VOICE_ENABLE) {
+    if (enable) {
         audio_rate_start(&no->rate);
     }
-    return 0;
 }
 
 static void *no_audio_init (Audiodev *dev)
@@ -115,12 +113,12 @@ static struct audio_pcm_ops no_pcm_ops = {
     .init_out = no_init_out,
     .fini_out = no_fini_out,
     .write    = no_write,
-    .ctl_out  = no_ctl_out,
+    .enable_out = no_enable_out,
 
     .init_in  = no_init_in,
     .fini_in  = no_fini_in,
     .read     = no_read,
-    .ctl_in   = no_ctl_in
+    .enable_in = no_enable_in
 };
 
 struct audio_driver no_audio_driver = {
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index de37645..a854d5a 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -574,59 +574,50 @@ static size_t oss_buffer_size_out(HWVoiceOut *hw)
     return oss->samples;
 }
 
-static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void oss_enable_out(HWVoiceOut *hw, bool enable)
 {
     int trig;
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
     AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
 
-    switch (cmd) {
-    case VOICE_ENABLE:
-        {
-            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
+    if (enable) {
+        bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
 
-            ldebug ("enabling voice\n");
-            if (poll_mode) {
-                oss_poll_out (hw);
-                poll_mode = 0;
-            }
-            hw->poll_mode = poll_mode;
-
-            if (!oss->mmapped) {
-                return 0;
-            }
+        ldebug ("enabling voice\n");
+        if (poll_mode) {
+            oss_poll_out (hw);
+            poll_mode = 0;
+        }
+        hw->poll_mode = poll_mode;
 
-            audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, oss->samples);
-            trig = PCM_ENABLE_OUTPUT;
-            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
-                oss_logerr (
-                    errno,
-                    "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
-                    );
-                return -1;
-            }
+        if (!oss->mmapped) {
+            return;
         }
-        break;
 
-    case VOICE_DISABLE:
+        audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, oss->samples);
+        trig = PCM_ENABLE_OUTPUT;
+        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+            oss_logerr(errno,
+                       "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
+            return;
+            }
+    } else {
         if (hw->poll_mode) {
             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
             hw->poll_mode = 0;
         }
 
         if (!oss->mmapped) {
-            return 0;
+            return;
         }
 
         ldebug ("disabling voice\n");
         trig = 0;
         if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
             oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
-            return -1;
+            return;
         }
-        break;
     }
-    return 0;
 }
 
 static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -719,32 +710,25 @@ static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
     return pos;
 }
 
-static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static void oss_enable_in(HWVoiceIn *hw, bool enable)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
     AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
 
-    switch (cmd) {
-    case VOICE_ENABLE:
-        {
-            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
+    if (enable) {
+        bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
 
-            if (poll_mode) {
-                oss_poll_in (hw);
-                poll_mode = 0;
-            }
-            hw->poll_mode = poll_mode;
+        if (poll_mode) {
+            oss_poll_in (hw);
+            poll_mode = 0;
         }
-        break;
-
-    case VOICE_DISABLE:
+        hw->poll_mode = poll_mode;
+    } else {
         if (hw->poll_mode) {
             hw->poll_mode = 0;
             qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
         }
-        break;
     }
-    return 0;
 }
 
 static void *oss_audio_init(Audiodev *dev)
@@ -773,13 +757,13 @@ static struct audio_pcm_ops oss_pcm_ops = {
     .buffer_size_out = oss_buffer_size_out,
     .get_buffer_out = oss_get_buffer_out,
     .put_buffer_out = oss_put_buffer_out,
-    .ctl_out  = oss_ctl_out,
+    .enable_out = oss_enable_out,
 
     .init_in  = oss_init_in,
     .fini_in  = oss_fini_in,
     .read     = oss_read,
     .buffer_size_in = oss_buffer_size_in,
-    .ctl_in   = oss_ctl_in
+    .enable_in = oss_enable_in
 };
 
 struct audio_driver oss_audio_driver = {
diff --git a/audio/paaudio.c b/audio/paaudio.c
index 97e12ba..e3938c4 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -533,7 +533,7 @@ static void qpa_fini_in (HWVoiceIn *hw)
     }
 }
 
-static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void qpa_volume_out(HWVoiceOut *hw, struct mixeng_volume *vol)
 {
     PAVoiceOut *pa = (PAVoiceOut *) hw;
     pa_operation *op;
@@ -544,48 +544,36 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
     pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
 #endif
 
-    switch (cmd) {
-    case VOICE_VOLUME:
-        {
-            SWVoiceOut *sw;
-            va_list ap;
+    v.channels = 2;
+    v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->l) / UINT32_MAX;
+    v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->r) / UINT32_MAX;
 
-            va_start (ap, cmd);
-            sw = va_arg (ap, SWVoiceOut *);
-            va_end (ap);
+    pa_threaded_mainloop_lock(c->mainloop);
 
-            v.channels = 2;
-            v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
-            v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
-
-            pa_threaded_mainloop_lock(c->mainloop);
-
-            op = pa_context_set_sink_input_volume(c->context,
-                pa_stream_get_index (pa->stream),
-                &v, NULL, NULL);
-            if (!op)
-                qpa_logerr (pa_context_errno(c->context),
-                            "set_sink_input_volume() failed\n");
-            else
-                pa_operation_unref (op);
-
-            op = pa_context_set_sink_input_mute(c->context,
-                pa_stream_get_index (pa->stream),
-               sw->vol.mute, NULL, NULL);
-            if (!op) {
-                qpa_logerr (pa_context_errno(c->context),
-                            "set_sink_input_mute() failed\n");
-            } else {
-                pa_operation_unref (op);
-            }
+    op = pa_context_set_sink_input_volume(c->context,
+                                          pa_stream_get_index(pa->stream),
+                                          &v, NULL, NULL);
+    if (!op) {
+        qpa_logerr(pa_context_errno(c->context),
+                   "set_sink_input_volume() failed\n");
+    } else {
+        pa_operation_unref (op);
+    }
 
-            pa_threaded_mainloop_unlock(c->mainloop);
-        }
+    op = pa_context_set_sink_input_mute(c->context,
+                                        pa_stream_get_index (pa->stream),
+                                        vol->mute, NULL, NULL);
+    if (!op) {
+        qpa_logerr (pa_context_errno(c->context),
+                    "set_sink_input_mute() failed\n");
+    } else {
+        pa_operation_unref (op);
     }
-    return 0;
+
+    pa_threaded_mainloop_unlock(c->mainloop);
 }
 
-static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static void qpa_volume_in(HWVoiceIn *hw, struct mixeng_volume *vol)
 {
     PAVoiceIn *pa = (PAVoiceIn *) hw;
     pa_operation *op;
@@ -596,47 +584,34 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
     pa_cvolume_init (&v);
 #endif
 
-    switch (cmd) {
-    case VOICE_VOLUME:
-        {
-            SWVoiceIn *sw;
-            va_list ap;
+    v.channels = 2;
+    v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->l) / UINT32_MAX;
+    v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->r) / UINT32_MAX;
 
-            va_start (ap, cmd);
-            sw = va_arg (ap, SWVoiceIn *);
-            va_end (ap);
+    pa_threaded_mainloop_lock(c->mainloop);
 
-            v.channels = 2;
-            v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
-            v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
-
-            pa_threaded_mainloop_lock(c->mainloop);
-
-            /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
-            op = pa_context_set_source_volume_by_index(c->context,
-                pa_stream_get_device_index (pa->stream),
-                &v, NULL, NULL);
-            if (!op) {
-                qpa_logerr (pa_context_errno(c->context),
-                            "set_source_volume() failed\n");
-            } else {
-                pa_operation_unref(op);
-            }
-
-            op = pa_context_set_source_mute_by_index(c->context,
-                pa_stream_get_index (pa->stream),
-                sw->vol.mute, NULL, NULL);
-            if (!op) {
-                qpa_logerr (pa_context_errno(c->context),
-                            "set_source_mute() failed\n");
-            } else {
-                pa_operation_unref (op);
-            }
+    /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
+    op = pa_context_set_source_volume_by_index(c->context,
+                                               pa_stream_get_device_index(pa->stream),
+                                               &v, NULL, NULL);
+    if (!op) {
+        qpa_logerr(pa_context_errno(c->context),
+                   "set_source_volume() failed\n");
+    } else {
+        pa_operation_unref(op);
+    }
 
-            pa_threaded_mainloop_unlock(c->mainloop);
-        }
+    op = pa_context_set_source_mute_by_index(c->context,
+                                             pa_stream_get_index(pa->stream),
+                                             vol->mute, NULL, NULL);
+    if (!op) {
+        qpa_logerr(pa_context_errno(c->context),
+                   "set_source_mute() failed\n");
+    } else {
+        pa_operation_unref(op);
     }
-    return 0;
+
+    pa_threaded_mainloop_unlock(c->mainloop);
 }
 
 /* common */
@@ -773,7 +748,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
     .buffer_size_out = qpa_buffer_size_out,
     .get_buffer_out = qpa_get_buffer_out,
     .put_buffer_out = qpa_write, /* pa handles it */
-    .ctl_out  = qpa_ctl_out,
+    .volume_out = qpa_volume_out,
 
     .init_in  = qpa_init_in,
     .fini_in  = qpa_fini_in,
@@ -781,7 +756,7 @@ static struct audio_pcm_ops qpa_pcm_ops = {
     .buffer_size_in = qpa_buffer_size_in,
     .get_buffer_in = qpa_get_buffer_in,
     .put_buffer_in = qpa_put_buffer_in,
-    .ctl_in   = qpa_ctl_in
+    .volume_in = qpa_volume_in
 };
 
 struct audio_driver pa_audio_driver = {
@@ -795,5 +770,4 @@ struct audio_driver pa_audio_driver = {
     .max_voices_in  = INT_MAX,
     .voice_size_out = sizeof (PAVoiceOut),
     .voice_size_in  = sizeof (PAVoiceIn),
-    .ctl_caps       = VOICE_VOLUME_CAP
 };
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index ee5ab92..5821341 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -350,20 +350,9 @@ static size_t sdl_buffer_size_out(HWVoiceOut *hw)
     return sdl->samples;
 }
 
-static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void sdl_enable_out(HWVoiceOut *hw, bool enable)
 {
-    (void) hw;
-
-    switch (cmd) {
-    case VOICE_ENABLE:
-        SDL_PauseAudio (0);
-        break;
-
-    case VOICE_DISABLE:
-        SDL_PauseAudio (1);
-        break;
-    }
-    return 0;
+    SDL_PauseAudio(!enable);
 }
 
 static void *sdl_audio_init(Audiodev *dev)
@@ -417,7 +406,7 @@ static struct audio_pcm_ops sdl_pcm_ops = {
     .buffer_size_out = sdl_buffer_size_out,
     .get_buffer_out = sdl_get_buffer_out,
     .put_buffer_out = sdl_put_buffer_out_nowrite,
-    .ctl_out  = sdl_ctl_out,
+    .enable_out = sdl_enable_out,
 };
 
 struct audio_driver sdl_audio_driver = {
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
index a7faed8..8c78afc 100644
--- a/audio/spiceaudio.c
+++ b/audio/spiceaudio.c
@@ -151,22 +151,20 @@ static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
     return size;
 }
 
-static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
+static void line_out_enable(HWVoiceOut *hw, bool enable)
 {
     SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
 
-    switch (cmd) {
-    case VOICE_ENABLE:
+    if (enable) {
         if (out->active) {
-            break;
+            return;
         }
         out->active = 1;
         audio_rate_start(&out->rate);
         spice_server_playback_start (&out->sin);
-        break;
-    case VOICE_DISABLE:
+    } else {
         if (!out->active) {
-            break;
+            return;
         }
         out->active = 0;
         if (out->frame) {
@@ -175,29 +173,21 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
             out->frame = NULL;
         }
         spice_server_playback_stop (&out->sin);
-        break;
-    case VOICE_VOLUME:
-        {
+    }
+}
+
 #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
-            SWVoiceOut *sw;
-            va_list ap;
-            uint16_t vol[2];
+static void line_out_volume(HWVoiceOut *hw, struct mixeng_volume *vol)
+{
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+    uint16_t svol[2];
 
-            va_start (ap, cmd);
-            sw = va_arg (ap, SWVoiceOut *);
-            va_end (ap);
-
-            vol[0] = sw->vol.l / ((1ULL << 16) + 1);
-            vol[1] = sw->vol.r / ((1ULL << 16) + 1);
-            spice_server_playback_set_volume (&out->sin, 2, vol);
-            spice_server_playback_set_mute (&out->sin, sw->vol.mute);
+    svol[0] = vol->l / ((1ULL << 16) + 1);
+    svol[1] = vol->r / ((1ULL << 16) + 1);
+    spice_server_playback_set_volume(&out->sin, 2, svol);
+    spice_server_playback_set_mute(&out->sin, vol->mute);
+}
 #endif
-            break;
-        }
-    }
-
-    return 0;
-}
 
 /* record */
 
@@ -253,48 +243,38 @@ static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
     return ready << 2;
 }
 
-static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
+static void line_in_enable(HWVoiceIn *hw, bool enable)
 {
     SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
 
-    switch (cmd) {
-    case VOICE_ENABLE:
+    if (enable) {
         if (in->active) {
-            break;
+            return;
         }
         in->active = 1;
         audio_rate_start(&in->rate);
         spice_server_record_start (&in->sin);
-        break;
-    case VOICE_DISABLE:
+    } else {
         if (!in->active) {
-            break;
+            return;
         }
         in->active = 0;
         spice_server_record_stop (&in->sin);
-        break;
-    case VOICE_VOLUME:
-        {
+    }
+}
+
 #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
-            SWVoiceIn *sw;
-            va_list ap;
-            uint16_t vol[2];
+static void line_in_volume(HWVoiceIn *hw, struct mixeng_volume *vol)
+{
+    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
+    uint16_t svol[2];
 
-            va_start (ap, cmd);
-            sw = va_arg (ap, SWVoiceIn *);
-            va_end (ap);
-
-            vol[0] = sw->vol.l / ((1ULL << 16) + 1);
-            vol[1] = sw->vol.r / ((1ULL << 16) + 1);
-            spice_server_record_set_volume (&in->sin, 2, vol);
-            spice_server_record_set_mute (&in->sin, sw->vol.mute);
+    svol[0] = vol->l / ((1ULL << 16) + 1);
+    svol[1] = vol->r / ((1ULL << 16) + 1);
+    spice_server_record_set_volume(&in->sin, 2, svol);
+    spice_server_record_set_mute(&in->sin, vol->mute);
+}
 #endif
-            break;
-        }
-    }
-
-    return 0;
-}
 
 static struct audio_pcm_ops audio_callbacks = {
     .init_out = line_out_init,
@@ -303,13 +283,19 @@ static struct audio_pcm_ops audio_callbacks = {
     .buffer_size_out = line_out_buffer_size,
     .get_buffer_out = line_out_get_buffer,
     .put_buffer_out = line_out_put_buffer,
-    .ctl_out  = line_out_ctl,
+    .enable_out = line_out_enable,
+#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
+    .volume_out = line_out_volume,
+#endif
 
     .init_in  = line_in_init,
     .fini_in  = line_in_fini,
     .read     = line_in_read,
     .buffer_size_in = line_in_buffer_size,
-    .ctl_in   = line_in_ctl,
+    .enable_in = line_in_enable,
+#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
+    .volume_in = line_in_volume,
+#endif
 };
 
 struct audio_driver spice_audio_driver = {
@@ -322,9 +308,6 @@ struct audio_driver spice_audio_driver = {
     .max_voices_in  = 1,
     .voice_size_out = sizeof (SpiceVoiceOut),
     .voice_size_in  = sizeof (SpiceVoiceIn),
-#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
-    .ctl_caps       = VOICE_VOLUME_CAP
-#endif
 };
 
 void qemu_spice_audio_init (void)
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 14afaaf..5b35173 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -169,14 +169,13 @@ static void wav_fini_out (HWVoiceOut *hw)
     wav->f = NULL;
 }
 
-static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void wav_enable_out(HWVoiceOut *hw, bool enable)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
 
-    if (cmd == VOICE_ENABLE) {
+    if (enable) {
         audio_rate_start(&wav->rate);
     }
-    return 0;
 }
 
 static void *wav_audio_init(Audiodev *dev)
@@ -194,7 +193,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
     .init_out = wav_init_out,
     .fini_out = wav_fini_out,
     .write    = wav_write_out,
-    .ctl_out  = wav_ctl_out,
+    .enable_out = wav_enable_out,
 };
 
 struct audio_driver wav_audio_driver = {
-- 
2.4.5

^ permalink raw reply related	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX Kővágó, Zoltán
@ 2015-08-19 18:17   ` Gerd Hoffmann
  2015-08-19 23:31     ` Peter Maydell
  2015-08-20 20:36   ` Marc-André Lureau
  1 sibling, 1 reply; 57+ messages in thread
From: Gerd Hoffmann @ 2015-08-19 18:17 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: qemu-devel

On Do, 2015-08-06 at 20:28 +0200, Kővágó, Zoltán wrote:
> Currently the gcc specific version only evaluates the arguments once,
> while the generic version evaluates one argument twice, which can cause
> debugging headaches when an argument has a side effect.

The answer to that is "don't do that".  Do we have macro calls with side
effects in the tree?

> This patch at least provides consistent behavior between compilers.

Makes sense.

> -#else
>  #define audio_MIN(a, b) ((a)>(b)?(b):(a))
>  #define audio_MAX(a, b) ((a)<(b)?(b):(a))
> -#endif

include/qemu/osdep.h already provides MIN/MAX macros.

I think we should either define audio_MIN (and audio_MAX) to those, or
simply do s/audio_MIN/MIN/ in audio/*.c

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends Kővágó, Zoltán
@ 2015-08-19 19:07   ` Gerd Hoffmann
  2015-08-20 22:28     ` Marc-André Lureau
  0 siblings, 1 reply; 57+ messages in thread
From: Gerd Hoffmann @ 2015-08-19 19:07 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: qemu-devel

On Do, 2015-08-06 at 20:28 +0200, Kővágó, Zoltán wrote:
> Backends no longer have to deal with mixeng, they just receive a buffer
> in the correct sample format, all mixeng logic is now in the audio.c
> (and mixeng.c).  Backends also do not have to deal with soft voices.
> 
> Backends now have two way to read/write sound:
> * write and read functions: similar to old read/write functions, except
>   that they actually read/write the data to the backend instead of
>   placing it into the mixeng buffer.  You no longer need run_in/run_out
>   afterwards.
> * get_buffer_out/put_buffer_out: the first function returns a buffer
>   that can hold some audio data.  The caller fills this buffer (maybe
>   partially) and calls put_buffer to actually write the data.  This way
>   we can save copying the buffer in some cases (for example mmaped
>   audio).  Similarly there's get_buffer_in/put_buffer_in for reading.
> 
> Backends only have to support one access method, but they can support
> both if they have efficient implementation for both cases.

Phew, this one became pretty big.  Could this be splitted up?  The
s/int/size_t/ should be easy to separate.

Also having individual patches for individual backends would be nice.
Possibly you can first add the new interfaces, & helper functions, then
switch backends over one by one, hooking the generic
audio_pcm_hw_run_out() function into run_out callback, finally remove
the old interfaces and call audio_pcm_hw_run_out directly.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX
  2015-08-19 18:17   ` Gerd Hoffmann
@ 2015-08-19 23:31     ` Peter Maydell
  0 siblings, 0 replies; 57+ messages in thread
From: Peter Maydell @ 2015-08-19 23:31 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU Developers, Kővágó, Zoltán

On 19 August 2015 at 19:17, Gerd Hoffmann <kraxel@redhat.com> wrote:
> On Do, 2015-08-06 at 20:28 +0200, Kővágó, Zoltán wrote:
>> Currently the gcc specific version only evaluates the arguments once,
>> while the generic version evaluates one argument twice, which can cause
>> debugging headaches when an argument has a side effect.
>
> The answer to that is "don't do that".  Do we have macro calls with side
> effects in the tree?
>
>> This patch at least provides consistent behavior between compilers.
>
> Makes sense.
>
>> -#else
>>  #define audio_MIN(a, b) ((a)>(b)?(b):(a))
>>  #define audio_MAX(a, b) ((a)<(b)?(b):(a))
>> -#endif
>
> include/qemu/osdep.h already provides MIN/MAX macros.
>
> I think we should either define audio_MIN (and audio_MAX) to those, or
> simply do s/audio_MIN/MIN/ in audio/*.c

My vote is for the latter. Incidentally we already assume both
typeof and statement expr support in our compilers, so we could
upgrade our local MIN/MAX implementations to use them if we
really needed. (We'd have to rename them though, since the
system implementation likely does the eval-twice thing.)

A quick grep doesn't show any audio_MIN/MAX which need to
avoid multiple-evaluation, though.

thanks
-- PMM

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 05/25] qapi: change Netdev into a flat union
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 05/25] qapi: change Netdev into a flat union Kővágó, Zoltán
@ 2015-08-20 15:03   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 15:03 UTC (permalink / raw)
  To: Kővágó, Zoltán
  Cc: Peter Maydell, Michael S. Tsirkin, Jason Wang, QEMU,
	Vincenzo Maffione, Alexander Graf, Max Filippov, Gerd Hoffmann,
	Dmitry Fleytman, Edgar E. Iglesias, Rob Herring,
	Markus Armbruster, Scott Feldman, Jiri Pirko, Jan Kiszka,
	Stefan Hajnoczi, Giuseppe Lettieri, Luiz Capitulino, Luigi Rizzo,
	David Gibson, Peter Crosthwaite, Michael Walle,
	open list:sPAPR pseries

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Except qapi-schema.json, this patch was geenrated by:

generated

> +# Captures the commopn configuration of a network device.

common

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 07/25] qapi: qapi for audio backends
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 07/25] qapi: qapi for audio backends Kővágó, Zoltán
@ 2015-08-20 15:30   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 15:30 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: Markus Armbruster, QEMU, Gerd Hoffmann

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> This patch adds structures into qapi to replace the existing
> configuration structures used by audio backends currently. This qapi
> will be the base of the -audiodev command line parameter (that replaces
> the old environment variables based config).
>
> This is not a 1:1 translation of the old options, I've tried to make
> them much more consistent (e.g. almost every backend had an option to
> specify buffer size, but the name was different for every backend, and
> some backends required usecs, while some other required frames, samples
> or bytes). Also tried to reduce the number of abbreviations used by the
> config keys.
>
> Some of the more important changes:
> * use `in` and `out` instead of `ADC` and `DAC`, as the former is more
>   user friendly imho
> * moved buffer settings into the global setting area (so it's the same
>   for all backends that support it. Backends that can't change buffer
>   size will simply ignore them). Also using usecs, as it's probably more
>   user friendly than samples or bytes.
> * try-poll is now an alsa backend specific option (as all other backends
>   currently ignore it)
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  Makefile         |   4 +-
>  qapi-schema.json |   3 +
>  qapi/audio.json  | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 268 insertions(+), 2 deletions(-)
>  create mode 100644 qapi/audio.json
>
> diff --git a/Makefile b/Makefile
> index 340d9c8..bdd0bc6 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -262,8 +262,8 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
>                 "  GEN   $@")
>
>  qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
> -               $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
> -               $(SRC_PATH)/qapi/event.json
> +               $(SRC_PATH)/qapi/audio.json  $(SRC_PATH)/qapi/block.json \
> +               $(SRC_PATH)/qapi/block-core.json $(SRC_PATH)/qapi/event.json
>
>  qapi-types.c qapi-types.h :\
>  $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> diff --git a/qapi-schema.json b/qapi-schema.json
> index c9d9263..e9cbe15 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -5,6 +5,9 @@
>  # QAPI common definitions
>  { 'include': 'qapi/common.json' }
>
> +# QAPI audio definitions
> +{ 'include': 'qapi/audio.json' }
> +
>  # QAPI block definitions
>  { 'include': 'qapi/block.json' }
>
> diff --git a/qapi/audio.json b/qapi/audio.json
> new file mode 100644
> index 0000000..b57b215
> --- /dev/null
> +++ b/qapi/audio.json
> @@ -0,0 +1,263 @@
> +# -*- mode: python -*-
> +#
> +# Copyright (C) 2015 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
> +#
> +# This work is licensed under the terms of the GNU GPL, version 2 or later.
> +# See the COPYING file in the top-level directory.
> +
> +##
> +# @AudiodevNoOptions
> +#
> +# The none, coreaudio, sdl and spice audio backend have no options.
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevNoOptions',
> +  'data': { } }
> +
> +##
> +# @AudiodevAlsaPerDirectionOptions
> +#
> +# Options of the alsa backend that are used for both playback and recording.
> +#
> +# @dev: #optional the name of the alsa device to use (default 'default')
> +#
> +# @try-poll: #optional attempt to use poll mode, falling back to non polling
> +#            access on failure (default on)
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevAlsaPerDirectionOptions',
> +  'data': {
> +    '*dev':      'str',
> +    '*try-poll': 'bool' } }
> +
> +##
> +# @AudiodevAlsaOptions
> +#
> +# Options of the alsa audio backend.
> +#
> +# @alsa-in: options of the capture stream
> +#
> +# @alsa-out: options of the playback stream
> +#
> +# @threshold: #optional set the threshold (in microsecods) when playback starts

microseconds.

> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevAlsaOptions',
> +  'data': {
> +    'alsa-in':    'AudiodevAlsaPerDirectionOptions',
> +    'alsa-out':   'AudiodevAlsaPerDirectionOptions',
> +    '*threshold': 'int' } }
> +
> +##
> +# @AudiodevDsoundOptions
> +#
> +# Options of the dsound audio backend.
> +#
> +# @latency: #optional add extra latency to playback in microseconds (default
> +#           10000)
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevDsoundOptions',
> +  'data': {
> +    '*latency': 'int' } }
> +
> +##
> +# @AudiodevOssPerDirectionOptions
> +#
> +# Options of the oss backend that are used for both playback and recording.
> +#
> +# @dev: #optional file name of the oss device (default '/dev/dsp')
> +#
> +# @try-poll: #optional attempt to use poll mode, falling back to non polling
> +#            access on failure (default on)
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevOssPerDirectionOptions',
> +  'data': {
> +    '*dev':      'str',
> +    '*try-poll': 'bool' } }
> +
> +##
> +# @AudiodevOssOptions
> +#
> +# Options of the oss audio backend.
> +#
> +# @oss-in: options of the capture stream
> +#
> +# @oss-out: options of the playback stream
> +#
> +# @try-mmap: #optional try using memory mapped access, falling back to non
> +#            memory mapped access on failure (default off)
> +#
> +# @exclusive: #optional open device in exclusive mode (vmix won't work)
> +#             (default off)
> +#
> +# @dsp-policy: #optional set the timing policy of the device (between 0 and 10,
> +#              where smaller number means smaller latency but higher CPU usage)
> +#              or -1 to use fragment mode (option ignored on some platforms)
> +#              (default 5)
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevOssOptions',
> +  'data': {
> +    'oss-in':      'AudiodevOssPerDirectionOptions',
> +    'oss-out':     'AudiodevOssPerDirectionOptions',
> +    '*try-mmap':   'bool',
> +    '*exclusive':  'bool',
> +    '*dsp-policy': 'int' } }
> +
> +##
> +# @AudiodevPaPerDirectionOptions
> +#
> +# Options of the pa backend that are used for both playback and recording.
> +#
> +# @name: #optional name of the sink/source to use
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevPaPerDirectionOptions',
> +  'data': {
> +    '*name': 'str' } }
> +
> +##
> +# @AudiodevPaOptions
> +#
> +# Options of the pa (PulseAudio) audio backend.
> +#
> +# @server: #optional PulseAudio server address (default: let PulseAudio choose)
> +#
> +# @sink: sink specific options
> +#
> +# @source: source specific options
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevPaOptions',
> +  'data': {
> +    '*server': 'str',
> +    'sink':    'AudiodevPaPerDirectionOptions',
> +    'source':  'AudiodevPaPerDirectionOptions' } }
> +
> +##
> +# @AudiodevWavOptions
> +#
> +# Options of the wav audio backend.
> +#
> +# @path: #optional name of the wav file to record (default 'qemu.wav')
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevWavOptions',
> +  'data': {
> +    '*path': 'str' } }
> +
> +
> +##
> +# @AudioFormat
> +#
> +# An enumeration of possible audio formats.
> +#
> +# Since: 2.5
> +##
> +{ 'enum': 'AudioFormat',
> +  'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] }
> +
> +##
> +# @AudiodevDriver
> +#
> +# An enumeration of possible audio backend drivers.
> +#
> +# Since: 2.5
> +##
> +{ 'enum': 'AudiodevDriver',
> +  'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', 'spice',
> +            'wav' ] }
> +
> +##
> +# @AudiodevPerDirectionOptions
> +#
> +# General audio backend options that are used for both playback and recording.
> +#
> +# @fixed-settings: #optional use fixed settings for host input/output.  When
> +#                  off, frequency, channels and format must not be specified
> +#                  (default on)
> +#
> +# @frequency: #optional frequency to use when using fixed settings
> +#             (default 44100)
> +#
> +# @channels: #optional number of channels when using fixed settings
> +#            (default 2)
> +#
> +# @voices: #optional number of voices to use (default 1)
> +#
> +# @format: #optional sample format to use when using fixed settings
> +#          (default s16)
> +#
> +# @buffer-len: #optional the buffer size in microseconds
> +#
> +# @buffer-count: #optional number of buffers
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevPerDirectionOptions',
> +  'data': {
> +    '*fixed-settings': 'bool',
> +    '*frequency':      'int',
> +    '*channels':       'int',
> +    '*voices':         'int',
> +    '*format':         'AudioFormat',
> +    '*buffer-len':     'int',
> +    '*buffer-count':   'int' } }
> +
> +##
> +# @AudiodevCommonOptions
> +#
> +# Captures the common configuration of an audio backend.
> +#
> +# @id: identifier of the backend
> +#
> +# @driver: the backend driver to use
> +#
> +# @in: options of the capture stream
> +#
> +# @out: options of the playback stream
> +#
> +# @timer-period: #optional timer period (in microseconds, 0: use lowest
> +#                possible)
> +#
> +# Since: 2.5
> +##
> +{ 'struct': 'AudiodevCommonOptions',
> +  'data': {
> +    'id':            'str',
> +    'driver':        'AudiodevDriver',
> +    'in':            'AudiodevPerDirectionOptions',
> +    'out':           'AudiodevPerDirectionOptions',
> +    '*timer-period': 'int' } }
> +
> +##
> +# @AudiodevBackendOptions
> +#
> +# Options of an audio backend.
> +#
> +# Since: 2.5
> +##
> +{ 'union': 'Audiodev',
> +  'base': 'AudiodevCommonOptions',
> +  'discriminator': 'driver',
> +  'data': {
> +    'none':      'AudiodevNoOptions',
> +    'alsa':      'AudiodevAlsaOptions',
> +    'coreaudio': 'AudiodevNoOptions',
> +    'dsound':    'AudiodevDsoundOptions',
> +    'oss':       'AudiodevOssOptions',
> +    'pa':        'AudiodevPaOptions',
> +    'sdl':       'AudiodevNoOptions',
> +    'spice':     'AudiodevNoOptions',
> +    'wav':       'AudiodevWavOptions' } }
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
@ 2015-08-20 15:55   ` Marc-André Lureau
  2015-08-20 21:21     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 15:55 UTC (permalink / raw)
  To: Kővágó, Zoltán
  Cc: Markus Armbruster, Gerd Hoffmann, QEMU, Michael Roth

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> The current OptsVisitor flattens the whole structure, if there are same
> named fields under different paths (like `in' and `out' in `Audiodev'),
> the current visitor can't cope with them (for example setting
> `frequency=44100' will set the in's frequency to 44100 and leave out's
> frequency unspecified).
>
> This patch fixes it, by always requiring a complete path in case of
> nested structs.  Fields in the path are separated by dots, similar to C
> structs (without pointers), like `in.frequency' or`out.frequency'.
>
> You must provide a full path even in non-ambigous cases.  The qapi
> flattening commits hopefully ensures that this change doesn't create
> backward compatibility problems.
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  qapi/opts-visitor.c                     | 114 ++++++++++++++++++++++++++------
>  tests/qapi-schema/qapi-schema-test.json |   9 ++-
>  tests/test-opts-visitor.c               |  34 ++++++++++
>  3 files changed, 135 insertions(+), 22 deletions(-)
>
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index aa68814..ff61d42 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -71,6 +71,7 @@ struct OptsVisitor
>       * schema, with a single mandatory scalar member. */
>      ListMode list_mode;
>      GQueue *repeated_opts;
> +    char *repeated_name;
>
>      /* When parsing a list of repeating options as integers, values of the form
>       * "a-b", representing a closed interval, are allowed. Elements in the
> @@ -86,6 +87,9 @@ struct OptsVisitor
>       * not survive or escape the OptsVisitor object.
>       */
>      QemuOpt *fake_id_opt;
> +
> +    /* List of field names leading to the current structure. */
> +    GQueue *nested_names;
>  };
>
>
> @@ -100,6 +104,7 @@ static void
>  opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
>  {
>      GQueue *list;
> +    assert(opt);
>
>      list = g_hash_table_lookup(unprocessed_opts, opt->name);
>      if (list == NULL) {
> @@ -127,6 +132,9 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
>      if (obj) {
>          *obj = g_malloc0(size > 0 ? size : 1);
>      }
> +
> +    g_queue_push_tail(ov->nested_names, (gpointer) name);
> +
>      if (ov->depth++ > 0) {
>          return;
>      }
> @@ -169,6 +177,8 @@ opts_end_struct(Visitor *v, Error **errp)
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      GQueue *any;
>
> +    g_queue_pop_tail(ov->nested_names);
> +
>      if (--ov->depth > 0) {
>          return;
>      }
> @@ -198,15 +208,54 @@ opts_end_implicit_struct(Visitor *v, Error **errp)
>  }
>
>
> +static void
> +sum_strlen(gpointer data, gpointer user_data)
> +{
> +    const char *str = data;
> +    size_t *sum_len = user_data;
> +
> +    if (str) { /* skip NULLs */
> +        *sum_len += strlen(str) + 1;
> +    }
> +}
> +
> +static void
> +append_str(gpointer data, gpointer user_data)
> +{
> +    const char *str = data;
> +    char *concat_str = user_data;
> +
> +    if (str) {
> +        strcat(concat_str, str);
> +        strcat(concat_str, ".");
> +    }
> +}
> +
> +/* lookup a name, using a fully qualified version */
>  static GQueue *
> -lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
> +lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key,
> +                Error **errp)
>  {
> -    GQueue *list;
> +    GQueue *list = NULL;
> +    char *key;
> +    size_t sum_len = strlen(name);
> +
> +    g_queue_foreach(ov->nested_names, sum_strlen, &sum_len);
> +    key = g_malloc(sum_len+1);
> +    key[0] = 0;
> +    g_queue_foreach(ov->nested_names, append_str, key);
> +    strcat(key, name);

Instead of using a GQueue, I think you could use a GArray, and use
g_strjoin() here.

> +
> +    list = g_hash_table_lookup(ov->unprocessed_opts, key);
> +    if (list && out_key) {
> +        *out_key = g_strdup(key);

or just *out_key = key; key = NULL; (g_free accepts NULL)

> +    }
>
> -    list = g_hash_table_lookup(ov->unprocessed_opts, name);
>      if (!list) {
>          error_setg(errp, QERR_MISSING_PARAMETER, name);
>      }
> +
> +    g_free(key);
>      return list;
>  }
>
> @@ -218,7 +267,7 @@ opts_start_list(Visitor *v, const char *name, Error **errp)
>
>      /* we can't traverse a list in a list */
>      assert(ov->list_mode == LM_NONE);
> -    ov->repeated_opts = lookup_distinct(ov, name, errp);

It would make sense to add an assert(ov->repeated_name == NULL) imho,
this could catch potential leaks.

> +    ov->repeated_opts = lookup_distinct(ov, name, &ov->repeated_name, errp);
>      if (ov->repeated_opts != NULL) {
>          ov->list_mode = LM_STARTED;
>      }
> @@ -254,11 +303,9 @@ opts_next_list(Visitor *v, GenericList **list, Error **errp)
>          /* range has been completed, fall through in order to pop option */
>
>      case LM_IN_PROGRESS: {
> -        const QemuOpt *opt;
> -
> -        opt = g_queue_pop_head(ov->repeated_opts);
> +        g_queue_pop_head(ov->repeated_opts);
>          if (g_queue_is_empty(ov->repeated_opts)) {
> -            g_hash_table_remove(ov->unprocessed_opts, opt->name);
> +            g_hash_table_remove(ov->unprocessed_opts, ov->repeated_name);
>              return NULL;
>          }
>          link = &(*list)->next;
> @@ -284,22 +331,28 @@ opts_end_list(Visitor *v, Error **errp)
>             ov->list_mode == LM_SIGNED_INTERVAL ||
>             ov->list_mode == LM_UNSIGNED_INTERVAL);
>      ov->repeated_opts = NULL;
> +
> +    g_free(ov->repeated_name);
> +    ov->repeated_name = NULL;
> +
>      ov->list_mode = LM_NONE;
>  }
>
>
>  static const QemuOpt *
> -lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
> +lookup_scalar(const OptsVisitor *ov, const char *name, char** out_key,
> +              Error **errp)
>  {
>      if (ov->list_mode == LM_NONE) {
>          GQueue *list;
>
>          /* the last occurrence of any QemuOpt takes effect when queried by name
>           */
> -        list = lookup_distinct(ov, name, errp);
> +        list = lookup_distinct(ov, name, out_key, errp);
>          return list ? g_queue_peek_tail(list) : NULL;
>      }
>      assert(ov->list_mode == LM_IN_PROGRESS);
> +    assert(out_key == NULL || *out_key == NULL);
>      return g_queue_peek_head(ov->repeated_opts);
>  }
>
> @@ -321,13 +374,15 @@ opts_type_str(Visitor *v, char **obj, const char *name, Error **errp)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      const QemuOpt *opt;
> +    char *key = NULL;
>
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
>      *obj = g_strdup(opt->str ? opt->str : "");
> -    processed(ov, name);
> +    processed(ov, key);
> +    g_free(key);
>  }
>
>
> @@ -337,8 +392,9 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>  {
>      OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
>      const QemuOpt *opt;
> +    char *key = NULL;
>
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -355,13 +411,15 @@ opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
>          } else {
>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                         "on|yes|y|off|no|n");
> +            g_free(key);
>              return;
>          }
>      } else {
>          *obj = true;
>      }
>
> -    processed(ov, name);
> +    processed(ov, key);
> +    g_free(key);
>  }
>
>
> @@ -373,13 +431,14 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      const char *str;
>      long long val;
>      char *endptr;
> +    char *key = NULL;
>
>      if (ov->list_mode == LM_SIGNED_INTERVAL) {
>          *obj = ov->range_next.s;
>          return;
>      }
>
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -393,11 +452,13 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
>          if (*endptr == '\0') {
>              *obj = val;
> -            processed(ov, name);
> +            processed(ov, key);
> +            g_free(key);
>              return;
>          }
>          if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
>              long long val2;
> +            assert(key == NULL);
>
>              str = endptr + 1;
>              val2 = strtoll(str, &endptr, 0);
> @@ -418,6 +479,7 @@ opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
>      error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                 (ov->list_mode == LM_NONE) ? "an int64 value" :
>                                              "an int64 value or range");
> +    g_free(key);
>  }
>
>
> @@ -429,13 +491,14 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      const char *str;
>      unsigned long long val;
>      char *endptr;
> +    char *key = NULL;
>
>      if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
>          *obj = ov->range_next.u;
>          return;
>      }
>
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -447,11 +510,13 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
>          if (*endptr == '\0') {
>              *obj = val;
> -            processed(ov, name);
> +            processed(ov, key);
> +            g_free(key);
>              return;
>          }
>          if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
>              unsigned long long val2;
> +            assert(key == NULL);
>
>              str = endptr + 1;
>              if (parse_uint_full(str, &val2, 0) == 0 &&
> @@ -470,6 +535,7 @@ opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                 (ov->list_mode == LM_NONE) ? "a uint64 value" :
>                                              "a uint64 value or range");
> +    g_free(key);
>  }
>
>
> @@ -480,8 +546,9 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      const QemuOpt *opt;
>      int64_t val;
>      char *endptr;
> +    char *key = NULL;
>
> -    opt = lookup_scalar(ov, name, errp);
> +    opt = lookup_scalar(ov, name, &key, errp);
>      if (!opt) {
>          return;
>      }
> @@ -491,11 +558,13 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
>      if (val < 0 || *endptr) {
>          error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
>                     "a size value representible as a non-negative int64");
> +        g_free(key);
>          return;
>      }
>
>      *obj = val;
> -    processed(ov, name);
> +    processed(ov, key);
> +    g_free(key);
>  }
>
>
> @@ -506,7 +575,7 @@ opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
>
>      /* we only support a single mandatory scalar field in a list node */
>      assert(ov->list_mode == LM_NONE);
> -    *present = (lookup_distinct(ov, name, NULL) != NULL);
> +    *present = (lookup_distinct(ov, name, NULL, NULL) != NULL);
>  }
>
>
> @@ -517,6 +586,8 @@ opts_visitor_new(const QemuOpts *opts)
>
>      ov = g_malloc0(sizeof *ov);
>
> +    ov->nested_names = g_queue_new();
> +
>      ov->visitor.start_struct = &opts_start_struct;
>      ov->visitor.end_struct   = &opts_end_struct;
>
> @@ -560,6 +631,7 @@ opts_visitor_cleanup(OptsVisitor *ov)
>      if (ov->unprocessed_opts != NULL) {
>          g_hash_table_destroy(ov->unprocessed_opts);
>      }
> +    g_queue_free(ov->nested_names);
>      g_free(ov->fake_id_opt);
>      g_free(ov);
>  }
> diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json
> index c7eaa86..a818eff 100644
> --- a/tests/qapi-schema/qapi-schema-test.json
> +++ b/tests/qapi-schema/qapi-schema-test.json
> @@ -81,6 +81,11 @@
>  { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' },
>    'returns': 'int' }
>
> +# For testing hierarchy support in opts-visitor
> +{ 'struct': 'UserDefOptionsSub',
> +  'data': {
> +    '*nint': 'int' } }
> +
>  # For testing integer range flattening in opts-visitor. The following schema
>  # corresponds to the option format:
>  #
> @@ -94,7 +99,9 @@
>      '*u64' : [ 'uint64' ],
>      '*u16' : [ 'uint16' ],
>      '*i64x':   'int'     ,
> -    '*u64x':   'uint64'  } }
> +    '*u64x':   'uint64'  ,
> +    'sub0':    'UserDefOptionsSub',

make check fails with this patch, you need to update
tests/qapi-schema/qapi-schema-test.out.

> +    'sub1':    'UserDefOptionsSub' } }
>
>  # testing event
>  { 'struct': 'EventStructOne',
> diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c
> index 1c753d9..4393266 100644
> --- a/tests/test-opts-visitor.c
> +++ b/tests/test-opts-visitor.c
> @@ -178,6 +178,34 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
>      g_assert(f->userdef->u64->value == UINT64_MAX);
>  }
>
> +static void
> +expect_both(OptsVisitorFixture *f, gconstpointer test_data)
> +{
> +    expect_ok(f, test_data);
> +    g_assert(f->userdef->sub0->has_nint);
> +    g_assert(f->userdef->sub0->nint == 13);
> +    g_assert(f->userdef->sub1->has_nint);
> +    g_assert(f->userdef->sub1->nint == 17);
> +}
> +
> +static void
> +expect_sub0(OptsVisitorFixture *f, gconstpointer test_data)
> +{
> +    expect_ok(f, test_data);
> +    g_assert(f->userdef->sub0->has_nint);
> +    g_assert(f->userdef->sub0->nint == 13);
> +    g_assert(!f->userdef->sub1->has_nint);
> +}
> +
> +static void
> +expect_sub1(OptsVisitorFixture *f, gconstpointer test_data)
> +{
> +    expect_ok(f, test_data);
> +    g_assert(!f->userdef->sub0->has_nint);
> +    g_assert(f->userdef->sub1->has_nint);
> +    g_assert(f->userdef->sub1->nint == 13);
> +}
> +
>  /* test cases */
>
>  int
> @@ -271,6 +299,12 @@ main(int argc, char **argv)
>      add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
>               "i64=-0x8000000000000000-0x7fffffffffffffff");
>
> +    /* Test nested structs support */
> +    add_test("/visitor/opts/nested/unqualified", &expect_fail, "nint=13");
> +    add_test("/visitor/opts/nested/both",        &expect_both,
> +             "sub0.nint=13,sub1.nint=17");
> +    add_test("/visitor/opts/nested/sub0",        &expect_sub0, "sub0.nint=13");
> +    add_test("/visitor/opts/nested/sub1",        &expect_sub1, "sub1.nint=13");
>      g_test_run();
>      return 0;
>  }
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 09/25] audio: use qapi AudioFormat instead of audfmt_e
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 09/25] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
@ 2015-08-20 16:00   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 16:00 UTC (permalink / raw)
  To: Kővágó, Zoltán
  Cc: Peter Maydell, Michael Walle, QEMU, Gerd Hoffmann

looks good,

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 10/25] audio: -audiodev command line option
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 10/25] audio: -audiodev command line option Kővágó, Zoltán
@ 2015-08-20 17:17   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 17:17 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: Paolo Bonzini, QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> This patch adds an -audiodev command line option, and deprecates the
> QEMU_* environment variables for audio backend configuration.  It's

"its" or simply "the"

> syntax is similar to existing options (-netdev, -device, etc): -audiodev
> driver_name,property=value,...
>
> Audio drivers now get an Audiodev * as config paramters, instead of the
> global audio_option structs.  There is some code in audio/audio_legacy.c
> that converts the old environment variables to audiodev options (this
> way backends do not have to worry about legacy options).  It also
> contains a replacement of -audio-help, which prints out the equivalent
> -audiodev based config of the currently specified environment variables.

I guess the option should be deprecated though, perhaps not even
visible in -help.

>
> Although now it's possible to specify multiple -audiodev options on
> command line, multiple audio backends are not supported yet.
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/Makefile.objs     |   2 +-
>  audio/alsaaudio.c       | 311 ++++++--------------
>  audio/audio.c           | 760 ++++++++++++++----------------------------------
>  audio/audio.h           |  23 +-
>  audio/audio_int.h       |  23 +-
>  audio/audio_legacy.c    | 328 +++++++++++++++++++++
>  audio/audio_template.h  |  13 +-
>  audio/coreaudio.c       |  49 +---
>  audio/dsound_template.h |   6 +-
>  audio/dsoundaudio.c     |  60 ++--
>  audio/noaudio.c         |   3 +-
>  audio/ossaudio.c        | 155 +++-------
>  audio/paaudio.c         |  82 ++----
>  audio/sdlaudio.c        |  24 +-
>  audio/spiceaudio.c      |   7 +-
>  audio/wavaudio.c        |  60 +---
>  qemu-options.hx         | 226 +++++++++++++-
>  vl.c                    |  10 +-
>  18 files changed, 1015 insertions(+), 1127 deletions(-)
>  create mode 100644 audio/audio_legacy.c
>

Quite a large patch, perhaps it could be splitted?

> diff --git a/audio/Makefile.objs b/audio/Makefile.objs
> index 481d1aa..9d8f579 100644
> --- a/audio/Makefile.objs
> +++ b/audio/Makefile.objs
> @@ -1,4 +1,4 @@
> -common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
> +common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
>  common-obj-$(CONFIG_SDL) += sdlaudio.o
>  common-obj-$(CONFIG_OSS) += ossaudio.o
>  common-obj-$(CONFIG_SPICE) += spiceaudio.o
> diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
> index 2b28b99..cfe4aec 100644
> --- a/audio/alsaaudio.c
> +++ b/audio/alsaaudio.c
> @@ -22,6 +22,7 @@
>   * THE SOFTWARE.
>   */
>  #include <alsa/asoundlib.h>
> +#include "qapi-visit.h"
>  #include "qemu-common.h"
>  #include "qemu/main-loop.h"
>  #include "audio.h"
> @@ -34,28 +35,9 @@
>  #define AUDIO_CAP "alsa"
>  #include "audio_int.h"
>
> -typedef struct ALSAConf {
> -    int size_in_usec_in;
> -    int size_in_usec_out;
> -    const char *pcm_name_in;
> -    const char *pcm_name_out;
> -    unsigned int buffer_size_in;
> -    unsigned int period_size_in;
> -    unsigned int buffer_size_out;
> -    unsigned int period_size_out;
> -    unsigned int threshold;
> -
> -    int buffer_size_in_overridden;
> -    int period_size_in_overridden;
> -
> -    int buffer_size_out_overridden;
> -    int period_size_out_overridden;
> -} ALSAConf;
> -
>  struct pollhlp {
>      snd_pcm_t *handle;
>      struct pollfd *pfds;
> -    ALSAConf *conf;
>      int count;
>      int mask;
>  };
> @@ -67,6 +49,7 @@ typedef struct ALSAVoiceOut {
>      void *pcm_buf;
>      snd_pcm_t *handle;
>      struct pollhlp pollhlp;
> +    Audiodev *dev;
>  } ALSAVoiceOut;
>
>  typedef struct ALSAVoiceIn {
> @@ -74,16 +57,13 @@ typedef struct ALSAVoiceIn {
>      snd_pcm_t *handle;
>      void *pcm_buf;
>      struct pollhlp pollhlp;
> +    Audiodev *dev;
>  } ALSAVoiceIn;
>
>  struct alsa_params_req {
>      int freq;
>      snd_pcm_format_t fmt;
>      int nchannels;
> -    int size_in_usec;
> -    int override_mask;
> -    unsigned int buffer_size;
> -    unsigned int period_size;
>  };
>
>  struct alsa_params_obt {
> @@ -409,7 +389,8 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
>
>  static void alsa_dump_info (struct alsa_params_req *req,
>                              struct alsa_params_obt *obt,
> -                            snd_pcm_format_t obtfmt)
> +                            snd_pcm_format_t obtfmt,
> +                            AudiodevPerDirectionOptions *pdo)
>  {
>      dolog ("parameter | requested value | obtained value\n");
>      dolog ("format    |      %10d |     %10d\n", req->fmt, obtfmt);
> @@ -417,8 +398,9 @@ static void alsa_dump_info (struct alsa_params_req *req,
>             req->nchannels, obt->nchannels);
>      dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
>      dolog ("============================================\n");
> -    dolog ("requested: buffer size %d period size %d\n",
> -           req->buffer_size, req->period_size);
> +    dolog ("requested: buffer size %" PRId64 " buffer count %" PRId64 "\n",
> +           pdo->has_buffer_len ? pdo->buffer_len : 0,
> +           pdo->has_buffer_len ? pdo->buffer_len : 0);

buffer size & buffer count are both buffer_len here, you should fix this

>      dolog ("obtained: samples %ld\n", obt->samples);
>  }
>
> @@ -452,23 +434,25 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
>      }
>  }
>
> -static int alsa_open (int in, struct alsa_params_req *req,
> -                      struct alsa_params_obt *obt, snd_pcm_t **handlep,
> -                      ALSAConf *conf)
> +static int alsa_open(bool in, struct alsa_params_req *req,
> +                     struct alsa_params_obt *obt, snd_pcm_t **handlep,
> +                     Audiodev *dev)
>  {
> +    AudiodevPerDirectionOptions *pdo = in ? dev->in : dev->out;
> +    AudiodevAlsaOptions *aopts = dev->alsa;
> +    AudiodevAlsaPerDirectionOptions *apdo =
> +        in ? aopts->alsa_in : aopts->alsa_out;
>      snd_pcm_t *handle;
>      snd_pcm_hw_params_t *hw_params;
>      int err;
> -    int size_in_usec;
>      unsigned int freq, nchannels;
> -    const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out;
> +    const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
>      snd_pcm_uframes_t obt_buffer_size;
>      const char *typ = in ? "ADC" : "DAC";
>      snd_pcm_format_t obtfmt;
>
>      freq = req->freq;
>      nchannels = req->nchannels;
> -    size_in_usec = req->size_in_usec;
>
>      snd_pcm_hw_params_alloca (&hw_params);
>
> @@ -528,79 +512,49 @@ static int alsa_open (int in, struct alsa_params_req *req,
>          goto err;
>      }
>
> -    if (req->buffer_size) {
> -        unsigned long obt;
> +    if (pdo->buffer_count) {
> +        if (pdo->buffer_len) {
> +            int64_t req = pdo->buffer_len * pdo->buffer_count;
>
> -        if (size_in_usec) {
>              int dir = 0;
> -            unsigned int btime = req->buffer_size;
> +            unsigned int btime = req;
>
> -            err = snd_pcm_hw_params_set_buffer_time_near (
> -                handle,
> -                hw_params,
> -                &btime,
> -                &dir
> -                );
> -            obt = btime;
> -        }
> -        else {
> -            snd_pcm_uframes_t bsize = req->buffer_size;
> +            err = snd_pcm_hw_params_set_buffer_time_near(
> +                handle, hw_params, &btime, &dir);
>
> -            err = snd_pcm_hw_params_set_buffer_size_near (
> -                handle,
> -                hw_params,
> -                &bsize
> -                );
> -            obt = bsize;
> -        }
> -        if (err < 0) {
> -            alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
> -                          size_in_usec ? "time" : "size", req->buffer_size);
> -            goto err;
> -        }
> +            if (err < 0) {
> +                alsa_logerr2(err, typ,
> +                             "Failed to set buffer time to %" PRId64 "\n",
> +                             req);
> +                goto err;
> +            }
>
> -        if ((req->override_mask & 2) && (obt - req->buffer_size))
> -            dolog ("Requested buffer %s %u was rejected, using %lu\n",
> -                   size_in_usec ? "time" : "size", req->buffer_size, obt);
> +            if (pdo->has_buffer_count && btime != req) {
> +                dolog("Requested buffer time %" PRId64
> +                      " was rejected, using %u\n", req, btime);
> +            }
> +        } else {
> +            dolog("Can't set buffer_count without buffer_size!\n");
> +        }
>      }
>
> -    if (req->period_size) {
> -        unsigned long obt;
> +    if (pdo->buffer_len) {
> +        int dir = 0;
> +        unsigned int ptime = pdo->buffer_len;
>
> -        if (size_in_usec) {
> -            int dir = 0;
> -            unsigned int ptime = req->period_size;
> -
> -            err = snd_pcm_hw_params_set_period_time_near (
> -                handle,
> -                hw_params,
> -                &ptime,
> -                &dir
> -                );
> -            obt = ptime;
> -        }
> -        else {
> -            int dir = 0;
> -            snd_pcm_uframes_t psize = req->period_size;
> -
> -            err = snd_pcm_hw_params_set_period_size_near (
> -                handle,
> -                hw_params,
> -                &psize,
> -                &dir
> -                );
> -            obt = psize;
> -        }
> +        err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
> +                                                     &dir);
>
>          if (err < 0) {
> -            alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
> -                          size_in_usec ? "time" : "size", req->period_size);
> +            alsa_logerr2(err, typ, "Failed to set period time to %" PRId64 "\n",
> +                         pdo->buffer_len);
>              goto err;
>          }
>
> -        if (((req->override_mask & 1) && (obt - req->period_size)))
> -            dolog ("Requested period %s %u was rejected, using %lu\n",
> -                   size_in_usec ? "time" : "size", req->period_size, obt);
> +        if (pdo->has_buffer_len && ptime != pdo->buffer_len) {
> +            dolog("Requested period time %" PRId64 " was rejected, using %d\n",
> +                  pdo->buffer_len, ptime);
> +        }
>      }
>
>      err = snd_pcm_hw_params (handle, hw_params);
> @@ -632,33 +586,10 @@ static int alsa_open (int in, struct alsa_params_req *req,
>          goto err;
>      }
>
> -    if (!in && conf->threshold) {
> -        snd_pcm_uframes_t threshold;
> -        int bytes_per_sec;
> -
> -        bytes_per_sec = freq << (nchannels == 2);
> -
> -        switch (obt->fmt) {
> -        case AUDIO_FORMAT_S8:
> -        case AUDIO_FORMAT_U8:
> -            break;
> -
> -        case AUDIO_FORMAT_S16:
> -        case AUDIO_FORMAT_U16:
> -            bytes_per_sec <<= 1;
> -            break;
> -
> -        case AUDIO_FORMAT_S32:
> -        case AUDIO_FORMAT_U32:
> -            bytes_per_sec <<= 2;
> -            break;
> -
> -        default:
> -            abort();
> -        }
> -
> -        threshold = (conf->threshold * bytes_per_sec) / 1000;
> -        alsa_set_threshold (handle, threshold);
> +    if (!in && aopts->has_threshold && aopts->threshold) {
> +        struct audsettings as = { .freq = freq };
> +        alsa_set_threshold(handle,
> +                           audio_buffer_frames(pdo, &as, aopts->threshold));
>      }
>
>      obt->nchannels = nchannels;
> @@ -671,11 +602,11 @@ static int alsa_open (int in, struct alsa_params_req *req,
>           obt->nchannels != req->nchannels ||
>           obt->freq != req->freq) {
>          dolog ("Audio parameters for %s\n", typ);
> -        alsa_dump_info (req, obt, obtfmt);
> +        alsa_dump_info (req, obt, obtfmt, pdo);
>      }
>
>  #ifdef DEBUG
> -    alsa_dump_info (req, obt, obtfmt);
> +    alsa_dump_info (req, obt, obtfmt, pdo);
>  #endif
>      return 0;
>
> @@ -801,19 +732,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
>      struct alsa_params_obt obt;
>      snd_pcm_t *handle;
>      struct audsettings obt_as;
> -    ALSAConf *conf = drv_opaque;
> +    Audiodev *dev = drv_opaque;
>
>      req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
>      req.freq = as->freq;
>      req.nchannels = as->nchannels;
> -    req.period_size = conf->period_size_out;
> -    req.buffer_size = conf->buffer_size_out;
> -    req.size_in_usec = conf->size_in_usec_out;
> -    req.override_mask =
> -        (conf->period_size_out_overridden ? 1 : 0) |
> -        (conf->buffer_size_out_overridden ? 2 : 0);
>
> -    if (alsa_open (0, &req, &obt, &handle, conf)) {
> +    if (alsa_open (0, &req, &obt, &handle, dev)) {
>          return -1;
>      }
>
> @@ -834,7 +759,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
>      }
>
>      alsa->handle = handle;
> -    alsa->pollhlp.conf = conf;
> +    alsa->dev = dev;
>      return 0;
>  }
>
> @@ -874,16 +799,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
>  static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
>  {
>      ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
> +    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_out;
>
>      switch (cmd) {
>      case VOICE_ENABLE:
>          {
> -            va_list ap;
> -            int poll_mode;
> -
> -            va_start (ap, cmd);
> -            poll_mode = va_arg (ap, int);
> -            va_end (ap);
> +            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
>
>              ldebug ("enabling voice\n");
>              if (poll_mode && alsa_poll_out (hw)) {
> @@ -912,19 +833,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      struct alsa_params_obt obt;
>      snd_pcm_t *handle;
>      struct audsettings obt_as;
> -    ALSAConf *conf = drv_opaque;
> +    Audiodev *dev = drv_opaque;
>
>      req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
>      req.freq = as->freq;
>      req.nchannels = as->nchannels;
> -    req.period_size = conf->period_size_in;
> -    req.buffer_size = conf->buffer_size_in;
> -    req.size_in_usec = conf->size_in_usec_in;
> -    req.override_mask =
> -        (conf->period_size_in_overridden ? 1 : 0) |
> -        (conf->buffer_size_in_overridden ? 2 : 0);
>
> -    if (alsa_open (1, &req, &obt, &handle, conf)) {
> +    if (alsa_open (1, &req, &obt, &handle, dev)) {
>          return -1;
>      }
>
> @@ -945,7 +860,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      }
>
>      alsa->handle = handle;
> -    alsa->pollhlp.conf = conf;
> +    alsa->dev = dev;
>      return 0;
>  }
>
> @@ -1087,16 +1002,12 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
>  static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
>  {
>      ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
> +    AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->alsa->alsa_in;
>
>      switch (cmd) {
>      case VOICE_ENABLE:
>          {
> -            va_list ap;
> -            int poll_mode;
> -
> -            va_start (ap, cmd);
> -            poll_mode = va_arg (ap, int);
> -            va_end (ap);
> +            bool poll_mode = !apdo->has_try_poll || apdo->try_poll;
>
>              ldebug ("enabling voice\n");
>              if (poll_mode && alsa_poll_in (hw)) {
> @@ -1119,88 +1030,35 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
>      return -1;
>  }
>
> -static ALSAConf glob_conf = {
> -    .buffer_size_out = 4096,
> -    .period_size_out = 1024,
> -    .pcm_name_out = "default",
> -    .pcm_name_in = "default",
> -};
> -
> -static void *alsa_audio_init (void)
> +static void *alsa_audio_init(Audiodev *dev)
>  {
> -    ALSAConf *conf = g_malloc(sizeof(ALSAConf));
> -    *conf = glob_conf;
> -    return conf;
> +    assert(dev->kind == AUDIODEV_DRIVER_ALSA);
> +
> +    /* need to define them, as otherwise alsa produces no sound
> +     * doesn't set has_* so alsa_open can identify it wasn't set by the user */
> +    if (!dev->out->has_buffer_count) {
> +        dev->out->buffer_count = 4;
> +    }
> +    if (!dev->out->has_buffer_len) {
> +        dev->out->buffer_len = 23219; /* 1024 frames assuming 44100Hz */
> +    }

how did you compute that? Would be worth leaving that in the code.

> +
> +    /* OptsVisitor sets unspecified optional fields to zero, but do not depend
> +     * on it... */
> +    if (!dev->in->has_buffer_count) {
> +        dev->in->buffer_count = 0;
> +    }
> +    if (!dev->in->has_buffer_len) {
> +        dev->in->buffer_len = 0;
> +    }
> +
> +    return dev;
>  }
>
>  static void alsa_audio_fini (void *opaque)
>  {
> -    g_free(opaque);
>  }
>
> -static struct audio_option alsa_options[] = {
> -    {
> -        .name        = "DAC_SIZE_IN_USEC",
> -        .tag         = AUD_OPT_BOOL,
> -        .valp        = &glob_conf.size_in_usec_out,
> -        .descr       = "DAC period/buffer size in microseconds (otherwise in frames)"
> -    },
> -    {
> -        .name        = "DAC_PERIOD_SIZE",
> -        .tag         = AUD_OPT_INT,
> -        .valp        = &glob_conf.period_size_out,
> -        .descr       = "DAC period size (0 to go with system default)",
> -        .overriddenp = &glob_conf.period_size_out_overridden
> -    },
> -    {
> -        .name        = "DAC_BUFFER_SIZE",
> -        .tag         = AUD_OPT_INT,
> -        .valp        = &glob_conf.buffer_size_out,
> -        .descr       = "DAC buffer size (0 to go with system default)",
> -        .overriddenp = &glob_conf.buffer_size_out_overridden
> -    },
> -    {
> -        .name        = "ADC_SIZE_IN_USEC",
> -        .tag         = AUD_OPT_BOOL,
> -        .valp        = &glob_conf.size_in_usec_in,
> -        .descr       =
> -        "ADC period/buffer size in microseconds (otherwise in frames)"
> -    },
> -    {
> -        .name        = "ADC_PERIOD_SIZE",
> -        .tag         = AUD_OPT_INT,
> -        .valp        = &glob_conf.period_size_in,
> -        .descr       = "ADC period size (0 to go with system default)",
> -        .overriddenp = &glob_conf.period_size_in_overridden
> -    },
> -    {
> -        .name        = "ADC_BUFFER_SIZE",
> -        .tag         = AUD_OPT_INT,
> -        .valp        = &glob_conf.buffer_size_in,
> -        .descr       = "ADC buffer size (0 to go with system default)",
> -        .overriddenp = &glob_conf.buffer_size_in_overridden
> -    },
> -    {
> -        .name        = "THRESHOLD",
> -        .tag         = AUD_OPT_INT,
> -        .valp        = &glob_conf.threshold,
> -        .descr       = "(undocumented)"
> -    },
> -    {
> -        .name        = "DAC_DEV",
> -        .tag         = AUD_OPT_STR,
> -        .valp        = &glob_conf.pcm_name_out,
> -        .descr       = "DAC device name (for instance dmix)"
> -    },
> -    {
> -        .name        = "ADC_DEV",
> -        .tag         = AUD_OPT_STR,
> -        .valp        = &glob_conf.pcm_name_in,
> -        .descr       = "ADC device name"
> -    },
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops alsa_pcm_ops = {
>      .init_out = alsa_init_out,
>      .fini_out = alsa_fini_out,
> @@ -1218,7 +1076,6 @@ static struct audio_pcm_ops alsa_pcm_ops = {
>  struct audio_driver alsa_audio_driver = {
>      .name           = "alsa",
>      .descr          = "ALSA http://www.alsa-project.org",
> -    .options        = alsa_options,
>      .init           = alsa_audio_init,
>      .fini           = alsa_audio_fini,
>      .pcm_ops        = &alsa_pcm_ops,
> diff --git a/audio/audio.c b/audio/audio.c
> index 334c935..08ac15e 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -24,7 +24,10 @@
>  #include "hw/hw.h"
>  #include "audio.h"
>  #include "monitor/monitor.h"
> +#include "qapi-visit.h"
> +#include "qapi/opts-visitor.h"
>  #include "qemu/timer.h"
> +#include "qemu/config-file.h"
>  #include "sysemu/sysemu.h"
>
>  #define AUDIO_CAP "audio"
> @@ -42,59 +45,14 @@
>     The 1st one is the one used by default, that is the reason
>      that we generate the list.
>  */
> -static struct audio_driver *drvtab[] = {
> +struct audio_driver *drvtab[] = {
>  #ifdef CONFIG_SPICE
>      &spice_audio_driver,
>  #endif
>      CONFIG_AUDIO_DRIVERS
>      &no_audio_driver,
> -    &wav_audio_driver
> -};
> -
> -struct fixed_settings {
> -    int enabled;
> -    int nb_voices;
> -    int greedy;
> -    struct audsettings settings;
> -};
> -
> -static struct {
> -    struct fixed_settings fixed_out;
> -    struct fixed_settings fixed_in;
> -    union {
> -        int hertz;
> -        int64_t ticks;
> -    } period;
> -    int try_poll_in;
> -    int try_poll_out;
> -} conf = {
> -    .fixed_out = { /* DAC fixed settings */
> -        .enabled = 1,
> -        .nb_voices = 1,
> -        .greedy = 1,
> -        .settings = {
> -            .freq = 44100,
> -            .nchannels = 2,
> -            .fmt = AUDIO_FORMAT_S16,
> -            .endianness =  AUDIO_HOST_ENDIANNESS,
> -        }
> -    },
> -
> -    .fixed_in = { /* ADC fixed settings */
> -        .enabled = 1,
> -        .nb_voices = 1,
> -        .greedy = 1,
> -        .settings = {
> -            .freq = 44100,
> -            .nchannels = 2,
> -            .fmt = AUDIO_FORMAT_S16,
> -            .endianness = AUDIO_HOST_ENDIANNESS,
> -        }
> -    },
> -
> -    .period = { .hertz = 100 },
> -    .try_poll_in = 1,
> -    .try_poll_out = 1,
> +    &wav_audio_driver,
> +    NULL
>  };
>
>  static AudioState glob_audio_state;
> @@ -113,9 +71,6 @@ const struct mixeng_volume nominal_volume = {
>  #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
>  #error No its not
>  #else
> -static void audio_print_options (const char *prefix,
> -                                 struct audio_option *opt);
> -
>  int audio_bug (const char *funcname, int cond)
>  {
>      if (cond) {
> @@ -123,16 +78,9 @@ int audio_bug (const char *funcname, int cond)
>
>          AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
>          if (!shown) {
> -            struct audio_driver *d;
> -
>              shown = 1;
>              AUD_log (NULL, "Save all your work and restart without audio\n");
> -            AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
>              AUD_log (NULL, "I am sorry\n");
> -            d = glob_audio_state.drv;
> -            if (d) {
> -                audio_print_options (d->name, d->options);
> -            }
>          }
>          AUD_log (NULL, "Context:\n");
>
> @@ -194,139 +142,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size)
>      return g_malloc0 (len);
>  }
>
> -static char *audio_alloc_prefix (const char *s)
> -{
> -    const char qemu_prefix[] = "QEMU_";
> -    size_t len, i;
> -    char *r, *u;
> -
> -    if (!s) {
> -        return NULL;
> -    }
> -
> -    len = strlen (s);
> -    r = g_malloc (len + sizeof (qemu_prefix));
> -
> -    u = r + sizeof (qemu_prefix) - 1;
> -
> -    pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
> -    pstrcat (r, len + sizeof (qemu_prefix), s);
> -
> -    for (i = 0; i < len; ++i) {
> -        u[i] = qemu_toupper(u[i]);
> -    }
> -
> -    return r;
> -}
> -
> -static const char *audio_audfmt_to_string (AudioFormat fmt)
> -{
> -    switch (fmt) {
> -    case AUDIO_FORMAT_U8:
> -        return "U8";
> -
> -    case AUDIO_FORMAT_U16:
> -        return "U16";
> -
> -    case AUDIO_FORMAT_S8:
> -        return "S8";
> -
> -    case AUDIO_FORMAT_S16:
> -        return "S16";
> -
> -    case AUDIO_FORMAT_U32:
> -        return "U32";
> -
> -    case AUDIO_FORMAT_S32:
> -        return "S32";
> -
> -    default:
> -        abort();
> -    }
> -
> -    dolog ("Bogus audfmt %d returning S16\n", fmt);
> -    return "S16";
> -}
> -
> -static AudioFormat audio_string_to_audfmt (const char *s, AudioFormat defval,
> -                                        int *defaultp)
> -{
> -    if (!strcasecmp (s, "u8")) {
> -        *defaultp = 0;
> -        return AUDIO_FORMAT_U8;
> -    }
> -    else if (!strcasecmp (s, "u16")) {
> -        *defaultp = 0;
> -        return AUDIO_FORMAT_U16;
> -    }
> -    else if (!strcasecmp (s, "u32")) {
> -        *defaultp = 0;
> -        return AUDIO_FORMAT_U32;
> -    }
> -    else if (!strcasecmp (s, "s8")) {
> -        *defaultp = 0;
> -        return AUDIO_FORMAT_S8;
> -    }
> -    else if (!strcasecmp (s, "s16")) {
> -        *defaultp = 0;
> -        return AUDIO_FORMAT_S16;
> -    }
> -    else if (!strcasecmp (s, "s32")) {
> -        *defaultp = 0;
> -        return AUDIO_FORMAT_S32;
> -    }
> -    else {
> -        dolog ("Bogus audio format `%s' using %s\n",
> -               s, audio_audfmt_to_string (defval));
> -        *defaultp = 1;
> -        return defval;
> -    }
> -}
> -
> -static AudioFormat audio_get_conf_fmt (const char *envname,
> -                                    AudioFormat defval,
> -                                    int *defaultp)
> -{
> -    const char *var = getenv (envname);
> -    if (!var) {
> -        *defaultp = 1;
> -        return defval;
> -    }
> -    return audio_string_to_audfmt (var, defval, defaultp);
> -}
> -
> -static int audio_get_conf_int (const char *key, int defval, int *defaultp)
> -{
> -    int val;
> -    char *strval;
> -
> -    strval = getenv (key);
> -    if (strval) {
> -        *defaultp = 0;
> -        val = atoi (strval);
> -        return val;
> -    }
> -    else {
> -        *defaultp = 1;
> -        return defval;
> -    }
> -}
> -
> -static const char *audio_get_conf_str (const char *key,
> -                                       const char *defval,
> -                                       int *defaultp)
> -{
> -    const char *val = getenv (key);
> -    if (!val) {
> -        *defaultp = 1;
> -        return defval;
> -    }
> -    else {
> -        *defaultp = 0;
> -        return val;
> -    }
> -}
> -
>  void AUD_vlog (const char *cap, const char *fmt, va_list ap)
>  {
>      if (cap) {
> @@ -345,161 +160,6 @@ void AUD_log (const char *cap, const char *fmt, ...)
>      va_end (ap);
>  }
>
> -static void audio_print_options (const char *prefix,
> -                                 struct audio_option *opt)
> -{
> -    char *uprefix;
> -
> -    if (!prefix) {
> -        dolog ("No prefix specified\n");
> -        return;
> -    }
> -
> -    if (!opt) {
> -        dolog ("No options\n");
> -        return;
> -    }
> -
> -    uprefix = audio_alloc_prefix (prefix);
> -
> -    for (; opt->name; opt++) {
> -        const char *state = "default";
> -        printf ("  %s_%s: ", uprefix, opt->name);
> -
> -        if (opt->overriddenp && *opt->overriddenp) {
> -            state = "current";
> -        }
> -
> -        switch (opt->tag) {
> -        case AUD_OPT_BOOL:
> -            {
> -                int *intp = opt->valp;
> -                printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
> -            }
> -            break;
> -
> -        case AUD_OPT_INT:
> -            {
> -                int *intp = opt->valp;
> -                printf ("integer, %s = %d\n", state, *intp);
> -            }
> -            break;
> -
> -        case AUD_OPT_FMT:
> -            {
> -                AudioFormat *fmtp = opt->valp;
> -                printf (
> -                    "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
> -                    state,
> -                    audio_audfmt_to_string (*fmtp)
> -                    );
> -            }
> -            break;
> -
> -        case AUD_OPT_STR:
> -            {
> -                const char **strp = opt->valp;
> -                printf ("string, %s = %s\n",
> -                        state,
> -                        *strp ? *strp : "(not set)");
> -            }
> -            break;
> -
> -        default:
> -            printf ("???\n");
> -            dolog ("Bad value tag for option %s_%s %d\n",
> -                   uprefix, opt->name, opt->tag);
> -            break;
> -        }
> -        printf ("    %s\n", opt->descr);
> -    }
> -
> -    g_free (uprefix);
> -}
> -
> -static void audio_process_options (const char *prefix,
> -                                   struct audio_option *opt)
> -{
> -    char *optname;
> -    const char qemu_prefix[] = "QEMU_";
> -    size_t preflen, optlen;
> -
> -    if (audio_bug (AUDIO_FUNC, !prefix)) {
> -        dolog ("prefix = NULL\n");
> -        return;
> -    }
> -
> -    if (audio_bug (AUDIO_FUNC, !opt)) {
> -        dolog ("opt = NULL\n");
> -        return;
> -    }
> -
> -    preflen = strlen (prefix);
> -
> -    for (; opt->name; opt++) {
> -        size_t len, i;
> -        int def;
> -
> -        if (!opt->valp) {
> -            dolog ("Option value pointer for `%s' is not set\n",
> -                   opt->name);
> -            continue;
> -        }
> -
> -        len = strlen (opt->name);
> -        /* len of opt->name + len of prefix + size of qemu_prefix
> -         * (includes trailing zero) + zero + underscore (on behalf of
> -         * sizeof) */
> -        optlen = len + preflen + sizeof (qemu_prefix) + 1;
> -        optname = g_malloc (optlen);
> -
> -        pstrcpy (optname, optlen, qemu_prefix);
> -
> -        /* copy while upper-casing, including trailing zero */
> -        for (i = 0; i <= preflen; ++i) {
> -            optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]);
> -        }
> -        pstrcat (optname, optlen, "_");
> -        pstrcat (optname, optlen, opt->name);
> -
> -        def = 1;
> -        switch (opt->tag) {
> -        case AUD_OPT_BOOL:
> -        case AUD_OPT_INT:
> -            {
> -                int *intp = opt->valp;
> -                *intp = audio_get_conf_int (optname, *intp, &def);
> -            }
> -            break;
> -
> -        case AUD_OPT_FMT:
> -            {
> -                AudioFormat *fmtp = opt->valp;
> -                *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
> -            }
> -            break;
> -
> -        case AUD_OPT_STR:
> -            {
> -                const char **strp = opt->valp;
> -                *strp = audio_get_conf_str (optname, *strp, &def);
> -            }
> -            break;
> -
> -        default:
> -            dolog ("Bad value tag for option `%s' - %d\n",
> -                   optname, opt->tag);
> -            break;
> -        }
> -
> -        if (!opt->overriddenp) {
> -            opt->overriddenp = &opt->overridden;
> -        }
> -        *opt->overriddenp = !def;
> -        g_free (optname);
> -    }
> -}
> -
>  static void audio_print_settings (struct audsettings *as)
>  {
>      dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
> @@ -1120,7 +780,7 @@ static void audio_reset_timer (AudioState *s)
>  {
>      if (audio_is_timer_needed ()) {
>          timer_mod (s->ts,
> -            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
> +            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
>      }
>      else {
>          timer_del (s->ts);
> @@ -1196,7 +856,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
>              if (!hw->enabled) {
>                  hw->enabled = 1;
>                  if (s->vm_running) {
> -                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
> +                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
>                      audio_reset_timer (s);
>                  }
>              }
> @@ -1241,7 +901,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
>              if (!hw->enabled) {
>                  hw->enabled = 1;
>                  if (s->vm_running) {
> -                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
> +                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
>                      audio_reset_timer (s);
>                  }
>              }
> @@ -1558,168 +1218,10 @@ void audio_run (const char *msg)
>  #endif
>  }
>
> -static struct audio_option audio_options[] = {
> -    /* DAC */
> -    {
> -        .name  = "DAC_FIXED_SETTINGS",
> -        .tag   = AUD_OPT_BOOL,
> -        .valp  = &conf.fixed_out.enabled,
> -        .descr = "Use fixed settings for host DAC"
> -    },
> -    {
> -        .name  = "DAC_FIXED_FREQ",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.fixed_out.settings.freq,
> -        .descr = "Frequency for fixed host DAC"
> -    },
> -    {
> -        .name  = "DAC_FIXED_FMT",
> -        .tag   = AUD_OPT_FMT,
> -        .valp  = &conf.fixed_out.settings.fmt,
> -        .descr = "Format for fixed host DAC"
> -    },
> -    {
> -        .name  = "DAC_FIXED_CHANNELS",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.fixed_out.settings.nchannels,
> -        .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
> -    },
> -    {
> -        .name  = "DAC_VOICES",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.fixed_out.nb_voices,
> -        .descr = "Number of voices for DAC"
> -    },
> -    {
> -        .name  = "DAC_TRY_POLL",
> -        .tag   = AUD_OPT_BOOL,
> -        .valp  = &conf.try_poll_out,
> -        .descr = "Attempt using poll mode for DAC"
> -    },
> -    /* ADC */
> -    {
> -        .name  = "ADC_FIXED_SETTINGS",
> -        .tag   = AUD_OPT_BOOL,
> -        .valp  = &conf.fixed_in.enabled,
> -        .descr = "Use fixed settings for host ADC"
> -    },
> -    {
> -        .name  = "ADC_FIXED_FREQ",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.fixed_in.settings.freq,
> -        .descr = "Frequency for fixed host ADC"
> -    },
> -    {
> -        .name  = "ADC_FIXED_FMT",
> -        .tag   = AUD_OPT_FMT,
> -        .valp  = &conf.fixed_in.settings.fmt,
> -        .descr = "Format for fixed host ADC"
> -    },
> -    {
> -        .name  = "ADC_FIXED_CHANNELS",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.fixed_in.settings.nchannels,
> -        .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
> -    },
> -    {
> -        .name  = "ADC_VOICES",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.fixed_in.nb_voices,
> -        .descr = "Number of voices for ADC"
> -    },
> -    {
> -        .name  = "ADC_TRY_POLL",
> -        .tag   = AUD_OPT_BOOL,
> -        .valp  = &conf.try_poll_in,
> -        .descr = "Attempt using poll mode for ADC"
> -    },
> -    /* Misc */
> -    {
> -        .name  = "TIMER_PERIOD",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.period.hertz,
> -        .descr = "Timer period in HZ (0 - use lowest possible)"
> -    },
> -    { /* End of list */ }
> -};
> -
> -static void audio_pp_nb_voices (const char *typ, int nb)
> +static int audio_driver_init(AudioState *s, struct audio_driver *drv,
> +                             Audiodev *dev)
>  {
> -    switch (nb) {
> -    case 0:
> -        printf ("Does not support %s\n", typ);
> -        break;
> -    case 1:
> -        printf ("One %s voice\n", typ);
> -        break;
> -    case INT_MAX:
> -        printf ("Theoretically supports many %s voices\n", typ);
> -        break;
> -    default:
> -        printf ("Theoretically supports up to %d %s voices\n", nb, typ);
> -        break;
> -    }
> -
> -}
> -
> -void AUD_help (void)
> -{
> -    size_t i;
> -
> -    audio_process_options ("AUDIO", audio_options);
> -    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
> -        struct audio_driver *d = drvtab[i];
> -        if (d->options) {
> -            audio_process_options (d->name, d->options);
> -        }
> -    }
> -
> -    printf ("Audio options:\n");
> -    audio_print_options ("AUDIO", audio_options);
> -    printf ("\n");
> -
> -    printf ("Available drivers:\n");
> -
> -    for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
> -        struct audio_driver *d = drvtab[i];
> -
> -        printf ("Name: %s\n", d->name);
> -        printf ("Description: %s\n", d->descr);
> -
> -        audio_pp_nb_voices ("playback", d->max_voices_out);
> -        audio_pp_nb_voices ("capture", d->max_voices_in);
> -
> -        if (d->options) {
> -            printf ("Options:\n");
> -            audio_print_options (d->name, d->options);
> -        }
> -        else {
> -            printf ("No options\n");
> -        }
> -        printf ("\n");
> -    }
> -
> -    printf (
> -        "Options are settable through environment variables.\n"
> -        "Example:\n"
> -#ifdef _WIN32
> -        "  set QEMU_AUDIO_DRV=wav\n"
> -        "  set QEMU_WAV_PATH=c:\\tune.wav\n"
> -#else
> -        "  export QEMU_AUDIO_DRV=wav\n"
> -        "  export QEMU_WAV_PATH=$HOME/tune.wav\n"
> -        "(for csh replace export with setenv in the above)\n"
> -#endif
> -        "  qemu ...\n\n"
> -        );
> -}
> -
> -static int audio_driver_init (AudioState *s, struct audio_driver *drv)
> -{
> -    if (drv->options) {
> -        audio_process_options (drv->name, drv->options);
> -    }
> -    s->drv_opaque = drv->init ();
> +    s->drv_opaque = drv->init(dev);
>
>      if (s->drv_opaque) {
>          audio_init_nb_voices_out (drv);
> @@ -1743,11 +1245,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
>
>      s->vm_running = running;
>      while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
> -        hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
> +        hwo->pcm_ops->ctl_out (hwo, op);
>      }
>
>      while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
> -        hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
> +        hwi->pcm_ops->ctl_in (hwi, op);
>      }
>      audio_reset_timer (s);
>  }
> @@ -1786,6 +1288,8 @@ static void audio_atexit (void)
>      if (s->drv) {
>          s->drv->fini (s->drv_opaque);
>      }
> +
> +    qapi_free_Audiodev(s->dev);
>  }
>
>  static const VMStateDescription vmstate_audio = {
> @@ -1797,18 +1301,37 @@ static const VMStateDescription vmstate_audio = {
>      }
>  };
>
> -static void audio_init (void)
> +static Audiodev *parse_option(QemuOpts *opts, Error **errp);
> +static int audio_init(Audiodev *dev)
>  {
>      size_t i;
>      int done = 0;
> -    const char *drvname;
> +    const char *drvname = NULL;
>      VMChangeStateEntry *e;
>      AudioState *s = &glob_audio_state;
> +    QemuOptsList *list = NULL; /* silence gcc warning about uninitialized
> +                                * variable */
>
>      if (s->drv) {
> -        return;
> +        if (dev) {
> +            dolog("Cannot create more than one audio backend, sorry\n");
> +            qapi_free_Audiodev(dev);
> +        }
> +        return -1;
>      }
>
> +    if (dev) {
> +        drvname = AudiodevDriver_lookup[dev->kind];
> +    } else {
> +        audio_handle_legacy_opts();
> +        list = qemu_find_opts("audiodev");
> +        dev = parse_option(QTAILQ_FIRST(&list->head), &error_abort);
> +        if (!dev) {
> +            exit(1);
> +        }
> +    }
> +    s->dev = dev;
> +
>      QLIST_INIT (&s->hw_head_out);
>      QLIST_INIT (&s->hw_head_in);
>      QLIST_INIT (&s->cap_head);
> @@ -1819,10 +1342,8 @@ static void audio_init (void)
>          hw_error("Could not create audio timer\n");
>      }
>
> -    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;
> +    s->nb_hw_voices_out = dev->out->voices;
> +    s->nb_hw_voices_in = dev->in->voices;
>
>      if (s->nb_hw_voices_out <= 0) {
>          dolog ("Bogus number of playback voices %d, setting to 1\n",
> @@ -1836,17 +1357,12 @@ static void audio_init (void)
>          s->nb_hw_voices_in = 0;
>      }
>
> -    {
> -        int def;
> -        drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
> -    }
> -
>      if (drvname) {
>          int found = 0;
>
> -        for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
> +        for (i = 0; drvtab[i]; i++) {
>              if (!strcmp (drvname, drvtab[i]->name)) {
> -                done = !audio_driver_init (s, drvtab[i]);
> +                done = !audio_driver_init (s, drvtab[i], dev);
>                  found = 1;
>                  break;
>              }
> @@ -1854,20 +1370,24 @@ static void audio_init (void)
>
>          if (!found) {
>              dolog ("Unknown audio driver `%s'\n", drvname);
> -            dolog ("Run with -audio-help to list available drivers\n");
>          }
> -    }
> -
> -    if (!done) {
> -        for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
> -            if (drvtab[i]->can_be_default) {
> -                done = !audio_driver_init (s, drvtab[i]);
> +    } else {
> +        for (i = 0; !done && drvtab[i]; i++) {
> +            QemuOpts *opts = qemu_opts_find(list, drvtab[i]->name);
> +            if (opts) {
> +                qapi_free_Audiodev(dev);
> +                dev = parse_option(opts, &error_abort);
> +                if (!dev) {
> +                    exit(1);
> +                }
> +                s->dev = dev;
> +                done = !audio_driver_init(s, drvtab[i], dev);
>              }
>          }
>      }
>
>      if (!done) {
> -        done = !audio_driver_init (s, &no_audio_driver);
> +        done = !audio_driver_init (s, &no_audio_driver, dev);
>          if (!done) {
>              hw_error("Could not initialize audio subsystem\n");
>          }
> @@ -1876,16 +1396,16 @@ static void audio_init (void)
>          }
>      }
>
> -    if (conf.period.hertz <= 0) {
> -        if (conf.period.hertz < 0) {
> -            dolog ("warning: Timer period is negative - %d "
> -                   "treating as zero\n",
> -                   conf.period.hertz);
> +    if (dev->timer_period <= 0) {
> +        if (dev->timer_period < 0) {
> +            dolog ("warning: Timer period is negative - %" PRId64
> +                   " treating as zero\n",
> +                   dev->timer_period);
>          }
> -        conf.period.ticks = 1;
> +        s->period_ticks = 1;
>      } else {
> -        conf.period.ticks =
> -            muldiv64 (1, get_ticks_per_sec (), conf.period.hertz);
> +        s->period_ticks =
> +            muldiv64(dev->timer_period, get_ticks_per_sec(), 1000000);
>      }
>
>      e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
> @@ -1896,11 +1416,12 @@ static void audio_init (void)
>
>      QLIST_INIT (&s->card_head);
>      vmstate_register (NULL, 0, &vmstate_audio, s);
> +    return 0;
>  }
>
>  void AUD_register_card (const char *name, QEMUSoundCard *card)
>  {
> -    audio_init ();
> +    audio_init(NULL);
>      card->name = g_strdup (name);
>      memset (&card->entries, 0, sizeof (card->entries));
>      QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
> @@ -2070,3 +1591,156 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
>          }
>      }
>  }
> +
> +QemuOptsList qemu_audiodev_opts = {
> +    .name = "audiodev",
> +    .head = QTAILQ_HEAD_INITIALIZER(qemu_audiodev_opts.head),
> +    .implied_opt_name = "driver",
> +    .desc = {
> +        /*
> +         * no elements => accept any params
> +         * sanity checking will happen later
> +         */
> +        { /* end of list */ }
> +    },
> +};
> +
> +static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo,
> +                                        Error **errp)
> +{
> +    if (!pdo->has_fixed_settings) {
> +        pdo->has_fixed_settings = true;
> +        pdo->fixed_settings = true;
> +    }
> +    if (!pdo->fixed_settings &&
> +        (pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
> +        error_setg(errp,
> +                   "You can't use frequency, channels or format with fixed-settings=off");
> +        return;
> +    }
> +
> +    if (!pdo->has_frequency) {
> +        pdo->has_frequency = true;
> +        pdo->frequency = 44100;
> +    }
> +    if (!pdo->has_channels) {
> +        pdo->has_channels = true;
> +        pdo->channels = 2;
> +    }
> +    if (!pdo->has_voices) {
> +        pdo->has_voices = true;
> +        pdo->voices = 1;
> +    }
> +    if (!pdo->has_format) {
> +        pdo->has_format = true;
> +        pdo->format = AUDIO_FORMAT_S16;
> +    }
> +}
> +
> +static Audiodev *parse_option(QemuOpts *opts, Error **errp)
> +{
> +    Error *local_err = NULL;
> +    OptsVisitor *ov = opts_visitor_new(opts);
> +    Audiodev *dev = NULL;
> +    visit_type_Audiodev(opts_get_visitor(ov), &dev, NULL, &local_err);
> +    opts_visitor_cleanup(ov);
> +
> +    if (local_err) {
> +        goto err2;
> +    }
> +
> +    validate_per_direction_opts(dev->in, &local_err);
> +    if (local_err) {
> +        goto err;
> +    }
> +    validate_per_direction_opts(dev->out, &local_err);
> +    if (local_err) {
> +        goto err;
> +    }
> +
> +    if (!dev->has_timer_period) {
> +        dev->has_timer_period = true;
> +        dev->timer_period = 10000; /* 100Hz -> 10ms */
> +    }
> +
> +    return dev;
> +
> +err:
> +    qapi_free_Audiodev(dev);
> +err2:
> +    error_propagate(errp, local_err);
> +    return NULL;
> +}
> +
> +static int each_option(void *opaque, QemuOpts *opts, Error **errp)
> +{
> +    Audiodev *dev = parse_option(opts, errp);
> +    if (!dev) {
> +        return -1;
> +    }
> +    return audio_init(dev);
> +}
> +
> +void audio_set_options(void)
> +{
> +    if (qemu_opts_foreach(qemu_find_opts("audiodev"), each_option, NULL,
> +                          &error_abort)) {
> +        exit(1);
> +    }
> +}
> +
> +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
> +{
> +    return (audsettings) {
> +        .freq = pdo->frequency,
> +        .nchannels = pdo->channels,
> +        .fmt = pdo->format,
> +        .endianness = AUDIO_HOST_ENDIANNESS,
> +    };
> +}
> +
> +int audioformat_bytes_per_sample(AudioFormat fmt)
> +{
> +    switch (fmt) {
> +    case AUDIO_FORMAT_U8:
> +    case AUDIO_FORMAT_S8:
> +        return 1;
> +
> +    case AUDIO_FORMAT_U16:
> +    case AUDIO_FORMAT_S16:
> +        return 2;
> +
> +    case AUDIO_FORMAT_U32:
> +    case AUDIO_FORMAT_S32:
> +        return 4;
> +
> +    case AUDIO_FORMAT_MAX:
> +        ;
> +    }
> +    abort();
> +}
> +
> +
> +/* frames = freq * usec / 1e6 */
> +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
> +                        audsettings *as, int def_usecs)
> +{
> +    uint64_t usecs = pdo->has_buffer_len ? pdo->buffer_len : def_usecs;
> +    return (as->freq * usecs + 500000) / 1000000;
> +}
> +
> +/* samples = channels * frames = channels * freq * usec / 1e6 */
> +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
> +                         audsettings *as, int def_usecs)
> +{
> +    return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);
> +}
> +
> +/* bytes = bytes_per_sample * samples =
> + *   bytes_per_sample * channels * freq * usec / 1e6 */
> +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
> +                       audsettings *as, int def_usecs)
> +{
> +    return audio_buffer_samples(pdo, as, def_usecs) *
> +        audioformat_bytes_per_sample(as->fmt);
> +}
> diff --git a/audio/audio.h b/audio/audio.h
> index e300511..177a673 100644
> --- a/audio/audio.h
> +++ b/audio/audio.h
> @@ -24,7 +24,10 @@
>  #ifndef QEMU_AUDIO_H
>  #define QEMU_AUDIO_H
>
> +#include <stdarg.h>
>  #include "config-host.h"
> +#include "qapi-types.h"
> +#include "qemu/option.h"
>  #include "qemu/queue.h"
>
>  typedef void (*audio_callback_fn) (void *opaque, int avail);
> @@ -35,12 +38,21 @@ typedef void (*audio_callback_fn) (void *opaque, int avail);
>  #define AUDIO_HOST_ENDIANNESS 0
>  #endif
>
> -struct audsettings {
> +typedef struct audsettings {
>      int freq;
>      int nchannels;
>      AudioFormat fmt;
>      int endianness;
> -};
> +} audsettings;
> +
> +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
> +int audioformat_bytes_per_sample(AudioFormat fmt);
> +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
> +                        audsettings *as, int def_usecs);
> +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
> +                         audsettings *as, int def_usecs);
> +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
> +                       audsettings *as, int def_usecs);
>
>  typedef enum {
>      AUD_CNOTIFY_ENABLE,
> @@ -77,10 +89,11 @@ typedef struct QEMUAudioTimeStamp {
>      uint64_t old_ts;
>  } QEMUAudioTimeStamp;
>
> +extern QemuOptsList qemu_audiodev_opts;
> +
>  void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
>  void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
>
> -void AUD_help (void);
>  void AUD_register_card (const char *name, QEMUSoundCard *card);
>  void AUD_remove_card (QEMUSoundCard *card);
>  CaptureVoiceOut *AUD_add_capture (
> @@ -154,4 +167,8 @@ static inline void *advance (void *p, int incr)
>  int wav_start_capture (CaptureState *s, const char *path, int freq,
>                         int bits, int nchannels);
>
> +void audio_set_options(void);
> +void audio_handle_legacy_opts(void);
> +void audio_legacy_help(void);
> +
>  #endif  /* audio.h */
> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index 566df5e..59b2362 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -32,22 +32,6 @@
>
>  struct audio_pcm_ops;
>
> -typedef enum {
> -    AUD_OPT_INT,
> -    AUD_OPT_FMT,
> -    AUD_OPT_STR,
> -    AUD_OPT_BOOL
> -} audio_option_tag_e;
> -
> -struct audio_option {
> -    const char *name;
> -    audio_option_tag_e tag;
> -    void *valp;
> -    const char *descr;
> -    int *overriddenp;
> -    int overridden;
> -};
> -
>  struct audio_callback {
>      void *opaque;
>      audio_callback_fn fn;
> @@ -143,8 +127,7 @@ struct SWVoiceIn {
>  struct audio_driver {
>      const char *name;
>      const char *descr;
> -    struct audio_option *options;
> -    void *(*init) (void);
> +    void *(*init) (Audiodev *);
>      void (*fini) (void *);
>      struct audio_pcm_ops *pcm_ops;
>      int can_be_default;
> @@ -190,6 +173,7 @@ struct SWVoiceCap {
>
>  struct AudioState {
>      struct audio_driver *drv;
> +    Audiodev *dev;
>      void *drv_opaque;
>
>      QEMUTimer *ts;
> @@ -200,6 +184,7 @@ struct AudioState {
>      int nb_hw_voices_out;
>      int nb_hw_voices_in;
>      int vm_running;
> +    int64_t period_ticks;
>  };
>
>  extern struct audio_driver no_audio_driver;
> @@ -213,6 +198,8 @@ extern struct audio_driver pa_audio_driver;
>  extern struct audio_driver spice_audio_driver;
>  extern const struct mixeng_volume nominal_volume;
>
> +extern struct audio_driver *drvtab[];
> +
>  void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
>  void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
>
> diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c
> new file mode 100644
> index 0000000..88f577d
> --- /dev/null
> +++ b/audio/audio_legacy.c
> @@ -0,0 +1,328 @@
> +#include "audio.h"
> +#include "qemu-common.h"
> +#include "qemu/config-file.h"
> +
> +#define AUDIO_CAP "audio-legacy"
> +#include "audio_int.h"
> +
> +typedef enum EnvTransform {
> +    ENV_TRANSFORM_NONE,
> +    ENV_TRANSFORM_BOOL,
> +    ENV_TRANSFORM_FMT,
> +    ENV_TRANSFORM_FRAMES_TO_USECS_IN,
> +    ENV_TRANSFORM_FRAMES_TO_USECS_OUT,
> +    ENV_TRANSFORM_SAMPLES_TO_USECS_IN,
> +    ENV_TRANSFORM_SAMPLES_TO_USECS_OUT,
> +    ENV_TRANSFORM_BYTES_TO_USECS_IN,
> +    ENV_TRANSFORM_BYTES_TO_USECS_OUT,
> +    ENV_TRANSFORM_MILLIS_TO_USECS,
> +    ENV_TRANSFORM_HZ_TO_USECS,
> +} EnvTransform;
> +
> +typedef struct SimpleEnvMap {
> +    const char *name;
> +    const char *option;
> +    EnvTransform transform;
> +} SimpleEnvMap;
> +
> +SimpleEnvMap global_map[] = {
> +    /* DAC/out settings */
> +    { "QEMU_AUDIO_DAC_FIXED_SETTINGS", "out.fixed-settings",
> +      ENV_TRANSFORM_BOOL },
> +    { "QEMU_AUDIO_DAC_FIXED_FREQ", "out.frequency" },
> +    { "QEMU_AUDIO_DAC_FIXED_FMT", "out.format", ENV_TRANSFORM_FMT },
> +    { "QEMU_AUDIO_DAC_FIXED_CHANNELS", "out.channels" },
> +    { "QEMU_AUDIO_DAC_VOICES", "out.voices" },
> +
> +    /* ADC/in settings */
> +    { "QEMU_AUDIO_ADC_FIXED_SETTINGS", "in.fixed-settings",
> +      ENV_TRANSFORM_BOOL },
> +    { "QEMU_AUDIO_ADC_FIXED_FREQ", "in.frequency" },
> +    { "QEMU_AUDIO_ADC_FIXED_FMT", "in.format", ENV_TRANSFORM_FMT },
> +    { "QEMU_AUDIO_ADC_FIXED_CHANNELS", "in.channels" },
> +    { "QEMU_AUDIO_ADC_VOICES", "in.voices" },
> +
> +    /* general */
> +    { "QEMU_AUDIO_TIMER_PERIOD", "timer-period", ENV_TRANSFORM_HZ_TO_USECS },
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap alsa_map[] = {
> +    { "QEMU_AUDIO_DAC_TRY_POLL", "alsa-out.try-poll", ENV_TRANSFORM_BOOL },
> +    { "QEMU_AUDIO_ADC_TRY_POLL", "alsa-in.try-poll", ENV_TRANSFORM_BOOL },
> +
> +    { "QEMU_ALSA_THRESHOLD", "threshold", ENV_TRANSFORM_MILLIS_TO_USECS },
> +    { "QEMU_ALSA_DAC_DEV", "alsa-out.dev" },
> +    { "QEMU_ALSA_ADC_DEV", "alsa-in.dev" },
> +
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap coreaudio_map[] = {
> +    { "QEMU_COREAUDIO_BUFFER_SIZE", "buffer-len",
> +      ENV_TRANSFORM_FRAMES_TO_USECS_OUT },
> +    { "QEMU_COREAUDIO_BUFFER_COUNT", "buffer-count" },
> +
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap dsound_map[] = {
> +    { "QEMU_DSOUND_LATENCY_MILLIS", "latency", ENV_TRANSFORM_MILLIS_TO_USECS },
> +    { "QEMU_DSOUND_BUFSIZE_OUT", "out.buffer-len",
> +      ENV_TRANSFORM_BYTES_TO_USECS_OUT },
> +    { "QEMU_DSOUND_BUFSIZE_IN", "in.buffer-len",
> +      ENV_TRANSFORM_BYTES_TO_USECS_IN },
> +
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap oss_map[] = {
> +    { "QEMU_AUDIO_DAC_TRY_POLL", "oss-out.try-poll", ENV_TRANSFORM_BOOL },
> +    { "QEMU_AUDIO_ADC_TRY_POLL", "oss-in.try-poll", ENV_TRANSFORM_BOOL },
> +
> +    { "QEMU_OSS_FRAGSIZE", "buffer-len", ENV_TRANSFORM_BYTES_TO_USECS_OUT },
> +    { "QEMU_OSS_NFRAGS", "buffer-count" },
> +    { "QEMU_OSS_MMAP", "try-mmap", ENV_TRANSFORM_BOOL },
> +    { "QEMU_OSS_DAC_DEV", "oss-out.dev" },
> +    { "QEMU_OSS_ADC_DEV", "oss-in.dev" },
> +    { "QEMU_OSS_EXCLUSIVE", "exclusive", ENV_TRANSFORM_BOOL },
> +    { "QEMU_OSS_POLICY", "dsp-policy" },
> +
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap pa_map[] = {
> +    { "QEMU_PA_SAMPLES", "buffer", ENV_TRANSFORM_SAMPLES_TO_USECS_OUT },
> +    { "QEMU_PA_SERVER", "server" },
> +    { "QEMU_PA_SINK", "sink" },
> +    { "QEMU_PA_SOURCE", "source" },
> +
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap sdl_map[] = {
> +    { "QEMU_SDL_SAMPLES", "buffer-len", ENV_TRANSFORM_SAMPLES_TO_USECS_OUT },
> +    { /* End of list */ }
> +};
> +
> +SimpleEnvMap wav_map[] = {
> +    { "QEMU_WAV_FREQUENCY", "out.frequency" },
> +    { "QEMU_WAV_FORMAT", "out.format", ENV_TRANSFORM_FMT },
> +    { "QEMU_WAV_DAC_FIXED_CHANNELS", "out.channels" },
> +    { "QEMU_WAV_PATH", "path" },
> +    { /* End of list */ }
> +};
> +
> +static unsigned long long toull(const char *str)
> +{
> +    unsigned long long ret;
> +    if (parse_uint_full(str, &ret, 10)) {
> +        dolog("Invalid boolean value `%s'\n", str);

integer?

> +        exit(1);
> +    }
> +    return ret;
> +}
> +
> +/* non reentrant typesafe or anything, but enough in this small c file */
> +static const char *tostr(unsigned long long val)
> +{
> +    #define LEN ((CHAR_BIT * sizeof(int) - 1) / 3 + 2)
> +    static char ret[LEN];

sizeof(int) ?

> +    snprintf(ret, LEN, "%llu", val);
> +    return ret;
> +}
> +
> +static uint64_t frames_to_usecs(QemuOpts *opts, uint64_t frames, bool in)
> +{
> +    const char *opt = in ? "in.frequency" : "out.frequency";
> +    uint64_t freq = qemu_opt_get_number(opts, opt, 44100);
> +    return (frames * 1000000 + freq/2) / freq;
> +}
> +
> +static uint64_t samples_to_usecs(QemuOpts *opts, uint64_t samples, bool in)
> +{
> +    const char *opt = in ? "in.channels" : "out.channels";
> +    uint64_t channels = qemu_opt_get_number(opts, opt, 2);
> +    return frames_to_usecs(opts, samples/channels, in);
> +}
> +
> +static uint64_t bytes_to_usecs(QemuOpts *opts, uint64_t bytes, bool in)
> +{
> +    const char *opt = in ? "in.format" : "out.format";
> +    const char *val = qemu_opt_get(opts, opt);
> +    uint64_t bytes_per_sample = (val ? toull(val) : 16) / 8;
> +    return samples_to_usecs(opts, bytes * bytes_per_sample, in);
> +}
> +
> +static const char *transform_val(QemuOpts *opts, const char *val,
> +                                 EnvTransform transform)
> +{
> +    switch (transform) {
> +    case ENV_TRANSFORM_NONE:
> +        return val;
> +
> +    case ENV_TRANSFORM_BOOL:
> +        return toull(val) ? "on" : "off";
> +
> +    case ENV_TRANSFORM_FMT:
> +        if (strcasecmp(val, "u8") == 0) {
> +            return "u8";
> +        } else if (strcasecmp(val, "u16") == 0) {
> +            return "u16";
> +        } else if (strcasecmp(val, "u32") == 0) {
> +            return "u32";
> +        } else if (strcasecmp(val, "s8") == 0) {
> +            return "s8";
> +        } else if (strcasecmp(val, "s16") == 0) {
> +            return "s16";
> +        } else if (strcasecmp(val, "s32") == 0) {
> +            return "s32";
> +        } else {
> +            dolog("Invalid audio format `%s'\n", val);
> +            exit(1);
> +        }
> +
> +    case ENV_TRANSFORM_FRAMES_TO_USECS_IN:
> +        return tostr(frames_to_usecs(opts, toull(val), true));
> +    case ENV_TRANSFORM_FRAMES_TO_USECS_OUT:
> +        return tostr(frames_to_usecs(opts, toull(val), false));
> +
> +    case ENV_TRANSFORM_SAMPLES_TO_USECS_IN:
> +        return tostr(samples_to_usecs(opts, toull(val), true));
> +    case ENV_TRANSFORM_SAMPLES_TO_USECS_OUT:
> +        return tostr(samples_to_usecs(opts, toull(val), false));
> +
> +    case ENV_TRANSFORM_BYTES_TO_USECS_IN:
> +        return tostr(bytes_to_usecs(opts, toull(val), true));
> +    case ENV_TRANSFORM_BYTES_TO_USECS_OUT:
> +        return tostr(bytes_to_usecs(opts, toull(val), false));
> +
> +    case ENV_TRANSFORM_MILLIS_TO_USECS:
> +        return tostr(toull(val) * 1000);
> +
> +    case ENV_TRANSFORM_HZ_TO_USECS:
> +        return tostr(1000000 / toull(val));
> +    }
> +
> +    abort(); /* it's unreachable, gcc */
> +}
> +
> +static void handle_env_opts(QemuOpts *opts, SimpleEnvMap *map)
> +{
> +    while (map->name) {
> +        const char *val = getenv(map->name);
> +
> +        if (val) {
> +            qemu_opt_set(opts, map->option,
> +                         transform_val(opts, val, map->transform),
> +                         &error_abort);
> +        }
> +
> +        ++map;
> +    }
> +}
> +
> +static void handle_alsa_side(QemuOpts *opts, int period, int buffer,
> +                             const char *usec_env, const char *period_env,
> +                             const char *buffer_env, const char *usec_opt,
> +                             const char *count_opt, bool in)
> +{
> +    char *usec_s, *period_s, *buffer_s;
> +    bool usec = false;
> +
> +    usec_s = getenv(usec_env);
> +    if (usec_s) {
> +        usec = toull(usec_s);
> +    }
> +
> +    period_s = getenv(period_env);
> +    if (period_s) {
> +        period = toull(period_s);
> +    }
> +    if (!usec) {
> +        period = frames_to_usecs(opts, period, in);
> +    }
> +    if (period_s) {
> +        qemu_opt_set(opts, usec_opt, tostr(period), &error_abort);
> +    }
> +
> +    buffer_s = getenv(buffer_env);
> +    if (buffer_s) {
> +        buffer = toull(buffer_s);
> +        if (!usec) {
> +            buffer = frames_to_usecs(opts, buffer, in);
> +        }
> +        printf("buffer %d period %d\n", buffer, period);
> +        qemu_opt_set(opts, count_opt, tostr((buffer+period/2)/period),
> +                     &error_abort);
> +    }
> +}
> +
> +static void handle_alsa(QemuOpts *opts)
> +{
> +    handle_alsa_side(opts, 1024, 4096,
> +                     "QEMU_ALSA_DAC_SIZE_IN_USEC", "QEMU_ALSA_DAC_PERIOD_SIZE",
> +                     "QEMU_ALSA_DAC_BUFFER_SIZE",
> +                     "out.buffer-len", "out.buffer-count", false);
> +    handle_alsa_side(opts, 0, 0,
> +                     "QEMU_ALSA_ADC_SIZE_IN_USEC", "QEMU_ALSA_ADC_PERIOD_SIZE",
> +                     "QEMU_ALSA_ADC_BUFFER_SIZE",
> +                     "in.buffer-len", "in.buffer-count", true);
> +}
> +
> +static void legacy_opt(const char *drv)
> +{
> +    QemuOpts *opts;
> +    opts = qemu_opts_create(qemu_find_opts("audiodev"), drv, true,
> +                            &error_abort);
> +    qemu_opt_set(opts, "driver", drv, &error_abort);
> +
> +    handle_env_opts(opts, global_map);
> +
> +    if (strcmp(drv, "alsa") == 0) {
> +        handle_env_opts(opts, alsa_map);
> +        handle_alsa(opts);
> +    } else if (strcmp(drv, "oss") == 0) {
> +        handle_env_opts(opts, oss_map);
> +    } else if (strcmp(drv, "pa") == 0) {
> +        handle_env_opts(opts, pa_map);
> +    } else if (strcmp(drv, "sdl") == 0) {
> +        handle_env_opts(opts, sdl_map);
> +    } else if (strcmp(drv, "wav") == 0) {
> +        handle_env_opts(opts, wav_map);
> +    }
> +}
> +
> +void audio_handle_legacy_opts(void)
> +{
> +    const char *drv = getenv("QEMU_AUDIO_DRV");
> +
> +    if (drv) {
> +        legacy_opt(drv);
> +    } else {
> +        struct audio_driver **drv;
> +        for (drv = drvtab; *drv; ++drv) {
> +            if ((*drv)->can_be_default) {
> +                legacy_opt((*drv)->name);
> +            }
> +        }
> +    }
> +}
> +
> +static int legacy_help_each(void *opaque, QemuOpts *opts, Error **errp)
> +{
> +    printf("-audiodev ");
> +    qemu_opts_print(opts, ",");
> +    printf("\n");
> +    return 0;
> +}
> +
> +void audio_legacy_help(void)
> +{
> +    printf("Environment variable based configuration deprecated.\n");
> +    printf("Please use the new -audiodev option.\n");
> +
> +    audio_handle_legacy_opts();
> +    printf("\nEquivalent -audiodev to your current environment variables:\n");
> +    qemu_opts_foreach(qemu_find_opts("audiodev"), legacy_help_each, NULL, NULL);
> +}
> diff --git a/audio/audio_template.h b/audio/audio_template.h
> index 99b27b2..096b2b3 100644
> --- a/audio/audio_template.h
> +++ b/audio/audio_template.h
> @@ -302,8 +302,10 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
>  static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
>  {
>      HW *hw;
> +    AudioState *s = &glob_audio_state;
> +    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
>
> -    if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
> +    if (pdo->fixed_settings) {
>          hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
>          if (hw) {
>              return hw;
> @@ -331,9 +333,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
>      SW *sw;
>      HW *hw;
>      struct audsettings hw_as;
> +    AudioState *s = &glob_audio_state;
> +    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
>
> -    if (glue (conf.fixed_, TYPE).enabled) {
> -        hw_as = glue (conf.fixed_, TYPE).settings;
> +    if (pdo->fixed_settings) {
> +        hw_as = audiodev_to_audsettings(pdo);
>      }
>      else {
>          hw_as = *as;
> @@ -398,6 +402,7 @@ SW *glue (AUD_open_, TYPE) (
>      )
>  {
>      AudioState *s = &glob_audio_state;
> +    AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
>
>      if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
>          dolog ("card=%p name=%p callback_fn=%p as=%p\n",
> @@ -422,7 +427,7 @@ SW *glue (AUD_open_, TYPE) (
>          return sw;
>      }
>
> -    if (!glue (conf.fixed_, TYPE).enabled && sw) {
> +    if (!pdo->fixed_settings && sw) {
>          glue (AUD_close_, TYPE) (card, sw);
>          sw = NULL;
>      }
> diff --git a/audio/coreaudio.c b/audio/coreaudio.c
> index 6dfd63e..dfa5e79 100644
> --- a/audio/coreaudio.c
> +++ b/audio/coreaudio.c
> @@ -34,11 +34,6 @@
>
>  static int isAtexit;
>
> -typedef struct {
> -    int buffer_frames;
> -    int nbuffers;
> -} CoreaudioConf;
> -
>  typedef struct coreaudioVoiceOut {
>      HWVoiceOut hw;
>      pthread_mutex_t mutex;
> @@ -292,7 +287,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
>      int err;
>      const char *typ = "playback";
>      AudioValueRange frameRange;
> -    CoreaudioConf *conf = drv_opaque;
> +    Audiodev *dev = drv_opaque;
> +    AudiodevPerDirectionOptions *pdo = dev->out;
> +    int frames;
>
>      /* create mutex */
>      err = pthread_mutex_init(&core->mutex, NULL);
> @@ -334,16 +331,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
>          return -1;
>      }
>
> -    if (frameRange.mMinimum > conf->buffer_frames) {
> +    frames = audio_buffer_frames(pdo, as, 11610);
> +    if (frameRange.mMinimum > frames) {
>          core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
>          dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
>      }
> -    else if (frameRange.mMaximum < conf->buffer_frames) {
> +    else if (frameRange.mMaximum < frames) {
>          core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
>          dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
>      }
>      else {
> -        core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
> +        core->audioDevicePropertyBufferFrameSize = frames;
>      }
>
>      /* set Buffer Frame Size */
> @@ -377,7 +375,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
>                             "Could not get device buffer frame size\n");
>          return -1;
>      }
> -    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
> +    hw->samples = (pdo->has_buffer_count ? pdo->buffer_count : 4) *
> +        core->audioDevicePropertyBufferFrameSize;
>
>      /* get StreamFormat */
>      propertySize = sizeof(core->outputStreamBasicDescription);
> @@ -497,41 +496,16 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
>      return 0;
>  }
>
> -static CoreaudioConf glob_conf = {
> -    .buffer_frames = 512,
> -    .nbuffers = 4,
> -};
> -
> -static void *coreaudio_audio_init (void)
> +static void *coreaudio_audio_init(Audiodev *dev)
>  {
> -    CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
> -    *conf = glob_conf;
> -
>      atexit(coreaudio_atexit);
> -    return conf;
> +    return dev;
>  }
>
>  static void coreaudio_audio_fini (void *opaque)
>  {
> -    g_free(opaque);
>  }
>
> -static struct audio_option coreaudio_options[] = {
> -    {
> -        .name  = "BUFFER_SIZE",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.buffer_frames,
> -        .descr = "Size of the buffer in frames"
> -    },
> -    {
> -        .name  = "BUFFER_COUNT",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.nbuffers,
> -        .descr = "Number of buffers"
> -    },
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops coreaudio_pcm_ops = {
>      .init_out = coreaudio_init_out,
>      .fini_out = coreaudio_fini_out,
> @@ -543,7 +517,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
>  struct audio_driver coreaudio_audio_driver = {
>      .name           = "coreaudio",
>      .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
> -    .options        = coreaudio_options,
>      .init           = coreaudio_audio_init,
>      .fini           = coreaudio_audio_fini,
>      .pcm_ops        = &coreaudio_pcm_ops,
> diff --git a/audio/dsound_template.h b/audio/dsound_template.h
> index b439f33..96181ef 100644
> --- a/audio/dsound_template.h
> +++ b/audio/dsound_template.h
> @@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
>      dsound *s = drv_opaque;
>      WAVEFORMATEX wfx;
>      struct audsettings obt_as;
> -    DSoundConf *conf = &s->conf;
>  #ifdef DSBTYPE_IN
>      const char *typ = "ADC";
>      DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
>      DSCBUFFERDESC bd;
>      DSCBCAPS bc;
> +    AudiodevPerDirectionOptions *pdo = s->dev->in;
>  #else
>      const char *typ = "DAC";
>      DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
>      DSBUFFERDESC bd;
>      DSBCAPS bc;
> +    AudiodevPerDirectionOptions *pdo = s->dev->out;
>  #endif
>
>      if (!s->FIELD2) {
> @@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
>      memset (&bd, 0, sizeof (bd));
>      bd.dwSize = sizeof (bd);
>      bd.lpwfxFormat = &wfx;
> +    bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
>  #ifdef DSBTYPE_IN
> -    bd.dwBufferBytes = conf->bufsize_in;
>      hr = IDirectSoundCapture_CreateCaptureBuffer (
>          s->dsound_capture,
>          &bd,
> @@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
>          );
>  #else
>      bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
> -    bd.dwBufferBytes = conf->bufsize_out;
>      hr = IDirectSound_CreateSoundBuffer (
>          s->dsound,
>          &bd,
> diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
> index e9472c1..7e7b3f2 100644
> --- a/audio/dsoundaudio.c
> +++ b/audio/dsoundaudio.c
> @@ -42,16 +42,10 @@
>  /* #define DEBUG_DSOUND */
>
>  typedef struct {
> -    int bufsize_in;
> -    int bufsize_out;
> -    int latency_millis;
> -} DSoundConf;
> -
> -typedef struct {
>      LPDIRECTSOUND dsound;
>      LPDIRECTSOUNDCAPTURE dsound_capture;
>      struct audsettings settings;
> -    DSoundConf conf;
> +    Audiodev *dev;
>  } dsound;
>
>  typedef struct {
> @@ -247,9 +241,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
>      dsound_log_hresult (hr);
>  }
>
> -static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
> +static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
>  {
> -    return (millis * info->bytes_per_second) / 1000;
> +    return muldiv64(usecs, info->bytes_per_second, 1000000);
>  }
>
>  #ifdef DEBUG_DSOUND
> @@ -477,7 +471,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
>      LPVOID p1, p2;
>      int bufsize;
>      dsound *s = ds->s;
> -    DSoundConf *conf = &s->conf;
> +    AudiodevDsoundOptions *dso = s->dev->dsound;
>
>      if (!dsb) {
>          dolog ("Attempt to run empty with playback buffer\n");
> @@ -500,14 +494,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
>      len = live << hwshift;
>
>      if (ds->first_time) {
> -        if (conf->latency_millis) {
> +        if (dso->latency) {
>              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;
> +                usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
>              old_pos %= bufsize;
>              old_pos &= ~hw->info.align;
>          }
> @@ -746,12 +740,6 @@ static int dsound_run_in (HWVoiceIn *hw)
>      return decr;
>  }
>
> -static DSoundConf glob_conf = {
> -    .bufsize_in         = 16384,
> -    .bufsize_out        = 16384,
> -    .latency_millis     = 10
> -};
> -
>  static void dsound_audio_fini (void *opaque)
>  {
>      HRESULT hr;
> @@ -782,13 +770,22 @@ static void dsound_audio_fini (void *opaque)
>      g_free(s);
>  }
>
> -static void *dsound_audio_init (void)
> +static void *dsound_audio_init(Audiodev *dev)
>  {
>      int err;
>      HRESULT hr;
>      dsound *s = g_malloc0(sizeof(dsound));
> +    AudiodevDsoundOptions *dso;
> +
> +    assert(dev->kind == AUDIODEV_DRIVER_DSOUND);
> +    s->dev = dev;
> +    dso = dev->dsound;
> +
> +    if (!dso->has_latency) {
> +        dso->has_latency = true;
> +        dso->latency = 10000; /* 10 ms */
> +    }
>
> -    s->conf = glob_conf;
>      hr = CoInitialize (NULL);
>      if (FAILED (hr)) {
>          dsound_logerr (hr, "Could not initialize COM\n");
> @@ -853,28 +850,6 @@ static void *dsound_audio_init (void)
>      return s;
>  }
>
> -static struct audio_option dsound_options[] = {
> -    {
> -        .name  = "LATENCY_MILLIS",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.latency_millis,
> -        .descr = "(undocumented)"
> -    },
> -    {
> -        .name  = "BUFSIZE_OUT",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.bufsize_out,
> -        .descr = "(undocumented)"
> -    },
> -    {
> -        .name  = "BUFSIZE_IN",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.bufsize_in,
> -        .descr = "(undocumented)"
> -    },
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops dsound_pcm_ops = {
>      .init_out = dsound_init_out,
>      .fini_out = dsound_fini_out,
> @@ -892,7 +867,6 @@ static struct audio_pcm_ops dsound_pcm_ops = {
>  struct audio_driver dsound_audio_driver = {
>      .name           = "dsound",
>      .descr          = "DirectSound http://wikipedia.org/wiki/DirectSound",
> -    .options        = dsound_options,
>      .init           = dsound_audio_init,
>      .fini           = dsound_audio_fini,
>      .pcm_ops        = &dsound_pcm_ops,
> diff --git a/audio/noaudio.c b/audio/noaudio.c
> index 50db1f3..4c94a26 100644
> --- a/audio/noaudio.c
> +++ b/audio/noaudio.c
> @@ -134,7 +134,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
>      return 0;
>  }
>
> -static void *no_audio_init (void)
> +static void *no_audio_init (Audiodev *dev)
>  {
>      return &no_audio_init;
>  }
> @@ -161,7 +161,6 @@ static struct audio_pcm_ops no_pcm_ops = {
>  struct audio_driver no_audio_driver = {
>      .name           = "none",
>      .descr          = "Timer based audio emulation",
> -    .options        = NULL,
>      .init           = no_audio_init,
>      .fini           = no_audio_fini,
>      .pcm_ops        = &no_pcm_ops,
> diff --git a/audio/ossaudio.c b/audio/ossaudio.c
> index 3ea7d27..a5e7f7c 100644
> --- a/audio/ossaudio.c
> +++ b/audio/ossaudio.c
> @@ -29,6 +29,7 @@
>  #include "qemu-common.h"
>  #include "qemu/main-loop.h"
>  #include "qemu/host-utils.h"
> +#include "qapi-visit.h"
>  #include "audio.h"
>  #include "trace.h"
>
> @@ -39,16 +40,6 @@
>  #define USE_DSP_POLICY
>  #endif
>
> -typedef struct OSSConf {
> -    int try_mmap;
> -    int nfrags;
> -    int fragsize;
> -    const char *devpath_out;
> -    const char *devpath_in;
> -    int exclusive;
> -    int policy;
> -} OSSConf;
> -
>  typedef struct OSSVoiceOut {
>      HWVoiceOut hw;
>      void *pcm_buf;
> @@ -58,7 +49,7 @@ typedef struct OSSVoiceOut {
>      int fragsize;
>      int mmapped;
>      int pending;
> -    OSSConf *conf;
> +    Audiodev *dev;
>  } OSSVoiceOut;
>
>  typedef struct OSSVoiceIn {
> @@ -67,12 +58,12 @@ typedef struct OSSVoiceIn {
>      int fd;
>      int nfrags;
>      int fragsize;
> -    OSSConf *conf;
> +    Audiodev *dev;
>  } OSSVoiceIn;
>
>  struct oss_params {
>      int freq;
> -    AudioFormat fmt;
> +    int fmt;
>      int nchannels;
>      int nfrags;
>      int fragsize;
> @@ -264,19 +255,26 @@ static int oss_get_version (int fd, int *version, const char *typ)
>  }
>  #endif
>
> -static int oss_open (int in, struct oss_params *req,
> -                     struct oss_params *obt, int *pfd, OSSConf* conf)
> +static int oss_open(int in, struct oss_params *req, audsettings *as,
> +                    struct oss_params *obt, int *pfd, Audiodev *dev)
>  {
> +    AudiodevOssOptions *oopts = dev->oss;
> +    AudiodevOssPerDirectionOptions *opdo = in ? oopts->oss_in : oopts->oss_out;
> +    AudiodevPerDirectionOptions *pdo = in ? dev->in : dev->out;
>      int fd;
> -    int oflags = conf->exclusive ? O_EXCL : 0;
> +    int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
>      audio_buf_info abinfo;
>      int fmt, freq, nchannels;
>      int setfragment = 1;
> -    const char *dspname = in ? conf->devpath_in : conf->devpath_out;
> +    const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp";
>      const char *typ = in ? "ADC" : "DAC";
> +#ifdef USE_DSP_POLICY
> +    int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5;
> +#endif
>
>      /* Kludge needed to have working mmap on Linux */
> -    oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
> +    oflags |= (oopts->has_try_mmap && oopts->try_mmap) ?
> +        O_RDWR : (in ? O_RDONLY : O_WRONLY);
>
>      fd = open (dspname, oflags | O_NONBLOCK);
>      if (-1 == fd) {
> @@ -287,6 +285,8 @@ static int oss_open (int in, struct oss_params *req,
>      freq = req->freq;
>      nchannels = req->nchannels;
>      fmt = req->fmt;
> +    req->nfrags = pdo->has_buffer_count ? pdo->buffer_count : 4;
> +    req->fragsize = audio_buffer_bytes(pdo, as, 23220);
>
>      if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
>          oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
> @@ -310,18 +310,18 @@ static int oss_open (int in, struct oss_params *req,
>      }
>
>  #ifdef USE_DSP_POLICY
> -    if (conf->policy >= 0) {
> +    if (policy >= 0) {
>          int version;
>
>          if (!oss_get_version (fd, &version, typ)) {
>              trace_oss_version(version);
>
>              if (version >= 0x040000) {
> -                int policy = conf->policy;
> -                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
> +                int policy2 = policy;
> +                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy2)) {
>                      oss_logerr2 (errno, typ,
>                                   "Failed to set timing policy to %d\n",
> -                                 conf->policy);
> +                                 policy);
>                      goto err;
>                  }
>                  setfragment = 0;
> @@ -504,17 +504,16 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
>      int fd;
>      AudioFormat effective_fmt;
>      struct audsettings obt_as;
> -    OSSConf *conf = drv_opaque;
> +    Audiodev *dev = drv_opaque;
> +    AudiodevOssOptions *oopts = dev->oss;
>
>      oss->fd = -1;
>
>      req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
>      req.freq = as->freq;
>      req.nchannels = as->nchannels;
> -    req.fragsize = conf->fragsize;
> -    req.nfrags = conf->nfrags;
>
> -    if (oss_open (0, &req, &obt, &fd, conf)) {
> +    if (oss_open(0, &req, as, &obt, &fd, dev)) {
>          return -1;
>      }
>
> @@ -541,7 +540,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
>      hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
>
>      oss->mmapped = 0;
> -    if (conf->try_mmap) {
> +    if (oopts->has_try_mmap && oopts->try_mmap) {
>          oss->pcm_buf = mmap (
>              NULL,
>              hw->samples << hw->info.shift,
> @@ -601,7 +600,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
>      }
>
>      oss->fd = fd;
> -    oss->conf = conf;
> +    oss->dev = dev;
>      return 0;
>  }
>
> @@ -609,16 +608,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
>  {
>      int trig;
>      OSSVoiceOut *oss = (OSSVoiceOut *) hw;
> +    AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
>
>      switch (cmd) {
>      case VOICE_ENABLE:
>          {
> -            va_list ap;
> -            int poll_mode;
> -
> -            va_start (ap, cmd);
> -            poll_mode = va_arg (ap, int);
> -            va_end (ap);
> +            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
>
>              ldebug ("enabling voice\n");
>              if (poll_mode) {
> @@ -673,16 +668,14 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      int fd;
>      AudioFormat effective_fmt;
>      struct audsettings obt_as;
> -    OSSConf *conf = drv_opaque;
> +    Audiodev *dev = drv_opaque;
>
>      oss->fd = -1;
>
>      req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
>      req.freq = as->freq;
>      req.nchannels = as->nchannels;
> -    req.fragsize = conf->fragsize;
> -    req.nfrags = conf->nfrags;
> -    if (oss_open (1, &req, &obt, &fd, conf)) {
> +    if (oss_open(1, &req, as, &obt, &fd, dev)) {
>          return -1;
>      }
>
> @@ -716,7 +709,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      }
>
>      oss->fd = fd;
> -    oss->conf = conf;
> +    oss->dev = dev;
>      return 0;
>  }
>
> @@ -807,16 +800,12 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
>  static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
>  {
>      OSSVoiceIn *oss = (OSSVoiceIn *) hw;
> +    AudiodevOssPerDirectionOptions *opdo = oss->dev->oss->oss_out;
>
>      switch (cmd) {
>      case VOICE_ENABLE:
>          {
> -            va_list ap;
> -            int poll_mode;
> -
> -            va_start (ap, cmd);
> -            poll_mode = va_arg (ap, int);
> -            va_end (ap);
> +            bool poll_mode = !opdo->has_try_poll || opdo->try_poll;
>
>              if (poll_mode) {
>                  oss_poll_in (hw);
> @@ -836,82 +825,25 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
>      return 0;
>  }
>
> -static OSSConf glob_conf = {
> -    .try_mmap = 0,
> -    .nfrags = 4,
> -    .fragsize = 4096,
> -    .devpath_out = "/dev/dsp",
> -    .devpath_in = "/dev/dsp",
> -    .exclusive = 0,
> -    .policy = 5
> -};
> -
> -static void *oss_audio_init (void)
> +static void *oss_audio_init(Audiodev *dev)
>  {
> -    OSSConf *conf = g_malloc(sizeof(OSSConf));
> -    *conf = glob_conf;
> +    AudiodevOssOptions *oopts;
> +    assert(dev->kind == AUDIODEV_DRIVER_OSS);
>
> -    if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
> -        access(conf->devpath_out, R_OK | W_OK) < 0) {
> -        g_free(conf);
> +    oopts = dev->oss;
> +    if (access(oopts->oss_in->has_dev ? oopts->oss_in->dev : "/dev/dsp",
> +               R_OK | W_OK) < 0 ||
> +        access(oopts->oss_out->has_dev ? oopts->oss_out->dev : "/dev/dsp",
> +               R_OK | W_OK) < 0) {
>          return NULL;
>      }
> -    return conf;
> +    return dev;
>  }
>
>  static void oss_audio_fini (void *opaque)
>  {
> -    g_free(opaque);
>  }
>
> -static struct audio_option oss_options[] = {
> -    {
> -        .name  = "FRAGSIZE",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.fragsize,
> -        .descr = "Fragment size in bytes"
> -    },
> -    {
> -        .name  = "NFRAGS",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.nfrags,
> -        .descr = "Number of fragments"
> -    },
> -    {
> -        .name  = "MMAP",
> -        .tag   = AUD_OPT_BOOL,
> -        .valp  = &glob_conf.try_mmap,
> -        .descr = "Try using memory mapped access"
> -    },
> -    {
> -        .name  = "DAC_DEV",
> -        .tag   = AUD_OPT_STR,
> -        .valp  = &glob_conf.devpath_out,
> -        .descr = "Path to DAC device"
> -    },
> -    {
> -        .name  = "ADC_DEV",
> -        .tag   = AUD_OPT_STR,
> -        .valp  = &glob_conf.devpath_in,
> -        .descr = "Path to ADC device"
> -    },
> -    {
> -        .name  = "EXCLUSIVE",
> -        .tag   = AUD_OPT_BOOL,
> -        .valp  = &glob_conf.exclusive,
> -        .descr = "Open device in exclusive mode (vmix wont work)"
> -    },
> -#ifdef USE_DSP_POLICY
> -    {
> -        .name  = "POLICY",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.policy,
> -        .descr = "Set the timing policy of the device, -1 to use fragment mode",
> -    },
> -#endif
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops oss_pcm_ops = {
>      .init_out = oss_init_out,
>      .fini_out = oss_fini_out,
> @@ -929,7 +861,6 @@ static struct audio_pcm_ops oss_pcm_ops = {
>  struct audio_driver oss_audio_driver = {
>      .name           = "oss",
>      .descr          = "OSS http://www.opensound.com",
> -    .options        = oss_options,
>      .init           = oss_audio_init,
>      .fini           = oss_audio_fini,
>      .pcm_ops        = &oss_pcm_ops,
> diff --git a/audio/paaudio.c b/audio/paaudio.c
> index cfdbdc6..a53aaf6 100644
> --- a/audio/paaudio.c
> +++ b/audio/paaudio.c
> @@ -1,6 +1,7 @@
>  /* public domain */
>  #include "qemu-common.h"
>  #include "audio.h"
> +#include "qapi-visit.h"
>
>  #include <pulse/pulseaudio.h>
>
> @@ -9,14 +10,7 @@
>  #include "audio_pt_int.h"
>
>  typedef struct {
> -    int samples;
> -    char *server;
> -    char *sink;
> -    char *source;
> -} PAConf;
> -
> -typedef struct {
> -    PAConf conf;
> +    Audiodev *dev;
>      pa_threaded_mainloop *mainloop;
>      pa_context *context;
>  } paaudio;
> @@ -31,6 +25,7 @@ typedef struct {
>      void *pcm_buf;
>      struct audio_pt pt;
>      paaudio *g;
> +    int samples;
>  } PAVoiceOut;
>
>  typedef struct {
> @@ -45,6 +40,7 @@ typedef struct {
>      const void *read_data;
>      size_t read_index, read_length;
>      paaudio *g;
> +    int samples;
>  } PAVoiceIn;
>
>  static void qpa_audio_fini(void *opaque);
> @@ -226,7 +222,7 @@ static void *qpa_thread_out (void *arg)
>              }
>          }
>
> -        decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
> +        decr = to_mix = audio_MIN (pa->live, pa->samples >> 2);
>          rpos = pa->rpos;
>
>          if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
> @@ -318,7 +314,7 @@ static void *qpa_thread_in (void *arg)
>              }
>          }
>
> -        incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
> +        incr = to_grab = audio_MIN (pa->dead, pa->samples >> 2);
>          wpos = pa->wpos;
>
>          if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
> @@ -545,6 +541,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
>      struct audsettings obt_as = *as;
>      PAVoiceOut *pa = (PAVoiceOut *) hw;
>      paaudio *g = pa->g = drv_opaque;
> +    AudiodevPaOptions *popts = g->dev->pa;
> +    AudiodevPaPerDirectionOptions *ppdo = popts->sink;
>
>      ss.format = audfmt_to_pa (as->fmt, as->endianness);
>      ss.channels = as->nchannels;
> @@ -565,7 +563,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
>          g,
>          "qemu",
>          PA_STREAM_PLAYBACK,
> -        g->conf.sink,
> +        ppdo->has_name ? ppdo->name : NULL,
>          &ss,
>          NULL,                   /* channel map */
>          &ba,                    /* buffering attributes */
> @@ -577,7 +575,8 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
>      }
>
>      audio_pcm_init_info (&hw->info, &obt_as);
> -    hw->samples = g->conf.samples;
> +    hw->samples = pa->samples = audio_buffer_samples(g->dev->out, &obt_as,
> +                                                     46440);
>      pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
>      pa->rpos = hw->rpos;
>      if (!pa->pcm_buf) {
> @@ -611,6 +610,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      struct audsettings obt_as = *as;
>      PAVoiceIn *pa = (PAVoiceIn *) hw;
>      paaudio *g = pa->g = drv_opaque;
> +    AudiodevPaOptions *popts = g->dev->pa;
> +    AudiodevPaPerDirectionOptions *ppdo = popts->source;
>
>      ss.format = audfmt_to_pa (as->fmt, as->endianness);
>      ss.channels = as->nchannels;
> @@ -622,7 +623,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>          g,
>          "qemu",
>          PA_STREAM_RECORD,
> -        g->conf.source,
> +        ppdo->has_name ? ppdo->name : NULL,
>          &ss,
>          NULL,                   /* channel map */
>          NULL,                   /* buffering attributes */
> @@ -634,7 +635,8 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      }
>
>      audio_pcm_init_info (&hw->info, &obt_as);
> -    hw->samples = g->conf.samples;
> +    hw->samples = pa->samples = audio_buffer_samples(g->dev->in, &obt_as,
> +                                                     46440);
>      pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
>      pa->wpos = hw->wpos;
>      if (!pa->pcm_buf) {
> @@ -808,14 +810,19 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
>  }
>
>  /* common */
> -static PAConf glob_conf = {
> -    .samples = 4096,
> -};
> -
> -static void *qpa_audio_init (void)
> +static void *qpa_audio_init(Audiodev *dev)
>  {
> -    paaudio *g = g_malloc(sizeof(paaudio));
> -    g->conf = glob_conf;
> +    paaudio *g;
> +    AudiodevPaOptions *popts;
> +    const char *server;
> +
> +    assert(dev->kind == AUDIODEV_DRIVER_PA);
> +
> +    g = g_malloc(sizeof(paaudio));
> +    popts = dev->pa;
> +    server = popts->has_server ? popts->server : NULL;
> +
> +    g->dev = dev;
>      g->mainloop = NULL;
>      g->context = NULL;
>
> @@ -825,14 +832,14 @@ static void *qpa_audio_init (void)
>      }
>
>      g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
> -                                 g->conf.server);
> +                                 server);
>      if (!g->context) {
>          goto fail;
>      }
>
>      pa_context_set_state_callback (g->context, context_state_cb, g);
>
> -    if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) {
> +    if (pa_context_connect (g->context, server, 0, NULL) < 0) {
>          qpa_logerr (pa_context_errno (g->context),
>                      "pa_context_connect() failed\n");
>          goto fail;
> @@ -895,34 +902,6 @@ static void qpa_audio_fini (void *opaque)
>      g_free(g);
>  }
>
> -struct audio_option qpa_options[] = {
> -    {
> -        .name  = "SAMPLES",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.samples,
> -        .descr = "buffer size in samples"
> -    },
> -    {
> -        .name  = "SERVER",
> -        .tag   = AUD_OPT_STR,
> -        .valp  = &glob_conf.server,
> -        .descr = "server address"
> -    },
> -    {
> -        .name  = "SINK",
> -        .tag   = AUD_OPT_STR,
> -        .valp  = &glob_conf.sink,
> -        .descr = "sink device name"
> -    },
> -    {
> -        .name  = "SOURCE",
> -        .tag   = AUD_OPT_STR,
> -        .valp  = &glob_conf.source,
> -        .descr = "source device name"
> -    },
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops qpa_pcm_ops = {
>      .init_out = qpa_init_out,
>      .fini_out = qpa_fini_out,
> @@ -940,7 +919,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
>  struct audio_driver pa_audio_driver = {
>      .name           = "pa",
>      .descr          = "http://www.pulseaudio.org/",
> -    .options        = qpa_options,
>      .init           = qpa_audio_init,
>      .fini           = qpa_audio_fini,
>      .pcm_ops        = &qpa_pcm_ops,
> diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
> index db0f95a..796238a 100644
> --- a/audio/sdlaudio.c
> +++ b/audio/sdlaudio.c
> @@ -44,18 +44,13 @@ typedef struct SDLVoiceOut {
>      int decr;
>  } SDLVoiceOut;
>
> -static struct {
> -    int nb_samples;
> -} conf = {
> -    .nb_samples = 1024
> -};
> -
>  static struct SDLAudioState {
>      int exit;
>      SDL_mutex *mutex;
>      SDL_sem *sem;
>      int initialized;
>      bool driver_created;
> +    Audiodev *dev;
>  } glob_sdl;
>  typedef struct SDLAudioState SDLAudioState;
>
> @@ -347,7 +342,7 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
>      req.freq = as->freq;
>      req.format = aud_to_sdlfmt (as->fmt);
>      req.channels = as->nchannels;
> -    req.samples = conf.nb_samples;
> +    req.samples = audio_buffer_samples(s->dev->out, as, 11610);
>      req.callback = sdl_callback;
>      req.userdata = sdl;
>
> @@ -391,7 +386,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
>      return 0;
>  }
>
> -static void *sdl_audio_init (void)
> +static void *sdl_audio_init(Audiodev *dev)
>  {
>      SDLAudioState *s = &glob_sdl;
>      if (s->driver_created) {
> @@ -420,6 +415,7 @@ static void *sdl_audio_init (void)
>      }
>
>      s->driver_created = true;
> +    s->dev = dev;
>      return s;
>  }
>
> @@ -431,18 +427,9 @@ static void sdl_audio_fini (void *opaque)
>      SDL_DestroyMutex (s->mutex);
>      SDL_QuitSubSystem (SDL_INIT_AUDIO);
>      s->driver_created = false;
> +    s->dev = NULL;
>  }
>
> -static struct audio_option sdl_options[] = {
> -    {
> -        .name  = "SAMPLES",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &conf.nb_samples,
> -        .descr = "Size of SDL buffer in samples"
> -    },
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops sdl_pcm_ops = {
>      .init_out = sdl_init_out,
>      .fini_out = sdl_fini_out,
> @@ -454,7 +441,6 @@ static struct audio_pcm_ops sdl_pcm_ops = {
>  struct audio_driver sdl_audio_driver = {
>      .name           = "sdl",
>      .descr          = "SDL http://www.libsdl.org",
> -    .options        = sdl_options,
>      .init           = sdl_audio_init,
>      .fini           = sdl_audio_fini,
>      .pcm_ops        = &sdl_pcm_ops,
> diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
> index 141fd8d..a9b9a1d 100644
> --- a/audio/spiceaudio.c
> +++ b/audio/spiceaudio.c
> @@ -75,7 +75,7 @@ static const SpiceRecordInterface record_sif = {
>      .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
>  };
>
> -static void *spice_audio_init (void)
> +static void *spice_audio_init(Audiodev *dev)
>  {
>      if (!using_spice) {
>          return NULL;
> @@ -371,10 +371,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
>      return 0;
>  }
>
> -static struct audio_option audio_options[] = {
> -    { /* end of list */ },
> -};
> -
>  static struct audio_pcm_ops audio_callbacks = {
>      .init_out = line_out_init,
>      .fini_out = line_out_fini,
> @@ -392,7 +388,6 @@ static struct audio_pcm_ops audio_callbacks = {
>  struct audio_driver spice_audio_driver = {
>      .name           = "spice",
>      .descr          = "spice audio driver",
> -    .options        = audio_options,
>      .init           = spice_audio_init,
>      .fini           = spice_audio_fini,
>      .pcm_ops        = &audio_callbacks,
> diff --git a/audio/wavaudio.c b/audio/wavaudio.c
> index 81250e6..1af6d23 100644
> --- a/audio/wavaudio.c
> +++ b/audio/wavaudio.c
> @@ -23,6 +23,7 @@
>   */
>  #include "hw/hw.h"
>  #include "qemu/timer.h"
> +#include "qapi-visit.h"
>  #include "audio.h"
>
>  #define AUDIO_CAP "wav"
> @@ -36,11 +37,6 @@ typedef struct WAVVoiceOut {
>      int total_samples;
>  } WAVVoiceOut;
>
> -typedef struct {
> -    struct audsettings settings;
> -    const char *wav_path;
> -} WAVConf;
> -
>  static int wav_run_out (HWVoiceOut *hw, int live)
>  {
>      WAVVoiceOut *wav = (WAVVoiceOut *) hw;
> @@ -111,8 +107,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
>          0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
>          0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
>      };
> -    WAVConf *conf = drv_opaque;
> -    struct audsettings wav_as = conf->settings;
> +    Audiodev *dev = drv_opaque;
> +    AudiodevWavOptions *wopts = dev->wav;
> +    struct audsettings wav_as = audiodev_to_audsettings(dev->out);
> +    const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav";
>
>      stereo = wav_as.nchannels == 2;
>      switch (wav_as.fmt) {
> @@ -153,10 +151,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
>      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");
> +    wav->f = fopen(wav_path, "wb");
>      if (!wav->f) {
>          dolog ("Failed to open wave file `%s'\nReason: %s\n",
> -               conf->wav_path, strerror (errno));
> +               wav_path, strerror(errno));
>          g_free (wav->pcm_buf);
>          wav->pcm_buf = NULL;
>          return -1;
> @@ -224,54 +222,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
>      return 0;
>  }
>
> -static WAVConf glob_conf = {
> -    .settings.freq      = 44100,
> -    .settings.nchannels = 2,
> -    .settings.fmt       = AUDIO_FORMAT_S16,
> -    .wav_path           = "qemu.wav"
> -};
> -
> -static void *wav_audio_init (void)
> +static void *wav_audio_init(Audiodev *dev)
>  {
> -    WAVConf *conf = g_malloc(sizeof(WAVConf));
> -    *conf = glob_conf;
> -    return conf;
> +    assert(dev->kind == AUDIODEV_DRIVER_WAV);
> +    return dev;
>  }
>
>  static void wav_audio_fini (void *opaque)
>  {
>      ldebug ("wav_fini");
> -    g_free(opaque);
>  }
>
> -static struct audio_option wav_options[] = {
> -    {
> -        .name  = "FREQUENCY",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.settings.freq,
> -        .descr = "Frequency"
> -    },
> -    {
> -        .name  = "FORMAT",
> -        .tag   = AUD_OPT_FMT,
> -        .valp  = &glob_conf.settings.fmt,
> -        .descr = "Format"
> -    },
> -    {
> -        .name  = "DAC_FIXED_CHANNELS",
> -        .tag   = AUD_OPT_INT,
> -        .valp  = &glob_conf.settings.nchannels,
> -        .descr = "Number of channels (1 - mono, 2 - stereo)"
> -    },
> -    {
> -        .name  = "PATH",
> -        .tag   = AUD_OPT_STR,
> -        .valp  = &glob_conf.wav_path,
> -        .descr = "Path to wave file"
> -    },
> -    { /* End of list */ }
> -};
> -
>  static struct audio_pcm_ops wav_pcm_ops = {
>      .init_out = wav_init_out,
>      .fini_out = wav_fini_out,
> @@ -283,7 +244,6 @@ static struct audio_pcm_ops wav_pcm_ops = {
>  struct audio_driver wav_audio_driver = {
>      .name           = "wav",
>      .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
> -    .options        = wav_options,
>      .init           = wav_audio_init,
>      .fini           = wav_audio_fini,
>      .pcm_ops        = &wav_pcm_ops,
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 77f5853..efd57e6 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -185,8 +185,8 @@ Set default value of @var{driver}'s property @var{prop} to @var{value}, e.g.:
>  qemu-system-i386 -global ide-drive.physical_block_size=4096 -drive file=file,if=ide,index=0,media=disk
>  @end example
>
> -In particular, you can use this to set driver properties for devices which are
> -created automatically by the machine model. To create a device which is not
> +In particular, you can use this to set driver properties for devices which are
> +created automatically by the machine model. To create a device which is not
>  created automatically and set properties on it, use -@option{device}.
>
>  -global @var{driver}.@var{prop}=@var{value} is shorthand for -global
> @@ -313,14 +313,230 @@ The default is @code{en-us}.
>  ETEXI
>
>
> +HXCOMM Deprecated by -audiodev
>  DEF("audio-help", 0, QEMU_OPTION_audio_help,
> -    "-audio-help     print list of audio drivers and their options\n",
> +    "-audio-help     show -audiodev equivalent of the current audio settings\n",

by current, you mean "old" or "deprecated" perhaps?

>      QEMU_ARCH_ALL)
>  STEXI
>  @item -audio-help
>  @findex -audio-help
> -Will show the audio subsystem help: list of drivers, tunable
> -parameters.
> +Will show the -audiodev equivalent of the currently specified
> +(deprecated) environment variables.
> +ETEXI
> +
> +DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
> +    "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n"
> +    "                specifies the audio backend to use\n"
> +    "                id= identifier of the backend\n"
> +    "                timer-period= timer period in microseconds\n"
> +    "                in|out.fixed-settings= use fixed settings for host audio\n"
> +    "                in|out.frequency= frequency to use with fixed settings\n"
> +    "                in|out.channels= number of channels to use with fixed settings\n"
> +    "                in|out.format= sample format to use with fixed settings\n"
> +    "                valid values: s8, s16, s32, u8, u16, u32\n"
> +    "                in|out.voices= number of voices to use\n"
> +    "                in|out.buffer-len= size of buffer in microseconds\n"
> +    "                in|out.buffer-count= number of buffers\n"
> +    "-audiodev none,id=id,[,prop[=value][,...]]\n"
> +    "                dummy driver that discards all output\n"
> +#ifdef CONFIG_ALSA
> +    "-audiodev alsa,id=id[,prop[=value][,...]]\n"
> +    "                alsa-in|alsa-out.dev= name of the audio device to use\n"
> +    "                alsa-in|alsa-out.try-poll= attempt to use poll mode\n"
> +    "                threshold= threshold (in microseconds) when playback starts\n"
> +#endif
> +#ifdef CONFIG_COREAUDIO
> +    "-audiodev coreaudio,id=id[,prop[=value][,...]]\n"
> +#endif
> +#ifdef CONFIG_DSOUND
> +    "-audiodev dsound,id=id[,prop[=value][,...]]\n"
> +    "                latency= add extra latency to playback in microseconds\n"
> +#endif
> +#ifdef CONFIG_OSS
> +    "-audiodev oss,id=id[,prop[=value][,...]]\n"
> +    "                oss-in|oss-out.dev= path of the audio device to use\n"
> +    "                oss-in|oss-out.try-poll= attempt to use poll mode\n"
> +    "                try-mmap= try using memory mapped access\n"
> +    "                exclusive= open device in exclusive mode\n"
> +    "                dsp-policy= set timing policy (0..10), -1 to use fragment mode\n"
> +#endif
> +#ifdef CONFIG_PA
> +    "-audiodev pa,id=id[,prop[=value][,...]]\n"
> +    "                server= PulseAudio server address\n"
> +    "                sink|source.name= sink/source device name\n"
> +#endif
> +#ifdef CONFIG_SDL
> +    "-audiodev sdl,id=id[,prop[=value][,...]]\n"
> +#endif
> +#ifdef CONFIG_SPICE
> +    "-audiodev spice,id=id[,prop[=value][,...]]\n"
> +#endif
> +    "-audiodev wav,id=id[,prop[=value][,...]]\n"
> +    "                path= path of wav file to record\n",
> +    QEMU_ARCH_ALL)
> +STEXI
> +@item -audiodev [driver=]@var{driver},id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +@findex -audiodev
> +Adds a new audio backend @var{driver} identified by @var{id}.  There are
> +global and driver specific properties.  Some values can be set
> +differently for input and output, they're marked with @code{in|out.}.
> +You can set the input's property with @code{in.@var{prop}} and the
> +output's property with @code{out.@var{prop}}. For example:
> +@example
> +-audiodev alsa,in.frequency=44110,out.frequency=8000
> +-audiodev alsa,out.channels=1 # leaves in.channels unspecified
> +@end example
> +
> +Valid global options are:
> +
> +@table @option
> +@item id=@var{identifier}
> +Identifies the audio backend.
> +
> +@item timer-period=@var{period}
> +Sets the timer @var{period} used by the audio subsystem in microseconds.
> +Default is 10000 (10 ms).
> +
> +@item in|out.fixed-settings=on|off
> +Use fixed settings for host audio.  When off, it will change based on
> +how the guest opens the sound card.  In this case you must not specify
> +@var{frequency}, @var{channels} or @var{format}.  Default is on.
> +
> +@item in|out.frequency=@var{frequency}
> +Specify the @var{frequency} to use when using @var{fixed-settings}.
> +Default is 44100Hz.
> +
> +@item in|out.channels=@var{channels}
> +Specify the number of @var{channels} to use when using
> +@var{fixed-settings}. Default is 2 (stereo).
> +
> +@item in|out.format=@var{format}
> +Specify the sample @var{format} to use when using @var{fixed-settings}.
> +Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8},
> +@code{u16}, @code{u32}. Default is @code{s16}.
> +
> +@item in|out.voices=@var{voices}
> +Specify the number of @var{voices} to use.  Default is 1.
> +
> +@item in|out.buffer=@var{usecs}
> +Sets the size of the buffer in microseconds.
> +
> +@item in|out.buffer-count=@var{count}
> +Sets the @var{count} of the buffers.
> +
> +@end table
> +
> +@item -audiodev none,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a dummy backend that discards all outputs.  This backend has no
> +backend specific properties.
> +
> +@item -audiodev alsa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates backend using the ALSA.  This backend is only available on
> +Linux.
> +
> +ALSA specific options are:
> +
> +@table @option
> +@item alsa-in|alsa-out.dev=@var{device}
> +Specify the ALSA @var{device} to use for input and/or output.  Default
> +is @code{default}.
> +
> +@item alsa-in|alsa-out.try-poll=on|off
> +Attempt to use poll mode with the device.  Default is on.
> +
> +@item threshold=@var{threshold}
> +Threshold (in microseconds) when playback starts.  Default is 0.
> +
> +@end table
> +
> +@item -audiodev coreaudio,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend using Apple's Core Audio.  This backend is only
> +available on Mac OS and only supports playback.  This backend has no
> +backend specific properties.
> +
> +@item -audiodev dsound,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend using Microsoft's DirectSound.  This backend is only
> +available on Windows and only supports playback.
> +
> +Backend specific options are:
> +
> +@table @option
> +
> +@item latency=@var{usecs}
> +Add extra @var{usecs} microseconds latency to playback.  Default is
> +10000 (10 ms).
> +
> +@end table
> +
> +@item -audiodev oss,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend using OSS.  This backend is available on most
> +Unix-like systems.
> +
> +OSS specific options are:
> +
> +@table @option
> +
> +@item oss-in|oss-out.dev=@var{device}
> +Specify the file name of the OSS @var{device} to use.  Default is
> +@code{/dev/dsp}.
> +
> +@item oss-in|oss-out.try-poll=on|of
> +Attempt to use poll mode with the device.  Default is on.
> +
> +@item try-mmap=on|off
> +Try using memory mapped device access.  Default is off.
> +
> +@item exclusive=on|off
> +Open the device in exclusive mode (vmix won't work in this case).
> +Default is off.
> +
> +@item dsp-policy=@var{policy}
> +Sets the timing policy (between 0 and 10, where smaller number means
> +smaller latency but higher CPU usage).  Use -1 to use buffer sizes
> +specified by @code{buffer} and @code{buffer-count}.  This option is
> +ignored if you do not have OSS 4. Default is 5.
> +
> +@end table
> +
> +@item -audiodev pa,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend using PulseAudio.  This backend is available on most
> +systems.
> +
> +PulseAudio specific options are:
> +
> +@table @option
> +
> +@item server=@var{server}
> +Sets the PulseAudio @var{server} to connect to.
> +
> +@item sink|source.name=@var{sink}
> +Use the specified sink/source for playback/recording.
> +
> +@end table
> +
> +@item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend using SDL.  This backend is available on most systems,
> +but you should use your platform's native backend if possible.  This
> +backend has no backend specific properties.
> +
> +@item -audiodev spice,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend that sends audio through SPICE.  This backend requires
> +@code{-spice} and automatically selected in that case, so usually you
> +can ignore this option.  This backend has no backend specific
> +properties.
> +
> +@item -audiodev wav,id=@var{id}[,@var{prop}[=@var{value}][,...]]
> +Creates a backend that writes audio to a WAV file.
> +
> +Backend specific options are:
> +
> +@table @option
> +
> +@item path=@var{path}
> +Write recorded audio into the specified file.  Default is
> +@code{qemu.wav}.
> +
> +@end table
>  ETEXI
>
>  DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
> diff --git a/vl.c b/vl.c
> index 0adbbd6..f1aa1df 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3004,6 +3004,7 @@ int main(int argc, char **argv, char **envp)
>      qemu_add_opts(&qemu_trace_opts);
>      qemu_add_opts(&qemu_option_rom_opts);
>      qemu_add_opts(&qemu_machine_opts);
> +    qemu_add_opts(&qemu_audiodev_opts);
>      qemu_add_opts(&qemu_mem_opts);
>      qemu_add_opts(&qemu_smp_opts);
>      qemu_add_opts(&qemu_boot_opts);
> @@ -3309,9 +3310,15 @@ int main(int argc, char **argv, char **envp)
>                  add_device_config(DEV_BT, optarg);
>                  break;
>              case QEMU_OPTION_audio_help:
> -                AUD_help ();
> +                audio_legacy_help();
>                  exit (0);
>                  break;
> +            case QEMU_OPTION_audiodev:
> +                if (!qemu_opts_parse_noisily(qemu_find_opts("audiodev"),
> +                                             optarg, true)) {
> +                    exit(1);
> +                }
> +                break;
>              case QEMU_OPTION_soundhw:
>                  select_soundhw (optarg);
>                  break;
> @@ -4511,6 +4518,7 @@ int main(int argc, char **argv, char **envp)
>
>      realtime_init();
>
> +    audio_set_options();
>      audio_init();
>
>      cpu_synchronize_all_post_init();
> --
> 2.4.5
>
>

overall looks good to me, but it will require testing.

Tbh, I wonder why qemu has so many audio backends and options...
(linking with gstreamer would be just one more lib of 1mb after all,
with a lot more flexibility, and it could be made a module..) :)

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 11/25] audio: reduce glob_audio_state usage
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 11/25] audio: reduce glob_audio_state usage Kővágó, Zoltán
@ 2015-08-20 17:39   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 17:39 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>

Fill a bit the commit message, to say simply use AudioState instead.
Also explain why you add AudioState *s in struct although it's not
used in that commit, saying simply it's going to be used in later
commit is enough imho (I guess it is).

> ---
>  audio/audio.c          | 41 ++++++++++++++++++++---------------------
>  audio/audio_int.h      |  4 ++++
>  audio/audio_template.h | 46 ++++++++++++++++++++++------------------------
>  3 files changed, 46 insertions(+), 45 deletions(-)
>
> diff --git a/audio/audio.c b/audio/audio.c
> index 08ac15e..05b24dc 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -367,12 +367,10 @@ static void noop_conv (struct st_sample *dst, const void *src, int samples)
>      (void) samples;
>  }
>
> -static CaptureVoiceOut *audio_pcm_capture_find_specific (
> -    struct audsettings *as
> -    )
> +static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
> +                                                        struct audsettings *as)
>  {
>      CaptureVoiceOut *cap;
> -    AudioState *s = &glob_audio_state;
>
>      for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
>          if (audio_pcm_info_eq (&cap->hw.info, as)) {
> @@ -449,7 +447,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
>
>  static int audio_attach_capture (HWVoiceOut *hw)
>  {
> -    AudioState *s = &glob_audio_state;
> +    AudioState *s = hw->s;
>      CaptureVoiceOut *cap;
>
>      audio_detach_capture (hw);
> @@ -762,15 +760,15 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
>  /*
>   * Timer
>   */
> -static int audio_is_timer_needed (void)
> +static int audio_is_timer_needed(AudioState *s)
>  {
>      HWVoiceIn *hwi = NULL;
>      HWVoiceOut *hwo = NULL;
>
> -    while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
> +    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
>          if (!hwo->poll_mode) return 1;
>      }
> -    while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
> +    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
>          if (!hwi->poll_mode) return 1;
>      }
>      return 0;
> @@ -778,7 +776,7 @@ static int audio_is_timer_needed (void)
>
>  static void audio_reset_timer (AudioState *s)
>  {
> -    if (audio_is_timer_needed ()) {
> +    if (audio_is_timer_needed(s)) {
>          timer_mod (s->ts,
>              qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
>      }
> @@ -847,7 +845,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
>
>      hw = sw->hw;
>      if (sw->active != on) {
> -        AudioState *s = &glob_audio_state;
> +        AudioState *s = sw->s;
>          SWVoiceOut *temp_sw;
>          SWVoiceCap *sc;
>
> @@ -894,7 +892,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
>
>      hw = sw->hw;
>      if (sw->active != on) {
> -        AudioState *s = &glob_audio_state;
> +        AudioState *s = sw->s;
>          SWVoiceIn *temp_sw;
>
>          if (on) {
> @@ -1017,7 +1015,7 @@ static void audio_run_out (AudioState *s)
>      HWVoiceOut *hw = NULL;
>      SWVoiceOut *sw;
>
> -    while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) {
> +    while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
>          int played;
>          int live, free, nb_live, cleanup_required, prev_rpos;
>
> @@ -1121,7 +1119,7 @@ static void audio_run_in (AudioState *s)
>  {
>      HWVoiceIn *hw = NULL;
>
> -    while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) {
> +    while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
>          SWVoiceIn *sw;
>          int captured, min;
>
> @@ -1224,8 +1222,8 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv,
>      s->drv_opaque = drv->init(dev);
>
>      if (s->drv_opaque) {
> -        audio_init_nb_voices_out (drv);
> -        audio_init_nb_voices_in (drv);
> +        audio_init_nb_voices_out(s, drv);
> +        audio_init_nb_voices_in(s, drv);
>          s->drv = drv;
>          return 0;
>      }
> @@ -1244,11 +1242,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
>      int op = running ? VOICE_ENABLE : VOICE_DISABLE;
>
>      s->vm_running = running;
> -    while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
> +    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
>          hwo->pcm_ops->ctl_out (hwo, op);
>      }
>
> -    while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
> +    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
>          hwi->pcm_ops->ctl_in (hwi, op);
>      }
>      audio_reset_timer (s);
> @@ -1260,7 +1258,7 @@ static void audio_atexit (void)
>      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))) {
>          SWVoiceCap *sc;
>
>          if (hwo->enabled) {
> @@ -1278,7 +1276,7 @@ static void audio_atexit (void)
>          }
>      }
>
> -    while ((hwi = audio_pcm_hw_find_any_in (hwi))) {
> +    while ((hwi = audio_pcm_hw_find_any_in(s, hwi))) {
>          if (hwi->enabled) {
>              hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
>          }
> @@ -1459,7 +1457,7 @@ CaptureVoiceOut *AUD_add_capture (
>      cb->ops = *ops;
>      cb->opaque = cb_opaque;
>
> -    cap = audio_pcm_capture_find_specific (as);
> +    cap = audio_pcm_capture_find_specific(s, as);
>      if (cap) {
>          QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
>          return cap;
> @@ -1476,6 +1474,7 @@ CaptureVoiceOut *AUD_add_capture (
>          }
>
>          hw = &cap->hw;
> +        hw->s = s;
>          QLIST_INIT (&hw->sw_head);
>          QLIST_INIT (&cap->cb_head);
>
> @@ -1509,7 +1508,7 @@ CaptureVoiceOut *AUD_add_capture (
>          QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
>
>          hw = NULL;
> -        while ((hw = audio_pcm_hw_find_any_out (hw))) {
> +        while ((hw = audio_pcm_hw_find_any_out(s, hw))) {
>              audio_attach_capture (hw);
>          }
>          return cap;
> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index 59b2362..101081b 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -51,6 +51,7 @@ struct audio_pcm_info {
>  typedef struct SWVoiceCap SWVoiceCap;
>
>  typedef struct HWVoiceOut {
> +    AudioState *s;
>      int enabled;
>      int poll_mode;
>      int pending_disable;
> @@ -72,6 +73,7 @@ typedef struct HWVoiceOut {
>  } HWVoiceOut;
>
>  typedef struct HWVoiceIn {
> +    AudioState *s;
>      int enabled;
>      int poll_mode;
>      struct audio_pcm_info info;
> @@ -93,6 +95,7 @@ typedef struct HWVoiceIn {
>
>  struct SWVoiceOut {
>      QEMUSoundCard *card;
> +    AudioState *s;
>      struct audio_pcm_info info;
>      t_sample *conv;
>      int64_t ratio;
> @@ -110,6 +113,7 @@ struct SWVoiceOut {
>
>  struct SWVoiceIn {
>      QEMUSoundCard *card;
> +    AudioState *s;
>      int active;
>      struct audio_pcm_info info;
>      int64_t ratio;
> diff --git a/audio/audio_template.h b/audio/audio_template.h
> index 096b2b3..455e50d 100644
> --- a/audio/audio_template.h
> +++ b/audio/audio_template.h
> @@ -36,9 +36,9 @@
>  #define HWBUF hw->conv_buf
>  #endif
>
> -static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
> +static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
> +                                              struct audio_driver *drv)
>  {
> -    AudioState *s = &glob_audio_state;
>      int max_voices = glue (drv->max_voices_, TYPE);
>      int voice_size = glue (drv->voice_size_, TYPE);
>
> @@ -183,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
>
>  static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
>  {
> -    AudioState *s = &glob_audio_state;
>      HW *hw = *hwp;
> +    AudioState *s = hw->s;
>
>      if (!hw->sw_head.lh_first) {
>  #ifdef DAC
> @@ -199,15 +199,14 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
>      }
>  }
>
> -static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
> +static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
>  {
> -    AudioState *s = &glob_audio_state;
>      return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
>  }
>
> -static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
> +static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
>  {
> -    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
> +    while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
>          if (hw->enabled) {
>              return hw;
>          }
> @@ -215,12 +214,10 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
>      return NULL;
>  }
>
> -static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
> -    HW *hw,
> -    struct audsettings *as
> -    )
> +static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
> +                                                   struct audsettings *as)
>  {
> -    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
> +    while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
>          if (audio_pcm_info_eq (&hw->info, as)) {
>              return hw;
>          }
> @@ -228,10 +225,10 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
>      return NULL;
>  }
>
> -static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
> +static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
> +                                             struct audsettings *as)
>  {
>      HW *hw;
> -    AudioState *s = &glob_audio_state;
>      struct audio_driver *drv = s->drv;
>
>      if (!glue (s->nb_hw_voices_, TYPE)) {
> @@ -255,6 +252,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
>          return NULL;
>      }
>
> +    hw->s = s;
>      hw->pcm_ops = drv->pcm_ops;
>      hw->ctl_caps = drv->ctl_caps;
>
> @@ -299,33 +297,33 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
>      return NULL;
>  }
>
> -static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
> +static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
>  {
>      HW *hw;
> -    AudioState *s = &glob_audio_state;
>      AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
>
>      if (pdo->fixed_settings) {
> -        hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
> +        hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
>          if (hw) {
>              return hw;
>          }
>      }
>
> -    hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
> +    hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
>      if (hw) {
>          return hw;
>      }
>
> -    hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
> +    hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
>      if (hw) {
>          return hw;
>      }
>
> -    return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
> +    return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
>  }
>
> -static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
> +static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
> +    AudioState *s,
>      const char *sw_name,
>      struct audsettings *as
>      )
> @@ -333,7 +331,6 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
>      SW *sw;
>      HW *hw;
>      struct audsettings hw_as;
> -    AudioState *s = &glob_audio_state;
>      AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
>
>      if (pdo->fixed_settings) {
> @@ -349,8 +346,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
>                 sw_name ? sw_name : "unknown", sizeof (*sw));
>          goto err1;
>      }
> +    sw->s = s;
>
> -    hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
> +    hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
>      if (!hw) {
>          goto err2;
>      }
> @@ -447,7 +445,7 @@ SW *glue (AUD_open_, TYPE) (
>          }
>      }
>      else {
> -        sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
> +        sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
>          if (!sw) {
>              dolog ("Failed to create voice `%s'\n", name);
>              return NULL;
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio Kővágó, Zoltán
@ 2015-08-20 18:01   ` Marc-André Lureau
  2015-08-21 12:23     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 18:01 UTC (permalink / raw)
  To: Kővágó, Zoltán
  Cc: Paolo Bonzini, Luiz Capitulino, Gerd Hoffmann, QEMU, Markus Armbruster

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Audio functions no longer access glob_audio_state, instead they get an
> AudioState as a parameter.  This is required in order to support
> multiple backends.
>
> glob_audio_state is also gone, and replaced with a tailq so we can store
> more than one states.
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/audio.c          | 95 +++++++++++++++++++++++++++++++++++++-------------
>  audio/audio.h          | 12 +++++--
>  audio/audio_int.h      |  2 ++
>  audio/audio_template.h |  2 +-
>  audio/wavcapture.c     |  6 ++--
>  hmp-commands.hx        | 11 +++---
>  monitor.c              | 12 ++++++-
>  qemu-options.hx        |  5 +++
>  ui/vnc.c               | 15 +++++++-
>  ui/vnc.h               |  2 ++
>  vl.c                   |  3 +-
>  11 files changed, 126 insertions(+), 39 deletions(-)
>
> diff --git a/audio/audio.c b/audio/audio.c
> index 05b24dc..10b9871 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -55,7 +55,8 @@ struct audio_driver *drvtab[] = {
>      NULL
>  };
>
> -static AudioState glob_audio_state;
> +static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
> +    QTAILQ_HEAD_INITIALIZER(audio_states);
>
>  const struct mixeng_volume nominal_volume = {
>      .mute = 0,
> @@ -1193,11 +1194,14 @@ static void audio_run_capture (AudioState *s)
>
>  void audio_run (const char *msg)
>  {
> -    AudioState *s = &glob_audio_state;
> +    AudioState *s;
> +
> +    QTAILQ_FOREACH(s, &audio_states, list) {
> +        audio_run_out (s);
> +        audio_run_in (s);
> +        audio_run_capture (s);
> +    }
>
> -    audio_run_out (s);
> -    audio_run_in (s);
> -    audio_run_capture (s);
>  #ifdef DEBUG_POLL
>      {
>          static double prevtime;
> @@ -1252,9 +1256,8 @@ static void audio_vm_change_state_handler (void *opaque, int running,
>      audio_reset_timer (s);
>  }
>
> -static void audio_atexit (void)
> +static void free_audio_state(AudioState *s)
>  {
> -    AudioState *s = &glob_audio_state;
>      HWVoiceOut *hwo = NULL;
>      HWVoiceIn *hwi = NULL;
>
> @@ -1288,6 +1291,16 @@ static void audio_atexit (void)
>      }
>
>      qapi_free_Audiodev(s->dev);
> +    g_free(s);
> +}
> +
> +static void audio_atexit(void)
> +{
> +    while (!QTAILQ_EMPTY(&audio_states)) {
> +        AudioState *s = QTAILQ_FIRST(&audio_states);
> +        QTAILQ_REMOVE(&audio_states, s, list);
> +        free_audio_state(s);
> +    }
>  }
>
>  static const VMStateDescription vmstate_audio = {
> @@ -1300,26 +1313,25 @@ static const VMStateDescription vmstate_audio = {
>  };
>
>  static Audiodev *parse_option(QemuOpts *opts, Error **errp);
> -static int audio_init(Audiodev *dev)
> +static AudioState *audio_init(Audiodev *dev)
>  {
> +    static bool atexit_registered;
>      size_t i;
>      int done = 0;
>      const char *drvname = NULL;
>      VMChangeStateEntry *e;
> -    AudioState *s = &glob_audio_state;
> +    AudioState *s;
>      QemuOptsList *list = NULL; /* silence gcc warning about uninitialized
>                                  * variable */
>
> -    if (s->drv) {
> -        if (dev) {
> -            dolog("Cannot create more than one audio backend, sorry\n");
> -            qapi_free_Audiodev(dev);
> -        }
> -        return -1;
> -    }
> -
> +    /* if we have dev, this function was called because of an -audiodev
> +     * argument => initialize a new state with it
> +     * if dev == NULL => legacy implicit initialization, return the already
> +     * created state or create a new one */

I think you could place this comment above the function.

>      if (dev) {
>          drvname = AudiodevDriver_lookup[dev->kind];
> +    } else if (!QTAILQ_EMPTY(&audio_states)) {
> +        return QTAILQ_FIRST(&audio_states);
>      } else {
>          audio_handle_legacy_opts();
>          list = qemu_find_opts("audiodev");
> @@ -1328,12 +1340,18 @@ static int audio_init(Audiodev *dev)
>              exit(1);
>          }
>      }
> +
> +    s = g_malloc0(sizeof(AudioState));
>      s->dev = dev;
>
>      QLIST_INIT (&s->hw_head_out);
>      QLIST_INIT (&s->hw_head_in);
>      QLIST_INIT (&s->cap_head);
> -    atexit (audio_atexit);
> +    if (!atexit_registered) {
> +        atexit(audio_atexit);
> +        atexit_registered = true;
> +    }
> +    QTAILQ_INSERT_TAIL(&audio_states, s, list);
>
>      s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
>      if (!s->ts) {
> @@ -1414,15 +1432,18 @@ static int audio_init(Audiodev *dev)
>
>      QLIST_INIT (&s->card_head);
>      vmstate_register (NULL, 0, &vmstate_audio, s);
> -    return 0;
> +    return s;
>  }
>
>  void AUD_register_card (const char *name, QEMUSoundCard *card)
>  {
> -    audio_init(NULL);
> +    if (!card->state) {
> +        card->state = audio_init(NULL);
> +    }
> +
>      card->name = g_strdup (name);
>      memset (&card->entries, 0, sizeof (card->entries));
> -    QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
> +    QLIST_INSERT_HEAD (&card->state->card_head, card, entries);
>  }
>
>  void AUD_remove_card (QEMUSoundCard *card)
> @@ -1432,16 +1453,20 @@ void AUD_remove_card (QEMUSoundCard *card)
>  }
>
>
> -CaptureVoiceOut *AUD_add_capture (
> +CaptureVoiceOut *AUD_add_capture(
> +    AudioState *s,
>      struct audsettings *as,
>      struct audio_capture_ops *ops,
>      void *cb_opaque
>      )
>  {
> -    AudioState *s = &glob_audio_state;
>      CaptureVoiceOut *cap;
>      struct capture_callback *cb;
>
> +    if (!s) { /* todo */

What's the todo here for?

> +        s = QTAILQ_FIRST(&audio_states);
> +    }
> +
>      if (audio_validate_settings (as)) {
>          dolog ("Invalid settings were passed when trying to add capture\n");
>          audio_print_settings (as);
> @@ -1677,7 +1702,7 @@ static int each_option(void *opaque, QemuOpts *opts, Error **errp)
>      if (!dev) {
>          return -1;
>      }
> -    return audio_init(dev);
> +    return audio_init(dev) ? 0 : -1;
>  }
>
>  void audio_set_options(void)
> @@ -1743,3 +1768,25 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
>      return audio_buffer_samples(pdo, as, def_usecs) *
>          audioformat_bytes_per_sample(as->fmt);
>  }
> +
> +AudioState *audio_state_by_name(const char *name)
> +{
> +    AudioState *s;
> +    QTAILQ_FOREACH(s, &audio_states, list) {
> +        assert(s->dev);
> +        if (strcmp(name, s->dev->id) == 0) {
> +            return s;
> +        }
> +    }
> +    return NULL;
> +}
> +
> +const char *audio_get_id(QEMUSoundCard *card)
> +{
> +    if (card->state) {
> +        assert(card->state->dev);
> +        return card->state->dev->id;
> +    } else {
> +        return "";
> +    }
> +}
> diff --git a/audio/audio.h b/audio/audio.h
> index 177a673..0085a07 100644
> --- a/audio/audio.h
> +++ b/audio/audio.h
> @@ -80,8 +80,10 @@ typedef struct SWVoiceOut SWVoiceOut;
>  typedef struct CaptureVoiceOut CaptureVoiceOut;
>  typedef struct SWVoiceIn SWVoiceIn;
>
> +typedef struct AudioState AudioState;
>  typedef struct QEMUSoundCard {
>      char *name;
> +    AudioState *state;
>      QLIST_ENTRY (QEMUSoundCard) entries;
>  } QEMUSoundCard;
>
> @@ -96,7 +98,8 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
>
>  void AUD_register_card (const char *name, QEMUSoundCard *card);
>  void AUD_remove_card (QEMUSoundCard *card);
> -CaptureVoiceOut *AUD_add_capture (
> +CaptureVoiceOut *AUD_add_capture(
> +    AudioState *s,
>      struct audsettings *as,
>      struct audio_capture_ops *ops,
>      void *opaque
> @@ -164,11 +167,14 @@ static inline void *advance (void *p, int incr)
>  #define audio_MAX(a, b) ((a)<(b)?(b):(a))
>  #endif
>
> -int wav_start_capture (CaptureState *s, const char *path, int freq,
> -                       int bits, int nchannels);
> +int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
> +                      int freq, int bits, int nchannels);
>
>  void audio_set_options(void);
>  void audio_handle_legacy_opts(void);
>  void audio_legacy_help(void);
>
> +AudioState *audio_state_by_name(const char *name);
> +const char *audio_get_id(QEMUSoundCard *card);
> +
>  #endif  /* audio.h */
> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index 101081b..1d81658 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -189,6 +189,8 @@ struct AudioState {
>      int nb_hw_voices_in;
>      int vm_running;
>      int64_t period_ticks;
> +
> +    QTAILQ_ENTRY(AudioState) list;
>  };
>
>  extern struct audio_driver no_audio_driver;
> diff --git a/audio/audio_template.h b/audio/audio_template.h
> index 455e50d..5a3dc90 100644
> --- a/audio/audio_template.h
> +++ b/audio/audio_template.h
> @@ -399,7 +399,7 @@ SW *glue (AUD_open_, TYPE) (
>      struct audsettings *as
>      )
>  {
> -    AudioState *s = &glob_audio_state;
> +    AudioState *s = card->state;
>      AudiodevPerDirectionOptions *pdo = s->dev->TYPE;
>
>      if (audio_bug (AUDIO_FUNC, !card || !name || !callback_fn || !as)) {
> diff --git a/audio/wavcapture.c b/audio/wavcapture.c
> index 798acdd..8b8d3d0 100644
> --- a/audio/wavcapture.c
> +++ b/audio/wavcapture.c
> @@ -104,8 +104,8 @@ static struct capture_ops wav_capture_ops = {
>      .info = wav_capture_info
>  };
>
> -int wav_start_capture (CaptureState *s, const char *path, int freq,
> -                       int bits, int nchannels)
> +int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
> +                      int freq, int bits, int nchannels)
>  {
>      Monitor *mon = cur_mon;
>      WAVState *wav;
> @@ -172,7 +172,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
>          goto error_free;
>      }
>
> -    cap = AUD_add_capture (&as, &ops, wav);
> +    cap = AUD_add_capture(state, &as, &ops, wav);
>      if (!cap) {
>          monitor_printf (mon, "Failed to add audio capture\n");
>          goto error_free;
> diff --git a/hmp-commands.hx b/hmp-commands.hx
> index d3b7932..9b49dc3 100644
> --- a/hmp-commands.hx
> +++ b/hmp-commands.hx
> @@ -741,16 +741,17 @@ ETEXI
>
>      {
>          .name       = "wavcapture",
> -        .args_type  = "path:F,freq:i?,bits:i?,nchannels:i?",
> -        .params     = "path [frequency [bits [channels]]]",
> +        .args_type  = "path:F,freq:i?,bits:i?,nchannels:i?,audiodev:s?",
> +        .params     = "path [frequency [bits [channels [audiodev]]]]",
>          .help       = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
>          .mhandler.cmd = hmp_wavcapture,
>      },
>  STEXI
> -@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]]
> +@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels} [@var{audiodev}]]]]
>  @findex wavcapture
> -Capture audio into @var{filename}. Using sample rate @var{frequency}
> -bits per sample @var{bits} and number of channels @var{channels}.
> +Capture audio into @var{filename} from @var{audiodev}. Using sample rate
> +@var{frequency} bits per sample @var{bits} and number of channels
> +@var{channels}.
>
>  Defaults:
>  @itemize @minus
> diff --git a/monitor.c b/monitor.c
> index a40138b..f527753 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1944,7 +1944,17 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
>      int bits = qdict_get_try_int(qdict, "bits", -1);
>      int has_channels = qdict_haskey(qdict, "nchannels");
>      int nchannels = qdict_get_try_int(qdict, "nchannels", -1);
> +    const char *audiodev = qdict_get_try_str(qdict, "audiodev");
>      CaptureState *s;
> +    AudioState *as = NULL;
> +
> +    if (audiodev) {
> +        as = audio_state_by_name(audiodev);
> +        if (!as) {
> +            monitor_printf(mon, "Invalid audiodev specified\n");
> +            return;
> +        }
> +    }
>
>      s = g_malloc0 (sizeof (*s));
>
> @@ -1952,7 +1962,7 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
>      bits = has_bits ? bits : 16;
>      nchannels = has_channels ? nchannels : 2;
>
> -    if (wav_start_capture (s, path, freq, bits, nchannels)) {
> +    if (wav_start_capture(as, s, path, freq, bits, nchannels)) {
>          monitor_printf(mon, "Failed to add wave capture\n");
>          g_free (s);
>          return;
> diff --git a/qemu-options.hx b/qemu-options.hx
> index efd57e6..60a3563 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1538,6 +1538,11 @@ everybody else.  'ignore' completely ignores the shared flag and
>  allows everybody connect unconditionally.  Doesn't conform to the rfb
>  spec but is traditional QEMU behavior.
>
> +@item audiodev=@var{audiodev}
> +
> +Use the specified @var{audiodev} when the VNC client requests audio
> +transmission.

That doesn't make much sense to me, vnc (or spice) should be connected
to the guest device, not to the audiodev. Same for for the wavcapture.

> +
>  @end table
>  ETEXI
>
> diff --git a/ui/vnc.c b/ui/vnc.c
> index febbad6..feee4ef 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -1193,7 +1193,7 @@ static void audio_add(VncState *vs)
>      ops.destroy = audio_capture_destroy;
>      ops.capture = audio_capture;
>
> -    vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs);
> +    vs->audio_cap = AUD_add_capture(vs->vd->audio_state, &vs->as, &ops, vs);
>      if (!vs->audio_cap) {
>          error_report("Failed to add audio capture");
>      }
> @@ -3295,6 +3295,9 @@ static QemuOptsList qemu_vnc_opts = {
>          },{
>              .name = "non-adaptive",
>              .type = QEMU_OPT_BOOL,
> +        },{
> +            .name = "audiodev",
> +            .type = QEMU_OPT_STRING,
>          },
>          { /* end of list */ }
>      },
> @@ -3455,6 +3458,7 @@ void vnc_display_open(const char *id, Error **errp)
>      int acl = 0;
>  #endif
>      int lock_key_sync = 1;
> +    const char *audiodev;
>
>      if (!vs) {
>          error_setg(errp, "VNC display not active");
> @@ -3637,6 +3641,15 @@ void vnc_display_open(const char *id, Error **errp)
>  #endif
>      vs->lock_key_sync = lock_key_sync;
>
> +    audiodev = qemu_opt_get(opts, "audiodev");
> +    if (audiodev) {
> +        vs->audio_state = audio_state_by_name(audiodev);
> +        if (!vs->audio_state) {
> +            error_setg(errp, "Audiodev '%s' not found", audiodev);
> +            goto fail;
> +        }
> +    }
> +
>      device_id = qemu_opt_get(opts, "display");
>      if (device_id) {
>          DeviceState *dev;
> diff --git a/ui/vnc.h b/ui/vnc.h
> index 814d720..f73bb94 100644
> --- a/ui/vnc.h
> +++ b/ui/vnc.h
> @@ -187,6 +187,8 @@ struct VncDisplay
>  #ifdef CONFIG_VNC_SASL
>      VncDisplaySASL sasl;
>  #endif
> +
> +    AudioState *audio_state;
>  };
>
>  typedef struct VncTight {
> diff --git a/vl.c b/vl.c
> index f1aa1df..30bc8be 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -4315,6 +4315,8 @@ int main(int argc, char **argv, char **envp)
>          exit(1);
>      }
>
> +    audio_set_options();
> +
>      machine_opts = qemu_get_machine_opts();
>      if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine,
>                           NULL)) {
> @@ -4518,7 +4520,6 @@ int main(int argc, char **argv, char **envp)
>
>      realtime_init();
>
> -    audio_set_options();
>      audio_init();
>
>      cpu_synchronize_all_post_init();
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 13/25] audio: add audiodev properties to frontends
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 13/25] audio: add audiodev properties to frontends Kővágó, Zoltán
@ 2015-08-20 18:55   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 18:55 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: Michael Walle, QEMU, Gerd Hoffmann

looks good,

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Finally add audiodev= options to audio frontends so users can specify
> which backend to use when multiple backends exist.  Not specifying an
> audiodev= option currently causes the first audiodev to be used, this is
> fixed in the next commit.
>
> Example usage: -audiodev pa,id=foo -device AC97,audiodev=foo
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/audio.h                    |  3 +++
>  hw/audio/ac97.c                  |  1 +
>  hw/audio/adlib.c                 |  1 +
>  hw/audio/cs4231a.c               |  1 +
>  hw/audio/es1370.c                |  7 ++++-
>  hw/audio/gus.c                   |  1 +
>  hw/audio/hda-codec.c             |  1 +
>  hw/audio/milkymist-ac97.c        |  6 +++++
>  hw/audio/pcspk.c                 |  1 +
>  hw/audio/pl041.c                 |  1 +
>  hw/audio/sb16.c                  |  1 +
>  hw/audio/wm8750.c                |  6 +++++
>  hw/core/qdev-properties-system.c | 57 ++++++++++++++++++++++++++++++++++++++++
>  hw/usb/dev-audio.c               |  1 +
>  include/hw/qdev-properties.h     |  3 +++
>  15 files changed, 90 insertions(+), 1 deletion(-)
>
> diff --git a/audio/audio.h b/audio/audio.h
> index 0085a07..68545b6 100644
> --- a/audio/audio.h
> +++ b/audio/audio.h
> @@ -177,4 +177,7 @@ void audio_legacy_help(void);
>  AudioState *audio_state_by_name(const char *name);
>  const char *audio_get_id(QEMUSoundCard *card);
>
> +#define DEFINE_AUDIO_PROPERTIES(_s, _f)         \
> +    DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
> +
>  #endif  /* audio.h */
> diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c
> index fa75f33..a835cb6 100644
> --- a/hw/audio/ac97.c
> +++ b/hw/audio/ac97.c
> @@ -1393,6 +1393,7 @@ static int ac97_init (PCIBus *bus)
>  }
>
>  static Property ac97_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(AC97LinkState, card),
>      DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0),
>      DEFINE_PROP_END_OF_LIST (),
>  };
> diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c
> index f8f0f55..58de8b0 100644
> --- a/hw/audio/adlib.c
> +++ b/hw/audio/adlib.c
> @@ -352,6 +352,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
>  }
>
>  static Property adlib_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(AdlibState, card),
>      DEFINE_PROP_UINT32 ("iobase",  AdlibState, port, 0x220),
>      DEFINE_PROP_UINT32 ("freq",    AdlibState, freq,  44100),
>      DEFINE_PROP_END_OF_LIST (),
> diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c
> index 626a173..43a1a39 100644
> --- a/hw/audio/cs4231a.c
> +++ b/hw/audio/cs4231a.c
> @@ -672,6 +672,7 @@ static int cs4231a_init (ISABus *bus)
>  }
>
>  static Property cs4231a_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(CSState, card),
>      DEFINE_PROP_UINT32 ("iobase",  CSState, port, 0x534),
>      DEFINE_PROP_UINT32 ("irq",     CSState, irq,  9),
>      DEFINE_PROP_UINT32 ("dma",     CSState, dma,  3),
> diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c
> index f6e74cb..fe6110d 100644
> --- a/hw/audio/es1370.c
> +++ b/hw/audio/es1370.c
> @@ -1047,6 +1047,11 @@ static int es1370_init (PCIBus *bus)
>      return 0;
>  }
>
> +static Property es1370_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(ES1370State, card),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
>  static void es1370_class_init (ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS (klass);
> @@ -1061,6 +1066,7 @@ static void es1370_class_init (ObjectClass *klass, void *data)
>      set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
>      dc->desc = "ENSONIQ AudioPCI ES1370";
>      dc->vmsd = &vmstate_es1370;
> +    dc->props = es1370_properties;
>  }
>
>  static const TypeInfo es1370_info = {
> @@ -1077,4 +1083,3 @@ static void es1370_register_types (void)
>  }
>
>  type_init (es1370_register_types)
> -
> diff --git a/hw/audio/gus.c b/hw/audio/gus.c
> index 6107824..ee08d50 100644
> --- a/hw/audio/gus.c
> +++ b/hw/audio/gus.c
> @@ -284,6 +284,7 @@ static int GUS_init (ISABus *bus)
>  }
>
>  static Property gus_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(GUSState, card),
>      DEFINE_PROP_UINT32 ("freq",    GUSState, freq,        44100),
>      DEFINE_PROP_UINT32 ("iobase",  GUSState, port,        0x240),
>      DEFINE_PROP_UINT32 ("irq",     GUSState, emu.gusirq,  7),
> diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c
> index 8693b7a..dba3331 100644
> --- a/hw/audio/hda-codec.c
> +++ b/hw/audio/hda-codec.c
> @@ -613,6 +613,7 @@ static const VMStateDescription vmstate_hda_audio = {
>  };
>
>  static Property hda_audio_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(HDAAudioState, card),
>      DEFINE_PROP_UINT32("debug", HDAAudioState, debug,   0),
>      DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer,  true),
>      DEFINE_PROP_END_OF_LIST(),
> diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c
> index 15169e2..b63fd6f 100644
> --- a/hw/audio/milkymist-ac97.c
> +++ b/hw/audio/milkymist-ac97.c
> @@ -323,6 +323,11 @@ static const VMStateDescription vmstate_milkymist_ac97 = {
>      }
>  };
>
> +static Property milkymist_ac97_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(MilkymistAC97State, card),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
>  static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
> @@ -331,6 +336,7 @@ static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
>      k->init = milkymist_ac97_init;
>      dc->reset = milkymist_ac97_reset;
>      dc->vmsd = &vmstate_milkymist_ac97;
> +    dc->props = milkymist_ac97_properties;
>  }
>
>  static const TypeInfo milkymist_ac97_info = {
> diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c
> index 302debf..85d6d00 100644
> --- a/hw/audio/pcspk.c
> +++ b/hw/audio/pcspk.c
> @@ -181,6 +181,7 @@ static void pcspk_realizefn(DeviceState *dev, Error **errp)
>  }
>
>  static Property pcspk_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(PCSpkState, card),
>      DEFINE_PROP_UINT32("iobase", PCSpkState, iobase,  -1),
>      DEFINE_PROP_PTR("pit", PCSpkState, pit),
>      DEFINE_PROP_END_OF_LIST(),
> diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c
> index 19982f2..c003b26 100644
> --- a/hw/audio/pl041.c
> +++ b/hw/audio/pl041.c
> @@ -616,6 +616,7 @@ static const VMStateDescription vmstate_pl041 = {
>  };
>
>  static Property pl041_device_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(PL041State, codec.card),
>      /* Non-compact FIFO depth property */
>      DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth,
>                         DEFAULT_FIFO_DEPTH),
> diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c
> index a159dcc..4bbb718 100644
> --- a/hw/audio/sb16.c
> +++ b/hw/audio/sb16.c
> @@ -1391,6 +1391,7 @@ static int SB16_init (ISABus *bus)
>  }
>
>  static Property sb16_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(SB16State, card),
>      DEFINE_PROP_UINT32 ("version", SB16State, ver,  0x0405), /* 4.5 */
>      DEFINE_PROP_UINT32 ("iobase",  SB16State, port, 0x220),
>      DEFINE_PROP_UINT32 ("irq",     SB16State, irq,  5),
> diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c
> index 4c4333c..049ddf9 100644
> --- a/hw/audio/wm8750.c
> +++ b/hw/audio/wm8750.c
> @@ -695,6 +695,11 @@ void wm8750_set_bclk_in(void *opaque, int new_hz)
>      wm8750_clk_update(s, 1);
>  }
>
> +static Property wm8750_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(WM8750State, card),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
>  static void wm8750_class_init(ObjectClass *klass, void *data)
>  {
>      DeviceClass *dc = DEVICE_CLASS(klass);
> @@ -705,6 +710,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
>      sc->recv = wm8750_rx;
>      sc->send = wm8750_tx;
>      dc->vmsd = &vmstate_wm8750;
> +    dc->props = wm8750_properties;
>  }
>
>  static const TypeInfo wm8750_info = {
> diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
> index 249976e..3807c86 100644
> --- a/hw/core/qdev-properties-system.c
> +++ b/hw/core/qdev-properties-system.c
> @@ -10,6 +10,7 @@
>   * See the COPYING file in the top-level directory.
>   */
>
> +#include "audio/audio.h"
>  #include "net/net.h"
>  #include "hw/qdev.h"
>  #include "qapi/qmp/qerror.h"
> @@ -357,6 +358,62 @@ PropertyInfo qdev_prop_vlan = {
>      .set   = set_vlan,
>  };
>
> +/* --- audiodev --- */
> +static void get_audiodev(Object *obj, Visitor *v, void *opaque,
> +                         const char *name, Error **errp)
> +{
> +    DeviceState *dev = DEVICE(obj);
> +    Property *prop = opaque;
> +    QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
> +    char *p = g_strdup(audio_get_id(card));
> +
> +    visit_type_str(v, &p, name, errp);
> +    g_free(p);
> +}
> +
> +static void set_audiodev(Object *obj, Visitor *v, void *opaque,
> +                         const char *name, Error **errp)
> +{
> +    DeviceState *dev = DEVICE(obj);
> +    Property *prop = opaque;
> +    QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
> +    AudioState *state;
> +    Error *local_err = NULL;
> +    int err = 0;
> +    char *str;
> +
> +    if (dev->realized) {
> +        qdev_prop_set_after_realize(dev, name, errp);
> +        return;
> +    }
> +
> +    visit_type_str(v, &str, name, &local_err);
> +    if (local_err) {
> +        error_propagate(errp, local_err);
> +        return;
> +    }
> +
> +    state = audio_state_by_name(str);
> +
> +    if (!state) {
> +        err = -ENOENT;
> +        goto out;
> +    }
> +    card->state = state;
> +
> +out:
> +    error_set_from_qdev_prop_error(errp, err, dev, prop, str);
> +    g_free(str);
> +}
> +
> +PropertyInfo qdev_prop_audiodev = {
> +    .name = "str",
> +    .description = "ID of an audiodev to use as a backend",
> +    /* release done on shutdown */
> +    .get = get_audiodev,
> +    .set = set_audiodev,
> +};
> +
>  void qdev_prop_set_drive(DeviceState *dev, const char *name,
>                           BlockBackend *value, Error **errp)
>  {
> diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
> index 0171579..48ac992 100644
> --- a/hw/usb/dev-audio.c
> +++ b/hw/usb/dev-audio.c
> @@ -662,6 +662,7 @@ static const VMStateDescription vmstate_usb_audio = {
>  };
>
>  static Property usb_audio_properties[] = {
> +    DEFINE_AUDIO_PROPERTIES(USBAudioState, card),
>      DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
>      DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
>                         8 * USBAUDIO_PACKET_SIZE),
> diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
> index 77538a8..edebcfa 100644
> --- a/include/hw/qdev-properties.h
> +++ b/include/hw/qdev-properties.h
> @@ -26,6 +26,7 @@ extern PropertyInfo qdev_prop_vlan;
>  extern PropertyInfo qdev_prop_pci_devfn;
>  extern PropertyInfo qdev_prop_blocksize;
>  extern PropertyInfo qdev_prop_pci_host_devaddr;
> +extern PropertyInfo qdev_prop_audiodev;
>  extern PropertyInfo qdev_prop_arraylen;
>
>  #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \
> @@ -163,6 +164,8 @@ extern PropertyInfo qdev_prop_arraylen;
>      DEFINE_PROP_DEFAULT(_n, _s, _f, 0, qdev_prop_blocksize, uint16_t)
>  #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \
>      DEFINE_PROP(_n, _s, _f, qdev_prop_pci_host_devaddr, PCIHostDeviceAddress)
> +#define DEFINE_PROP_AUDIODEV(_n, _s, _f) \
> +    DEFINE_PROP(_n, _s, _f, qdev_prop_audiodev, QEMUSoundCard)
>
>  #define DEFINE_PROP_END_OF_LIST()               \
>      {}
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 14/25] audio: audiodev= parameters no longer optional when -audiodev present
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 14/25] audio: audiodev= parameters no longer optional when -audiodev present Kővágó, Zoltán
@ 2015-08-20 19:15   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 19:15 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/audio.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/audio/audio.c b/audio/audio.c
> index 10b9871..7468b94 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -69,6 +69,8 @@ const struct mixeng_volume nominal_volume = {
>  #endif
>  };
>
> +static bool legacy_config;
> +
>  #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
>  #error No its not
>  #else
> @@ -1331,8 +1333,13 @@ static AudioState *audio_init(Audiodev *dev)
>      if (dev) {
>          drvname = AudiodevDriver_lookup[dev->kind];
>      } else if (!QTAILQ_EMPTY(&audio_states)) {
> +        if (!legacy_config) {
> +            dolog("Must specify audiodev when using -audiodev\n");
> +            exit(1);
> +        }
>          return QTAILQ_FIRST(&audio_states);
>      } else {
> +        legacy_config = true;
>          audio_handle_legacy_opts();
>          list = qemu_find_opts("audiodev");
>          dev = parse_option(QTAILQ_FIRST(&list->head), &error_abort);
> @@ -1463,7 +1470,11 @@ CaptureVoiceOut *AUD_add_capture(
>      CaptureVoiceOut *cap;
>      struct capture_callback *cb;
>
> -    if (!s) { /* todo */
> +    if (!s) {
> +        if (!legacy_config) {
> +            dolog("Must specify audiodev when using -audiodev\n");

This error message is a bit confusing. I think adding a bit of context
could help, you could pass the card name from the AUD_register_card
call, and use a sentence like: "You must specify an audiodev= for the
device %s".

> +            goto err0;
> +        }
>          s = QTAILQ_FIRST(&audio_states);
>      }
>
> --
> 2.4.5
>
>

looks good otherwise,

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server Kővágó, Zoltán
@ 2015-08-20 19:38   ` Marc-André Lureau
  2015-08-21 12:41     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 19:38 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/paaudio.c | 301 ++++++++++++++++++++++++++++++++------------------------
>  1 file changed, 175 insertions(+), 126 deletions(-)
>
> diff --git a/audio/paaudio.c b/audio/paaudio.c
> index a53aaf6..e3b8207 100644
> --- a/audio/paaudio.c
> +++ b/audio/paaudio.c
> @@ -9,10 +9,21 @@
>  #include "audio_int.h"
>  #include "audio_pt_int.h"
>
> -typedef struct {
> -    Audiodev *dev;
> +typedef struct PAConnection {
> +    char *server;
> +    int refcount;
> +    QTAILQ_ENTRY(PAConnection) list;
> +
>      pa_threaded_mainloop *mainloop;
>      pa_context *context;
> +} PAConnection;
> +
> +static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
> +    QTAILQ_HEAD_INITIALIZER(pa_conns);
> +
> +typedef struct {
> +    Audiodev *dev;
> +    PAConnection *conn;
>  } paaudio;
>
>  typedef struct {
> @@ -43,7 +54,7 @@ typedef struct {
>      int samples;
>  } PAVoiceIn;
>
> -static void qpa_audio_fini(void *opaque);
> +static void qpa_conn_fini(PAConnection *c);
>
>  static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
>  {
> @@ -106,11 +117,11 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
>
>  static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
>  {
> -    paaudio *g = p->g;
> +    PAConnection *c = p->g->conn;
>
> -    pa_threaded_mainloop_lock (g->mainloop);
> +    pa_threaded_mainloop_lock(c->mainloop);
>
> -    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
> +    CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
>
>      while (length > 0) {
>          size_t l;
> @@ -119,11 +130,11 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror
>              int r;
>
>              r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
> -            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
> +            CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
>
>              if (!p->read_data) {
> -                pa_threaded_mainloop_wait (g->mainloop);
> -                CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
> +                pa_threaded_mainloop_wait(c->mainloop);
> +                CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
>              } else {
>                  p->read_index = 0;
>              }
> @@ -146,53 +157,53 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror
>              p->read_length = 0;
>              p->read_index = 0;
>
> -            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
> +            CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
>          }
>      }
>
> -    pa_threaded_mainloop_unlock (g->mainloop);
> +    pa_threaded_mainloop_unlock(c->mainloop);
>      return 0;
>
>  unlock_and_fail:
> -    pa_threaded_mainloop_unlock (g->mainloop);
> +    pa_threaded_mainloop_unlock(c->mainloop);
>      return -1;
>  }
>
>  static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
>  {
> -    paaudio *g = p->g;
> +    PAConnection *c = p->g->conn;
>
> -    pa_threaded_mainloop_lock (g->mainloop);
> +    pa_threaded_mainloop_lock(c->mainloop);
>
> -    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
> +    CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
>
>      while (length > 0) {
>          size_t l;
>          int r;
>
>          while (!(l = pa_stream_writable_size (p->stream))) {
> -            pa_threaded_mainloop_wait (g->mainloop);
> -            CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
> +            pa_threaded_mainloop_wait(c->mainloop);
> +            CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
>          }
>
> -        CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
> +        CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
>
>          if (l > length) {
>              l = length;
>          }
>
>          r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
> -        CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
> +        CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
>
>          data = (const uint8_t *) data + l;
>          length -= l;
>      }
>
> -    pa_threaded_mainloop_unlock (g->mainloop);
> +    pa_threaded_mainloop_unlock(c->mainloop);
>      return 0;
>
>  unlock_and_fail:
> -    pa_threaded_mainloop_unlock (g->mainloop);
> +    pa_threaded_mainloop_unlock(c->mainloop);
>      return -1;
>  }
>
> @@ -430,13 +441,13 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
>
>  static void context_state_cb (pa_context *c, void *userdata)
>  {
> -    paaudio *g = userdata;
> +    PAConnection *conn = userdata;
>
>      switch (pa_context_get_state(c)) {
>      case PA_CONTEXT_READY:
>      case PA_CONTEXT_TERMINATED:
>      case PA_CONTEXT_FAILED:
> -        pa_threaded_mainloop_signal (g->mainloop, 0);
> +        pa_threaded_mainloop_signal(conn->mainloop, 0);
>          break;
>
>      case PA_CONTEXT_UNCONNECTED:
> @@ -449,14 +460,14 @@ static void context_state_cb (pa_context *c, void *userdata)
>
>  static void stream_state_cb (pa_stream *s, void * userdata)
>  {
> -    paaudio *g = userdata;
> +    PAConnection *c = userdata;
>
>      switch (pa_stream_get_state (s)) {
>
>      case PA_STREAM_READY:
>      case PA_STREAM_FAILED:
>      case PA_STREAM_TERMINATED:
> -        pa_threaded_mainloop_signal (g->mainloop, 0);
> +        pa_threaded_mainloop_signal(c->mainloop, 0);
>          break;
>
>      case PA_STREAM_UNCONNECTED:
> @@ -467,13 +478,13 @@ static void stream_state_cb (pa_stream *s, void * userdata)
>
>  static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
>  {
> -    paaudio *g = userdata;
> +    PAConnection *c = userdata;
>
> -    pa_threaded_mainloop_signal (g->mainloop, 0);
> +    pa_threaded_mainloop_signal(c->mainloop, 0);
>  }
>
>  static pa_stream *qpa_simple_new (
> -        paaudio *g,
> +        PAConnection *c,
>          const char *name,
>          pa_stream_direction_t dir,
>          const char *dev,
> @@ -484,50 +495,52 @@ static pa_stream *qpa_simple_new (
>  {
>      int r;
>      pa_stream *stream;
> +    pa_stream_flags_t flags;
>
> -    pa_threaded_mainloop_lock (g->mainloop);
> +    pa_threaded_mainloop_lock(c->mainloop);
>
> -    stream = pa_stream_new (g->context, name, ss, map);
> +    stream = pa_stream_new(c->context, name, ss, map);
>      if (!stream) {
>          goto fail;
>      }
>
> -    pa_stream_set_state_callback (stream, stream_state_cb, g);
> -    pa_stream_set_read_callback (stream, stream_request_cb, g);
> -    pa_stream_set_write_callback (stream, stream_request_cb, g);
> +    pa_stream_set_state_callback (stream, stream_state_cb, c);
> +    pa_stream_set_read_callback (stream, stream_request_cb, c);
> +    pa_stream_set_write_callback (stream, stream_request_cb, c);
> +
> +    flags =
> +        PA_STREAM_INTERPOLATE_TIMING
> +#ifdef PA_STREAM_ADJUST_LATENCY
> +        |PA_STREAM_ADJUST_LATENCY
> +#endif
> +        |PA_STREAM_AUTO_TIMING_UPDATE;
> +    if (dev) {
> +        /* don't move the stream if the user specified a sink/source */
> +        flags |= PA_STREAM_DONT_MOVE;

This is unrelated, and I don't think it's justified, imho user should
be allowed to move the stream later if needed.

> +    }
>
>      if (dir == PA_STREAM_PLAYBACK) {
> -        r = pa_stream_connect_playback (stream, dev, attr,
> -                                        PA_STREAM_INTERPOLATE_TIMING
> -#ifdef PA_STREAM_ADJUST_LATENCY
> -                                        |PA_STREAM_ADJUST_LATENCY
> -#endif
> -                                        |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
> +        r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
>      } else {
> -        r = pa_stream_connect_record (stream, dev, attr,
> -                                      PA_STREAM_INTERPOLATE_TIMING
> -#ifdef PA_STREAM_ADJUST_LATENCY
> -                                      |PA_STREAM_ADJUST_LATENCY
> -#endif
> -                                      |PA_STREAM_AUTO_TIMING_UPDATE);
> +        r = pa_stream_connect_record(stream, dev, attr, flags);
>      }
>
>      if (r < 0) {
>        goto fail;
>      }
>
> -    pa_threaded_mainloop_unlock (g->mainloop);
> +    pa_threaded_mainloop_unlock(c->mainloop);
>
>      return stream;
>
>  fail:
> -    pa_threaded_mainloop_unlock (g->mainloop);
> +    pa_threaded_mainloop_unlock(c->mainloop);
>
>      if (stream) {
>          pa_stream_unref (stream);
>      }
>
> -    *rerror = pa_context_errno (g->context);
> +    *rerror = pa_context_errno(c->context);
>
>      return NULL;
>  }
> @@ -543,6 +556,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
>      paaudio *g = pa->g = drv_opaque;
>      AudiodevPaOptions *popts = g->dev->pa;
>      AudiodevPaPerDirectionOptions *ppdo = popts->sink;
> +    PAConnection *c = g->conn;
>
>      ss.format = audfmt_to_pa (as->fmt, as->endianness);
>      ss.channels = as->nchannels;
> @@ -560,7 +574,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
>      obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
>
>      pa->stream = qpa_simple_new (
> -        g,
> +        c,
>          "qemu",
>          PA_STREAM_PLAYBACK,
>          ppdo->has_name ? ppdo->name : NULL,
> @@ -612,6 +626,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      paaudio *g = pa->g = drv_opaque;
>      AudiodevPaOptions *popts = g->dev->pa;
>      AudiodevPaPerDirectionOptions *ppdo = popts->source;
> +    PAConnection *c = g->conn;
>
>      ss.format = audfmt_to_pa (as->fmt, as->endianness);
>      ss.channels = as->nchannels;
> @@ -620,7 +635,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
>
>      pa->stream = qpa_simple_new (
> -        g,
> +        c,
>          "qemu",
>          PA_STREAM_RECORD,
>          ppdo->has_name ? ppdo->name : NULL,
> @@ -708,7 +723,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
>      PAVoiceOut *pa = (PAVoiceOut *) hw;
>      pa_operation *op;
>      pa_cvolume v;
> -    paaudio *g = pa->g;
> +    PAConnection *c = pa->g->conn;
>
>  #ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */
>      pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
> @@ -728,28 +743,28 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
>              v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
>              v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
>
> -            pa_threaded_mainloop_lock (g->mainloop);
> +            pa_threaded_mainloop_lock(c->mainloop);
>
> -            op = pa_context_set_sink_input_volume (g->context,
> +            op = pa_context_set_sink_input_volume(c->context,
>                  pa_stream_get_index (pa->stream),
>                  &v, NULL, NULL);
>              if (!op)
> -                qpa_logerr (pa_context_errno (g->context),
> +                qpa_logerr (pa_context_errno(c->context),
>                              "set_sink_input_volume() failed\n");
>              else
>                  pa_operation_unref (op);
>
> -            op = pa_context_set_sink_input_mute (g->context,
> +            op = pa_context_set_sink_input_mute(c->context,
>                  pa_stream_get_index (pa->stream),
>                 sw->vol.mute, NULL, NULL);
>              if (!op) {
> -                qpa_logerr (pa_context_errno (g->context),
> +                qpa_logerr (pa_context_errno(c->context),
>                              "set_sink_input_mute() failed\n");
>              } else {
>                  pa_operation_unref (op);
>              }
>
> -            pa_threaded_mainloop_unlock (g->mainloop);
> +            pa_threaded_mainloop_unlock(c->mainloop);
>          }
>      }
>      return 0;
> @@ -760,7 +775,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
>      PAVoiceIn *pa = (PAVoiceIn *) hw;
>      pa_operation *op;
>      pa_cvolume v;
> -    paaudio *g = pa->g;
> +    PAConnection *c = pa->g->conn;
>
>  #ifdef PA_CHECK_VERSION
>      pa_cvolume_init (&v);
> @@ -780,123 +795,157 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
>              v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
>              v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
>
> -            pa_threaded_mainloop_lock (g->mainloop);
> +            pa_threaded_mainloop_lock(c->mainloop);
>
>              /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
> -            op = pa_context_set_source_volume_by_index (g->context,
> +            op = pa_context_set_source_volume_by_index(c->context,
>                  pa_stream_get_device_index (pa->stream),
>                  &v, NULL, NULL);
>              if (!op) {
> -                qpa_logerr (pa_context_errno (g->context),
> +                qpa_logerr (pa_context_errno(c->context),
>                              "set_source_volume() failed\n");
>              } else {
>                  pa_operation_unref(op);
>              }
>
> -            op = pa_context_set_source_mute_by_index (g->context,
> +            op = pa_context_set_source_mute_by_index(c->context,
>                  pa_stream_get_index (pa->stream),
>                  sw->vol.mute, NULL, NULL);
>              if (!op) {
> -                qpa_logerr (pa_context_errno (g->context),
> +                qpa_logerr (pa_context_errno(c->context),
>                              "set_source_mute() failed\n");
>              } else {
>                  pa_operation_unref (op);
>              }
>
> -            pa_threaded_mainloop_unlock (g->mainloop);
> +            pa_threaded_mainloop_unlock(c->mainloop);
>          }
>      }
>      return 0;
>  }
>
>  /* common */
> +static void *qpa_conn_init(const char *server)
> +{
> +    PAConnection *c = g_malloc0(sizeof(PAConnection));
> +    QTAILQ_INSERT_TAIL(&pa_conns, c, list);
> +
> +    c->mainloop = pa_threaded_mainloop_new();
> +    if (!c->mainloop) {
> +        goto fail;
> +    }
> +
> +    c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
> +                                server);
> +    if (!c->context) {
> +        goto fail;
> +    }
> +
> +    pa_context_set_state_callback(c->context, context_state_cb, c);
> +
> +    if (pa_context_connect(c->context, server, 0, NULL) < 0) {
> +        qpa_logerr(pa_context_errno(c->context),
> +                   "pa_context_connect() failed\n");
> +        goto fail;
> +    }
> +
> +    pa_threaded_mainloop_lock(c->mainloop);
> +
> +    if (pa_threaded_mainloop_start(c->mainloop) < 0) {
> +        goto unlock_and_fail;
> +    }
> +
> +    for (;;) {
> +        pa_context_state_t state;
> +
> +        state = pa_context_get_state(c->context);
> +
> +        if (state == PA_CONTEXT_READY) {
> +            break;
> +        }
> +
> +        if (!PA_CONTEXT_IS_GOOD (state)) {
> +            qpa_logerr(pa_context_errno(c->context),
> +                       "Wrong context state\n");
> +            goto unlock_and_fail;
> +        }
> +
> +        /* Wait until the context is ready */
> +        pa_threaded_mainloop_wait(c->mainloop);
> +    }
> +
> +    pa_threaded_mainloop_unlock(c->mainloop);
> +    return c;
> +
> +unlock_and_fail:
> +    pa_threaded_mainloop_unlock(c->mainloop);
> +fail:
> +    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
> +    qpa_conn_fini(c);
> +    return NULL;
> +}
> +
>  static void *qpa_audio_init(Audiodev *dev)
>  {
>      paaudio *g;
>      AudiodevPaOptions *popts;
>      const char *server;
> +    PAConnection *c;
>
>      assert(dev->kind == AUDIODEV_DRIVER_PA);
>
> -    g = g_malloc(sizeof(paaudio));
> +    g = g_malloc0(sizeof(paaudio));
>      popts = dev->pa;
>      server = popts->has_server ? popts->server : NULL;
>
>      g->dev = dev;
> -    g->mainloop = NULL;
> -    g->context = NULL;
>
> -    g->mainloop = pa_threaded_mainloop_new ();
> -    if (!g->mainloop) {
> -        goto fail;
> -    }
> -
> -    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
> -                                 server);
> -    if (!g->context) {
> -        goto fail;
> -    }
> -
> -    pa_context_set_state_callback (g->context, context_state_cb, g);
> -
> -    if (pa_context_connect (g->context, server, 0, NULL) < 0) {
> -        qpa_logerr (pa_context_errno (g->context),
> -                    "pa_context_connect() failed\n");
> -        goto fail;
> -    }
> -
> -    pa_threaded_mainloop_lock (g->mainloop);
> -
> -    if (pa_threaded_mainloop_start (g->mainloop) < 0) {
> -        goto unlock_and_fail;
> -    }
> -
> -    for (;;) {
> -        pa_context_state_t state;
> -
> -        state = pa_context_get_state (g->context);
> -
> -        if (state == PA_CONTEXT_READY) {
> +    QTAILQ_FOREACH(c, &pa_conns, list) {
> +        if (server == NULL || c->server == NULL ?
> +            server == c->server :
> +            strcmp(server, c->server) == 0) {
> +            g->conn = c;
>              break;
>          }
> -
> -        if (!PA_CONTEXT_IS_GOOD (state)) {
> -            qpa_logerr (pa_context_errno (g->context),
> -                        "Wrong context state\n");
> -            goto unlock_and_fail;
> -        }
> -
> -        /* Wait until the context is ready */
> -        pa_threaded_mainloop_wait (g->mainloop);
> +    }
> +    if (!g->conn) {
> +        g->conn = qpa_conn_init(server);
> +    }
> +    if (!g->conn) {
> +        g_free(g);
> +        return NULL;
>      }
>
> -    pa_threaded_mainloop_unlock (g->mainloop);
> -
> +    ++g->conn->refcount;
>      return g;
> +}
>
> -unlock_and_fail:
> -    pa_threaded_mainloop_unlock (g->mainloop);
> -fail:
> -    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
> -    qpa_audio_fini(g);
> -    return NULL;
> +static void qpa_conn_fini(PAConnection *c)
> +{
> +    if (c->mainloop) {
> +        pa_threaded_mainloop_stop(c->mainloop);
> +    }
> +
> +    if (c->context) {
> +        pa_context_disconnect(c->context);
> +        pa_context_unref(c->context);
> +    }
> +
> +    if (c->mainloop) {
> +        pa_threaded_mainloop_free(c->mainloop);
> +    }
> +
> +    QTAILQ_REMOVE(&pa_conns, c, list);
> +    g_free(c);
>  }
>
>  static void qpa_audio_fini (void *opaque)
>  {
>      paaudio *g = opaque;
> +    PAConnection *c = g->conn;
>
> -    if (g->mainloop) {
> -        pa_threaded_mainloop_stop (g->mainloop);
> -    }
> -
> -    if (g->context) {
> -        pa_context_disconnect (g->context);
> -        pa_context_unref (g->context);
> -    }
> -
> -    if (g->mainloop) {
> -        pa_threaded_mainloop_free (g->mainloop);
> +    if (--c->refcount == 0) {
> +        qpa_conn_fini(c);
>      }
>
>      g_free(g);
> --
> 2.4.5
>
>

otherwise

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 16/25] paaudio: properly disconnect streams in fini_*
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 16/25] paaudio: properly disconnect streams in fini_* Kővágó, Zoltán
@ 2015-08-20 20:29   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 20:29 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Currently this needs a workaround due to bug #74624 in pulseaudio.
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/paaudio.c | 23 +++++++++++++++++++++--
>  1 file changed, 21 insertions(+), 2 deletions(-)
>
> diff --git a/audio/paaudio.c b/audio/paaudio.c
> index e3b8207..2aee22f 100644
> --- a/audio/paaudio.c
> +++ b/audio/paaudio.c
> @@ -678,6 +678,25 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>      return -1;
>  }
>
> +static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
> +{
> +    int err;
> +
> +    pa_threaded_mainloop_lock(c->mainloop);
> +    /* wait until actually connects. workaround pa bug #74624
> +     * https://bugs.freedesktop.org/show_bug.cgi?id=74624 */
> +    while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
> +        pa_threaded_mainloop_wait(c->mainloop);
> +    }
> +
> +    err = pa_stream_disconnect(stream);
> +    if (err != 0) {
> +        dolog("Failed to dissconnect! err=%d\n", err);
> +    }
> +    pa_stream_unref(stream);
> +    pa_threaded_mainloop_unlock(c->mainloop);
> +}
> +
>  static void qpa_fini_out (HWVoiceOut *hw)
>  {
>      void *ret;
> @@ -689,7 +708,7 @@ static void qpa_fini_out (HWVoiceOut *hw)
>      audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
>
>      if (pa->stream) {
> -        pa_stream_unref (pa->stream);
> +        qpa_simple_disconnect(pa->g->conn, pa->stream);
>          pa->stream = NULL;
>      }
>
> @@ -709,7 +728,7 @@ static void qpa_fini_in (HWVoiceIn *hw)
>      audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
>
>      if (pa->stream) {
> -        pa_stream_unref (pa->stream);
> +        qpa_simple_disconnect(pa->g->conn, pa->stream);
>          pa->stream = NULL;
>      }
>
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX Kővágó, Zoltán
  2015-08-19 18:17   ` Gerd Hoffmann
@ 2015-08-20 20:36   ` Marc-André Lureau
  1 sibling, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 20:36 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Currently the gcc specific version only evaluates the arguments once,
> while the generic version evaluates one argument twice, which can cause
> debugging headaches when an argument has a side effect.  This patch at
> least provides consistent behavior between compilers.
>

Going this way, you could simply replace audio_MIN/MAX with MIN/MAX
(from osdep.h and glib headers)

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/audio.h | 14 --------------
>  1 file changed, 14 deletions(-)
>
> diff --git a/audio/audio.h b/audio/audio.h
> index 68545b6..3a54e17 100644
> --- a/audio/audio.h
> +++ b/audio/audio.h
> @@ -150,22 +150,8 @@ static inline void *advance (void *p, int incr)
>      return (d + incr);
>  }
>
> -#ifdef __GNUC__
> -#define audio_MIN(a, b) ( __extension__ ({      \
> -    __typeof (a) ta = a;                        \
> -    __typeof (b) tb = b;                        \
> -    ((ta)>(tb)?(tb):(ta));                      \
> -}))
> -
> -#define audio_MAX(a, b) ( __extension__ ({      \
> -    __typeof (a) ta = a;                        \
> -    __typeof (b) tb = b;                        \
> -    ((ta)<(tb)?(tb):(ta));                      \
> -}))
> -#else
>  #define audio_MIN(a, b) ((a)>(b)?(b):(a))
>  #define audio_MAX(a, b) ((a)<(b)?(b):(a))
> -#endif
>
>  int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
>                        int freq, int bits, int nchannels);
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run Kővágó, Zoltán
@ 2015-08-20 20:49   ` Marc-André Lureau
  2015-08-21 12:59     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 20:49 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> audio_run is called manually by alsa and oss backends when polling.
> In this case only the requesting backend should be run, not all of them.
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/alsaaudio.c |  7 +++++--
>  audio/audio.c     | 17 +++++++----------
>  audio/audio_int.h |  2 +-
>  audio/ossaudio.c  | 12 ++++++------
>  4 files changed, 19 insertions(+), 19 deletions(-)
>
> diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
> index cfe4aec..0750d0d 100644
> --- a/audio/alsaaudio.c
> +++ b/audio/alsaaudio.c
> @@ -40,6 +40,7 @@ struct pollhlp {
>      struct pollfd *pfds;
>      int count;
>      int mask;
> +    AudioState *s;

Instead of adding that pointer, you could lookup the hw->s using container_of()

>  };
>
>  typedef struct ALSAVoiceOut {
> @@ -200,11 +201,11 @@ static void alsa_poll_handler (void *opaque)
>          break;
>
>      case SND_PCM_STATE_PREPARED:
> -        audio_run ("alsa run (prepared)");
> +        audio_run(hlp->s, "alsa run (prepared)");
>          break;
>
>      case SND_PCM_STATE_RUNNING:
> -        audio_run ("alsa run (running)");
> +        audio_run (hlp->s, "alsa run (running)");
>          break;
>
>      default:
> @@ -758,6 +759,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
>          return -1;
>      }
>
> +    alsa->pollhlp.s = hw->s;
>      alsa->handle = handle;
>      alsa->dev = dev;
>      return 0;
> @@ -859,6 +861,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>          return -1;
>      }
>
> +    alsa->pollhlp.s = hw->s;
>      alsa->handle = handle;
>      alsa->dev = dev;
>      return 0;
> diff --git a/audio/audio.c b/audio/audio.c
> index 7468b94..e213deb 100644
> --- a/audio/audio.c
> +++ b/audio/audio.c
> @@ -790,8 +790,9 @@ static void audio_reset_timer (AudioState *s)
>
>  static void audio_timer (void *opaque)
>  {
> -    audio_run ("timer");
> -    audio_reset_timer (opaque);
> +    AudioState *s = opaque;
> +    audio_run(s, "timer");
> +    audio_reset_timer(s);
>  }
>
>  /*
> @@ -1194,15 +1195,11 @@ static void audio_run_capture (AudioState *s)
>      }
>  }
>
> -void audio_run (const char *msg)
> +void audio_run(AudioState *s, const char *msg)
>  {
> -    AudioState *s;
> -
> -    QTAILQ_FOREACH(s, &audio_states, list) {
> -        audio_run_out (s);
> -        audio_run_in (s);
> -        audio_run_capture (s);
> -    }
> +    audio_run_out(s);
> +    audio_run_in(s);
> +    audio_run_capture(s);
>
>  #ifdef DEBUG_POLL
>      {
> diff --git a/audio/audio_int.h b/audio/audio_int.h
> index 1d81658..c52c656 100644
> --- a/audio/audio_int.h
> +++ b/audio/audio_int.h
> @@ -220,7 +220,7 @@ int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
>  int audio_bug (const char *funcname, int cond);
>  void *audio_calloc (const char *funcname, int nmemb, size_t size);
>
> -void audio_run (const char *msg);
> +void audio_run(AudioState *s, const char *msg);
>
>  #define VOICE_ENABLE 1
>  #define VOICE_DISABLE 2
> diff --git a/audio/ossaudio.c b/audio/ossaudio.c
> index a5e7f7c..9eae769 100644
> --- a/audio/ossaudio.c
> +++ b/audio/ossaudio.c
> @@ -112,28 +112,28 @@ static void oss_anal_close (int *fdp)
>
>  static void oss_helper_poll_out (void *opaque)
>  {
> -    (void) opaque;
> -    audio_run ("oss_poll_out");
> +    AudioState *s = opaque;
> +    audio_run(s, "oss_poll_out");
>  }
>
>  static void oss_helper_poll_in (void *opaque)
>  {
> -    (void) opaque;
> -    audio_run ("oss_poll_in");
> +    AudioState *s = opaque;
> +    audio_run(s, "oss_poll_in");
>  }
>
>  static void oss_poll_out (HWVoiceOut *hw)
>  {
>      OSSVoiceOut *oss = (OSSVoiceOut *) hw;
>
> -    qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
> +    qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
>  }
>
>  static void oss_poll_in (HWVoiceIn *hw)
>  {
>      OSSVoiceIn *oss = (OSSVoiceIn *) hw;
>
> -    qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
> +    qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
>  }
>
>  static int oss_write (SWVoiceOut *sw, void *buf, int len)
> --
> 2.4.5
>
>

other than that,
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor
  2015-08-20 15:55   ` Marc-André Lureau
@ 2015-08-20 21:21     ` Kővágó Zoltán
  0 siblings, 0 replies; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-20 21:21 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: Markus Armbruster, Gerd Hoffmann, QEMU, Michael Roth

Hi,

2015-08-20 17:55 keltezéssel, Marc-André Lureau írta:
[snip]
>> +static void
>> +sum_strlen(gpointer data, gpointer user_data)
>> +{
>> +    const char *str = data;
>> +    size_t *sum_len = user_data;
>> +
>> +    if (str) { /* skip NULLs */
>> +        *sum_len += strlen(str) + 1;
>> +    }
>> +}
>> +
>> +static void
>> +append_str(gpointer data, gpointer user_data)
>> +{
>> +    const char *str = data;
>> +    char *concat_str = user_data;
>> +
>> +    if (str) {
>> +        strcat(concat_str, str);
>> +        strcat(concat_str, ".");
>> +    }
>> +}
>> +
>> +/* lookup a name, using a fully qualified version */
>>   static GQueue *
>> -lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
>> +lookup_distinct(const OptsVisitor *ov, const char *name, char **out_key,
>> +                Error **errp)
>>   {
>> -    GQueue *list;
>> +    GQueue *list = NULL;
>> +    char *key;
>> +    size_t sum_len = strlen(name);
>> +
>> +    g_queue_foreach(ov->nested_names, sum_strlen, &sum_len);
>> +    key = g_malloc(sum_len+1);
>> +    key[0] = 0;
>> +    g_queue_foreach(ov->nested_names, append_str, key);
>> +    strcat(key, name);
>
> Instead of using a GQueue, I think you could use a GArray, and use
> g_strjoin() here.

Unfortunately g_strjoinv won't skip NULL pointers in the array (as it 
expects a NULL terminated array), but we will get structs with NULL name 
(the top level struct will be such one).  And one can't simply say if 
(name) { g_array_append_val(...); } in opts_start_struct because we need 
to know somehow in opts_end_struct if we need to pop or not.

One way is to use NULL pointers because in that case we must pop, but 
g_strjoinv won't work.  An alternate is to only push non-NULL elements 
into the array and have another bool array where we store if we need to 
pop or not.  Not sure which one is cleaner.

Or maybe if it's guaranteed that only the top-level struct will have a 
NULL name, we can hack around by only pushing non-NULL elements and 
ignore empty array when popping.

Zoltan

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches Kővágó, Zoltán
@ 2015-08-20 22:08   ` Marc-André Lureau
  2015-08-21 13:18     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 22:08 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> Pulseaudio normally assumes that when the server wants it, the client
> can generate the audio samples and send it right away.  Unfortunately
> this is not the case with QEMU -- it's up to the emulated system when
> does it generate the samples.  Buffering the samples and sending them
> from a background thread is just a workaround, that doesn't work too
> well.  Instead enable pa's compatibility support and let pa worry about
> the details.

Afaik, it's not a problem if the audio data is not available right
away (live sources): PulseAudio should deal with that. I think it will
simply silence and wait (but it could actually be smarter than that?)

PA_STREAM_EARLY_REQUESTS is going to make PA request data more
frequently (every minreq). I don't get why that would help here. It's
meant for bad behaving applications using usleep and such that aren't
fixable, not a solution for audio glitches.

If qemu produces too much data than what PA can consume, it should
drop the old data, and only play current audio (unfortunately, that
doesn't seem to be the case looking at audio_pcm_sw_write). If qemu
produces too little, it can still write whatever it has when it has
it.

Could you provide a way to reproduce the glitch you experience or
explain more clearly how this flag helps?


thanks

>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  audio/paaudio.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
>
> diff --git a/audio/paaudio.c b/audio/paaudio.c
> index 2aee22f..3990a80 100644
> --- a/audio/paaudio.c
> +++ b/audio/paaudio.c
> @@ -510,10 +510,8 @@ static pa_stream *qpa_simple_new (
>
>      flags =
>          PA_STREAM_INTERPOLATE_TIMING
> -#ifdef PA_STREAM_ADJUST_LATENCY
> -        |PA_STREAM_ADJUST_LATENCY
> -#endif
> -        |PA_STREAM_AUTO_TIMING_UPDATE;
> +        |PA_STREAM_AUTO_TIMING_UPDATE
> +        |PA_STREAM_EARLY_REQUESTS;
>      if (dev) {
>          /* don't move the stream if the user specified a sink/source */
>          flags |= PA_STREAM_DONT_MOVE;
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends
  2015-08-19 19:07   ` Gerd Hoffmann
@ 2015-08-20 22:28     ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 22:28 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: QEMU, Kővágó, Zoltán

Hi

This commit fails to compile with errors such as:

audio/audio.c: In function ‘audio_pcm_hw_run_in’:
audio/audio.c:1143:41: error: ‘struct audio_pcm_info’ has no member
named ‘bytes_per_frame’
         size_t size = samples * hw->info.bytes_per_frame;

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs Kővágó, Zoltán
@ 2015-08-20 22:37   ` Marc-André Lureau
  2015-08-21 13:34     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 22:37 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
>
> -    decr = rate_get_samples(&hw->info, &out->rate);
> -    decr = audio_MIN(out->fsize - out->fpos, decr);
> -
> -    *size = decr << 2;
> +    *size = audio_rate_get_bytes(&hw->info, &out->rate,
> +                                 (out->fsize - out->fpos) << 2);
>      return out->frame + out->fpos;
>  }

I am not familiar with this "rate" thing, but you are changing the way
it works for Spice without explaining why, and that worries me a
little.

The rate->bytes_sent is not updated the same way, the result is not
always << 2..

I really don't get what this is really doing tbh, some kind of clever
buffer limit :)



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation)
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation) Kővágó, Zoltán
@ 2015-08-20 22:49   ` Marc-André Lureau
  2015-08-21 13:46     ` Kővágó Zoltán
  0 siblings, 1 reply; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 22:49 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: Markus Armbruster, QEMU, Gerd Hoffmann

Hi

On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
> This will allow us to disable mixeng when we use a decent backend.
>
> Disabling mixeng have a few advantages:
> * we no longer convert the audio output from one format to another, when
>   the underlying audio system would just convert it to a third format.
>   We no longer convert, only the underlying system, when needed.
> * the underlying system probably has better resampling and sample format
>   converting methods anyway...
> * we may support formats that the mixeng currently does not support (S24
>   or float samples, more than two channels)
> * when using an audio server (like pulseaudio) different sound card
>   outputs will show up as separate streams, even if we use only one
>   backend
>
> Disadvantages:
> * audio capturing no longer works (wavcapture, and vnc audio extension)
> * some backends only support a single playback stream or very picky
>   about the audio format.  In this case we can't disable mixeng.
>

Since the setting is so complicated, shouldn't it be a OnOffAuto? and
let the backend decide what's best for the user?

> However mixeng is not removed, only made optional, so this shouldn't be
> a big concern.
>
> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
> ---
>  qapi/audio.json | 5 +++++
>  qemu-options.hx | 6 ++++++
>  2 files changed, 11 insertions(+)
>
> diff --git a/qapi/audio.json b/qapi/audio.json
> index b57b215..0216a10 100644
> --- a/qapi/audio.json
> +++ b/qapi/audio.json
> @@ -184,6 +184,10 @@
>  #
>  # General audio backend options that are used for both playback and recording.
>  #
> +# @mixeng: #optional use QEMU's mixing engine to mix all streams inside QEMU.
> +#          When set to off, fixed-settings must be also off.  Not every backend
> +#          compatible with the off setting (default on)
> +#
>  # @fixed-settings: #optional use fixed settings for host input/output.  When
>  #                  off, frequency, channels and format must not be specified
>  #                  (default on)
> @@ -207,6 +211,7 @@
>  ##
>  { 'struct': 'AudiodevPerDirectionOptions',
>    'data': {
> +    '*mixeng':         'bool',
>      '*fixed-settings': 'bool',
>      '*frequency':      'int',
>      '*channels':       'int',
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 60a3563..bd922bf 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -329,6 +329,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
>      "                specifies the audio backend to use\n"
>      "                id= identifier of the backend\n"
>      "                timer-period= timer period in microseconds\n"
> +    "                in|out.mixeng= use mixeng to mix streams inside QEMU\n"
>      "                in|out.fixed-settings= use fixed settings for host audio\n"
>      "                in|out.frequency= frequency to use with fixed settings\n"
>      "                in|out.channels= number of channels to use with fixed settings\n"
> @@ -397,6 +398,11 @@ Identifies the audio backend.
>  Sets the timer @var{period} used by the audio subsystem in microseconds.
>  Default is 10000 (10 ms).
>
> +@item in|out.mixeng=on|off
> +Use QEMU's mixing engine to mix all streams inside QEMU.  When off,
> +@var{fixed-settings} must be off too.  Not every backend is fully
> +compatible with the off setting.  Default is on.
> +
>  @item in|out.fixed-settings=on|off
>  Use fixed settings for host audio.  When off, it will change based on
>  how the guest opens the sound card.  In this case you must not specify
> --
> 2.4.5
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 23/25] audio: make mixeng optional
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 23/25] audio: make mixeng optional Kővágó, Zoltán
@ 2015-08-20 22:56   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 22:56 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

Fails to compile:

audio/audio.c: In function ‘audio_run_out’:
audio/audio.c:1058:32: error: ‘struct audio_pcm_ops’ has no member
named ‘enable_out’
                 if (hw->pcm_ops->enable_out) {

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 25/25] audio: split ctl_* functions into enable_* and volume_*
  2015-08-06 18:28 ` [Qemu-devel] [PATCH 25/25] audio: split ctl_* functions into enable_* and volume_* Kővágó, Zoltán
@ 2015-08-20 23:05   ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-20 23:05 UTC (permalink / raw)
  To: Kővágó, Zoltán; +Cc: QEMU, Gerd Hoffmann

nice! :)

Looks good, I'll check more thoroughly when you will have an updated series.

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio
  2015-08-20 18:01   ` Marc-André Lureau
@ 2015-08-21 12:23     ` Kővágó Zoltán
  2015-08-21 14:24       ` Marc-André Lureau
  0 siblings, 1 reply; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-21 12:23 UTC (permalink / raw)
  To: Marc-André Lureau
  Cc: Paolo Bonzini, Luiz Capitulino, Gerd Hoffmann, QEMU, Markus Armbruster

Hi

2015-08-20 20:01 keltezéssel, Marc-André Lureau írta:
[snip]
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index efd57e6..60a3563 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -1538,6 +1538,11 @@ everybody else.  'ignore' completely ignores the shared flag and
>>   allows everybody connect unconditionally.  Doesn't conform to the rfb
>>   spec but is traditional QEMU behavior.
>>
>> +@item audiodev=@var{audiodev}
>> +
>> +Use the specified @var{audiodev} when the VNC client requests audio
>> +transmission.
>
> That doesn't make much sense to me, vnc (or spice) should be connected
> to the guest device, not to the audiodev. Same for for the wavcapture.

Hmm.  Maybe it would make more sense, but it's not how the audio system 
works.  The situation cyrrently (before my patch) is the following: 
VNC/spice/wavcapture attaches a capture to the global AudioState and 
starts recording/transmitting the mixed together audio of all connected 
guest sound cards.

Now with more audiodevs, you have to specify which one you wan't to 
capture from.  If there's multiple sound cards attached to a single 
audiodev (AudioState), then we will transmit the mix of them.  (If 
there's only one attached though, then we will only transmit that...)

To support capture from a single guest device would require some 
rewriting of the audio capture logic, in a backward compatible way 
(which would mean support both capturing form a single card and 
capturing a mix of all cards.  And in the latter case we still need some 
way to specify audiodev...)

Zoltan

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server
  2015-08-20 19:38   ` Marc-André Lureau
@ 2015-08-21 12:41     ` Kővágó Zoltán
  2015-08-21 14:36       ` Marc-André Lureau
  0 siblings, 1 reply; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-21 12:41 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann

Hi,

2015-08-20 21:38 keltezéssel, Marc-André Lureau írta:
[snip]
>> +    flags =
>> +        PA_STREAM_INTERPOLATE_TIMING
>> +#ifdef PA_STREAM_ADJUST_LATENCY
>> +        |PA_STREAM_ADJUST_LATENCY
>> +#endif
>> +        |PA_STREAM_AUTO_TIMING_UPDATE;
>> +    if (dev) {
>> +        /* don't move the stream if the user specified a sink/source */
>> +        flags |= PA_STREAM_DONT_MOVE;
>
> This is unrelated, and I don't think it's justified, imho user should
> be allowed to move the stream later if needed.

True, I will remove it from this commit.  But not sure if I want to 
remove it completely, because it looks like pulseaudio has a tendency to 
automatically move the stream on connect, even if you explicitly specify 
a sink/source when connecting.  (And the problem is that all streams 
appear under the name qemu in pulseaudio, so once the user moves one 
stream, pulseaudio will assume all streams subsequently created by qemu 
are the same on, and move them too.  Maybe we shold add some extra 
options to the pa backend?)

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run
  2015-08-20 20:49   ` Marc-André Lureau
@ 2015-08-21 12:59     ` Kővágó Zoltán
  0 siblings, 0 replies; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-21 12:59 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann

Hi

2015-08-20 22:49 keltezéssel, Marc-André Lureau írta:
> Hi
>
> On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
>> audio_run is called manually by alsa and oss backends when polling.
>> In this case only the requesting backend should be run, not all of them.
>>
>> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
>> ---
>>   audio/alsaaudio.c |  7 +++++--
>>   audio/audio.c     | 17 +++++++----------
>>   audio/audio_int.h |  2 +-
>>   audio/ossaudio.c  | 12 ++++++------
>>   4 files changed, 19 insertions(+), 19 deletions(-)
>>
>> diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
>> index cfe4aec..0750d0d 100644
>> --- a/audio/alsaaudio.c
>> +++ b/audio/alsaaudio.c
>> @@ -40,6 +40,7 @@ struct pollhlp {
>>       struct pollfd *pfds;
>>       int count;
>>       int mask;
>> +    AudioState *s;
>
> Instead of adding that pointer, you could lookup the hw->s using container_of()

I'm afraid that won't work.  The pollhlp is used both inside ALSAVoiceIn 
and ALSAVoiceOut, but they have a common callback.

>
>>   };
>>
>>   typedef struct ALSAVoiceOut {
>> @@ -200,11 +201,11 @@ static void alsa_poll_handler (void *opaque)
>>           break;
>>
>>       case SND_PCM_STATE_PREPARED:
>> -        audio_run ("alsa run (prepared)");
>> +        audio_run(hlp->s, "alsa run (prepared)");
>>           break;
>>
>>       case SND_PCM_STATE_RUNNING:
>> -        audio_run ("alsa run (running)");
>> +        audio_run (hlp->s, "alsa run (running)");
>>           break;
>>
>>       default:
>> @@ -758,6 +759,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
>>           return -1;
>>       }
>>
>> +    alsa->pollhlp.s = hw->s;
>>       alsa->handle = handle;
>>       alsa->dev = dev;
>>       return 0;
>> @@ -859,6 +861,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
>>           return -1;
>>       }
>>
>> +    alsa->pollhlp.s = hw->s;
>>       alsa->handle = handle;
>>       alsa->dev = dev;
>>       return 0;
>> diff --git a/audio/audio.c b/audio/audio.c
>> index 7468b94..e213deb 100644
>> --- a/audio/audio.c
>> +++ b/audio/audio.c
>> @@ -790,8 +790,9 @@ static void audio_reset_timer (AudioState *s)
>>
>>   static void audio_timer (void *opaque)
>>   {
>> -    audio_run ("timer");
>> -    audio_reset_timer (opaque);
>> +    AudioState *s = opaque;
>> +    audio_run(s, "timer");
>> +    audio_reset_timer(s);
>>   }
>>
>>   /*
>> @@ -1194,15 +1195,11 @@ static void audio_run_capture (AudioState *s)
>>       }
>>   }
>>
>> -void audio_run (const char *msg)
>> +void audio_run(AudioState *s, const char *msg)
>>   {
>> -    AudioState *s;
>> -
>> -    QTAILQ_FOREACH(s, &audio_states, list) {
>> -        audio_run_out (s);
>> -        audio_run_in (s);
>> -        audio_run_capture (s);
>> -    }
>> +    audio_run_out(s);
>> +    audio_run_in(s);
>> +    audio_run_capture(s);
>>
>>   #ifdef DEBUG_POLL
>>       {
>> diff --git a/audio/audio_int.h b/audio/audio_int.h
>> index 1d81658..c52c656 100644
>> --- a/audio/audio_int.h
>> +++ b/audio/audio_int.h
>> @@ -220,7 +220,7 @@ int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
>>   int audio_bug (const char *funcname, int cond);
>>   void *audio_calloc (const char *funcname, int nmemb, size_t size);
>>
>> -void audio_run (const char *msg);
>> +void audio_run(AudioState *s, const char *msg);
>>
>>   #define VOICE_ENABLE 1
>>   #define VOICE_DISABLE 2
>> diff --git a/audio/ossaudio.c b/audio/ossaudio.c
>> index a5e7f7c..9eae769 100644
>> --- a/audio/ossaudio.c
>> +++ b/audio/ossaudio.c
>> @@ -112,28 +112,28 @@ static void oss_anal_close (int *fdp)
>>
>>   static void oss_helper_poll_out (void *opaque)
>>   {
>> -    (void) opaque;
>> -    audio_run ("oss_poll_out");
>> +    AudioState *s = opaque;
>> +    audio_run(s, "oss_poll_out");
>>   }
>>
>>   static void oss_helper_poll_in (void *opaque)
>>   {
>> -    (void) opaque;
>> -    audio_run ("oss_poll_in");
>> +    AudioState *s = opaque;
>> +    audio_run(s, "oss_poll_in");
>>   }
>>
>>   static void oss_poll_out (HWVoiceOut *hw)
>>   {
>>       OSSVoiceOut *oss = (OSSVoiceOut *) hw;
>>
>> -    qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
>> +    qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
>>   }
>>
>>   static void oss_poll_in (HWVoiceIn *hw)
>>   {
>>       OSSVoiceIn *oss = (OSSVoiceIn *) hw;
>>
>> -    qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
>> +    qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
>>   }
>>
>>   static int oss_write (SWVoiceOut *sw, void *buf, int len)
>> --
>> 2.4.5
>>
>>
>
> other than that,
> Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
>
>

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches
  2015-08-20 22:08   ` Marc-André Lureau
@ 2015-08-21 13:18     ` Kővágó Zoltán
  0 siblings, 0 replies; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-21 13:18 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann

2015-08-21 00:08 keltezéssel, Marc-André Lureau írta:
> Hi
>
> On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
>> Pulseaudio normally assumes that when the server wants it, the client
>> can generate the audio samples and send it right away.  Unfortunately
>> this is not the case with QEMU -- it's up to the emulated system when
>> does it generate the samples.  Buffering the samples and sending them
>> from a background thread is just a workaround, that doesn't work too
>> well.  Instead enable pa's compatibility support and let pa worry about
>> the details.
>
> Afaik, it's not a problem if the audio data is not available right
> away (live sources): PulseAudio should deal with that. I think it will
> simply silence and wait (but it could actually be smarter than that?)

Yes, it will simply silence and wait, until more data arrives.  The 
problem is that thing happens, like 10 times each second.  (If I watch 
pacmd list-sink-inputs, I can see that status quickly varies between 
drained and running, while playing an audio file in the guest)

> PA_STREAM_EARLY_REQUESTS is going to make PA request data more
> frequently (every minreq). I don't get why that would help here. It's
> meant for bad behaving applications using usleep and such that aren't
> fixable, not a solution for audio glitches.

The problem is that normally pulseaudio sends requests for data very 
late -- assuming that the application will quickly generate the required 
samples and send them back.  The problem is that the audio subsystem 
only "runs" each 10 ms, so it may take up to 10ms until qemu sends back 
data after the request, which is apparently too much.  With 
PA_STREAM_EARLY_REQUEST pulseaudio will request data as soon as it has 
enough free space in it's buffer, and not wait until the last moment.

Maybe tweaking pa buffer settings would help too, I'm not sure.  And 
yes, qemu is pretty much a bad behaving application that, while not 
using usleep directly, only produces audio every 10ms (by default).

>
> If qemu produces too much data than what PA can consume, it should
> drop the old data, and only play current audio (unfortunately, that
> doesn't seem to be the case looking at audio_pcm_sw_write). If qemu
> produces too little, it can still write whatever it has when it has
> it.
>
> Could you provide a way to reproduce the glitch you experience or
> explain more clearly how this flag helps?

It happens for me with a pretty much a default pulseaudio configuration, 
even with the stable release.  For example day24 from last year qemu 
advent calendar with pulseaudio backend.  (Sometimes it fixes itself 
after some time, very rarely it work normally, but most cases I just get 
that constant buffer underflow.)

>
>
> thanks
>
>>
>> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
>> ---
>>   audio/paaudio.c | 6 ++----
>>   1 file changed, 2 insertions(+), 4 deletions(-)
>>
>> diff --git a/audio/paaudio.c b/audio/paaudio.c
>> index 2aee22f..3990a80 100644
>> --- a/audio/paaudio.c
>> +++ b/audio/paaudio.c
>> @@ -510,10 +510,8 @@ static pa_stream *qpa_simple_new (
>>
>>       flags =
>>           PA_STREAM_INTERPOLATE_TIMING
>> -#ifdef PA_STREAM_ADJUST_LATENCY
>> -        |PA_STREAM_ADJUST_LATENCY
>> -#endif
>> -        |PA_STREAM_AUTO_TIMING_UPDATE;
>> +        |PA_STREAM_AUTO_TIMING_UPDATE
>> +        |PA_STREAM_EARLY_REQUESTS;
>>       if (dev) {
>>           /* don't move the stream if the user specified a sink/source */
>>           flags |= PA_STREAM_DONT_MOVE;
>> --
>> 2.4.5
>>
>>
>
>
>

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs
  2015-08-20 22:37   ` Marc-André Lureau
@ 2015-08-21 13:34     ` Kővágó Zoltán
  0 siblings, 0 replies; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-21 13:34 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: QEMU, Gerd Hoffmann

Hi,

2015-08-21 00:37 keltezéssel, Marc-André Lureau írta:
> Hi
>
> On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
>>
>> -    decr = rate_get_samples(&hw->info, &out->rate);
>> -    decr = audio_MIN(out->fsize - out->fpos, decr);
>> -
>> -    *size = decr << 2;
>> +    *size = audio_rate_get_bytes(&hw->info, &out->rate,
>> +                                 (out->fsize - out->fpos) << 2);
>>       return out->frame + out->fpos;
>>   }
>
> I am not familiar with this "rate" thing, but you are changing the way
> it works for Spice without explaining why, and that worries me a
> little.
>
> The rate->bytes_sent is not updated the same way, the result is not
> always << 2..
>
> I really don't get what this is really doing tbh, some kind of clever
> buffer limit :)
>

The old code pretty much ignored if the buffer overflow (which shouldn't 
happen normally), while the new one doesn't.  The problem is that with 
this new get_buffer/put_buffer thing if we reach the end of the circular 
buffer, it will produce have the same effect as a buffer overflow, but 
we need to handle it more gracefully as it will now happen every now and 
then.

But probably I'll change << 2 to info.shift, to handle more gracefully 
if the spice stream format changes.

Zoltan

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation)
  2015-08-20 22:49   ` Marc-André Lureau
@ 2015-08-21 13:46     ` Kővágó Zoltán
  0 siblings, 0 replies; 57+ messages in thread
From: Kővágó Zoltán @ 2015-08-21 13:46 UTC (permalink / raw)
  To: Marc-André Lureau; +Cc: Markus Armbruster, QEMU, Gerd Hoffmann

2015-08-21 00:49 keltezéssel, Marc-André Lureau írta:
> Hi
>
> On Thu, Aug 6, 2015 at 8:28 PM, Kővágó, Zoltán <dirty.ice.hu@gmail.com> wrote:
>> This will allow us to disable mixeng when we use a decent backend.
>>
>> Disabling mixeng have a few advantages:
>> * we no longer convert the audio output from one format to another, when
>>    the underlying audio system would just convert it to a third format.
>>    We no longer convert, only the underlying system, when needed.
>> * the underlying system probably has better resampling and sample format
>>    converting methods anyway...
>> * we may support formats that the mixeng currently does not support (S24
>>    or float samples, more than two channels)
>> * when using an audio server (like pulseaudio) different sound card
>>    outputs will show up as separate streams, even if we use only one
>>    backend
>>
>> Disadvantages:
>> * audio capturing no longer works (wavcapture, and vnc audio extension)
>> * some backends only support a single playback stream or very picky
>>    about the audio format.  In this case we can't disable mixeng.
>>
>
> Since the setting is so complicated, shouldn't it be a OnOffAuto? and
> let the backend decide what's best for the user?

Because of the items under disadvantages, the default must be on for 
backward compatibility.  And how would we choose the auto one?  For the 
time being it's only useful in some specific cases (like my upcoming 5.1 
support), so we should probably stay with OnOff (adding an auto option 
is possible later), expecting the user knows what he/she is doing if 
he/she disables it.

>
>> However mixeng is not removed, only made optional, so this shouldn't be
>> a big concern.
>>
>> Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
>> ---
>>   qapi/audio.json | 5 +++++
>>   qemu-options.hx | 6 ++++++
>>   2 files changed, 11 insertions(+)
>>
>> diff --git a/qapi/audio.json b/qapi/audio.json
>> index b57b215..0216a10 100644
>> --- a/qapi/audio.json
>> +++ b/qapi/audio.json
>> @@ -184,6 +184,10 @@
>>   #
>>   # General audio backend options that are used for both playback and recording.
>>   #
>> +# @mixeng: #optional use QEMU's mixing engine to mix all streams inside QEMU.
>> +#          When set to off, fixed-settings must be also off.  Not every backend
>> +#          compatible with the off setting (default on)
>> +#
>>   # @fixed-settings: #optional use fixed settings for host input/output.  When
>>   #                  off, frequency, channels and format must not be specified
>>   #                  (default on)
>> @@ -207,6 +211,7 @@
>>   ##
>>   { 'struct': 'AudiodevPerDirectionOptions',
>>     'data': {
>> +    '*mixeng':         'bool',
>>       '*fixed-settings': 'bool',
>>       '*frequency':      'int',
>>       '*channels':       'int',
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 60a3563..bd922bf 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -329,6 +329,7 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
>>       "                specifies the audio backend to use\n"
>>       "                id= identifier of the backend\n"
>>       "                timer-period= timer period in microseconds\n"
>> +    "                in|out.mixeng= use mixeng to mix streams inside QEMU\n"
>>       "                in|out.fixed-settings= use fixed settings for host audio\n"
>>       "                in|out.frequency= frequency to use with fixed settings\n"
>>       "                in|out.channels= number of channels to use with fixed settings\n"
>> @@ -397,6 +398,11 @@ Identifies the audio backend.
>>   Sets the timer @var{period} used by the audio subsystem in microseconds.
>>   Default is 10000 (10 ms).
>>
>> +@item in|out.mixeng=on|off
>> +Use QEMU's mixing engine to mix all streams inside QEMU.  When off,
>> +@var{fixed-settings} must be off too.  Not every backend is fully
>> +compatible with the off setting.  Default is on.
>> +
>>   @item in|out.fixed-settings=on|off
>>   Use fixed settings for host audio.  When off, it will change based on
>>   how the guest opens the sound card.  In this case you must not specify
>> --
>> 2.4.5
>>
>>
>
>
>

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio
  2015-08-21 12:23     ` Kővágó Zoltán
@ 2015-08-21 14:24       ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-21 14:24 UTC (permalink / raw)
  To: Kővágó Zoltán
  Cc: Paolo Bonzini, Luiz Capitulino, Gerd Hoffmann, QEMU, Markus Armbruster

Hi

On Fri, Aug 21, 2015 at 2:23 PM, Kővágó Zoltán <dirty.ice.hu@gmail.com> wrote:
> Hmm.  Maybe it would make more sense, but it's not how the audio system
> works.  The situation cyrrently (before my patch) is the following:
> VNC/spice/wavcapture attaches a capture to the global AudioState and starts
> recording/transmitting the mixed together audio of all connected guest sound
> cards.
>

Ok, thanks for explaining.

> Now with more audiodevs, you have to specify which one you wan't to capture
> from.  If there's multiple sound cards attached to a single audiodev
> (AudioState), then we will transmit the mix of them.  (If there's only one
> attached though, then we will only transmit that...)
>
> To support capture from a single guest device would require some rewriting
> of the audio capture logic, in a backward compatible way (which would mean
> support both capturing form a single card and capturing a mix of all cards.
> And in the latter case we still need some way to specify audiodev...)

Forget about that for now then :)

thanks


-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

* Re: [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server
  2015-08-21 12:41     ` Kővágó Zoltán
@ 2015-08-21 14:36       ` Marc-André Lureau
  0 siblings, 0 replies; 57+ messages in thread
From: Marc-André Lureau @ 2015-08-21 14:36 UTC (permalink / raw)
  To: Kővágó Zoltán; +Cc: QEMU, Gerd Hoffmann

On Fri, Aug 21, 2015 at 2:41 PM, Kővágó Zoltán <dirty.ice.hu@gmail.com> wrote:
> True, I will remove it from this commit.  But not sure if I want to remove
> it completely, because it looks like pulseaudio has a tendency to
> automatically move the stream on connect, even if you explicitly specify a
> sink/source when connecting.  (And the problem is that all streams appear
> under the name qemu in pulseaudio, so once the user moves one stream,
> pulseaudio will assume all streams subsequently created by qemu are the same
> on, and move them too.  Maybe we shold add some extra options to the pa
> backend?)


In theory, stream-restore shouldn't move streams if the sink is
already specified, see sink_input_new_hook_callback() in PA
module-stream-restore.c, but there might be something else applying
other policies? Yes, qemu could provide more context to the streams
with additional properties, which could help the policy. I think this
can be discussed seperately.

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 57+ messages in thread

end of thread, other threads:[~2015-08-21 14:36 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-06 18:28 [Qemu-devel] [PATCH 00/25] audio: -audiodev option, multiple options Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 01/25] qapi: support implicit structs in OptsVisitor Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 02/25] qapi: convert NumaOptions into a flat union Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 03/25] net: remove NetLegacy struct Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 04/25] net: use Netdev instead of NetClientOptions in client init Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 05/25] qapi: change Netdev into a flat union Kővágó, Zoltán
2015-08-20 15:03   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 06/25] qapi: reorder NetdevBase and Netdev Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 07/25] qapi: qapi for audio backends Kővágó, Zoltán
2015-08-20 15:30   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 08/25] qapi: support nested structs in OptsVisitor Kővágó, Zoltán
2015-08-20 15:55   ` Marc-André Lureau
2015-08-20 21:21     ` Kővágó Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 09/25] audio: use qapi AudioFormat instead of audfmt_e Kővágó, Zoltán
2015-08-20 16:00   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 10/25] audio: -audiodev command line option Kővágó, Zoltán
2015-08-20 17:17   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 11/25] audio: reduce glob_audio_state usage Kővágó, Zoltán
2015-08-20 17:39   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 12/25] audio: basic support for multi backend audio Kővágó, Zoltán
2015-08-20 18:01   ` Marc-André Lureau
2015-08-21 12:23     ` Kővágó Zoltán
2015-08-21 14:24       ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 13/25] audio: add audiodev properties to frontends Kővágó, Zoltán
2015-08-20 18:55   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 14/25] audio: audiodev= parameters no longer optional when -audiodev present Kővágó, Zoltán
2015-08-20 19:15   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 15/25] paaudio: do not create multiple connections to the same server Kővágó, Zoltán
2015-08-20 19:38   ` Marc-André Lureau
2015-08-21 12:41     ` Kővágó Zoltán
2015-08-21 14:36       ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 16/25] paaudio: properly disconnect streams in fini_* Kővágó, Zoltán
2015-08-20 20:29   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 17/25] audio: remove gcc specific audio_MIN, audio_MAX Kővágó, Zoltán
2015-08-19 18:17   ` Gerd Hoffmann
2015-08-19 23:31     ` Peter Maydell
2015-08-20 20:36   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 18/25] audio: do not run each backend in audio_run Kővágó, Zoltán
2015-08-20 20:49   ` Marc-André Lureau
2015-08-21 12:59     ` Kővágó Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 19/25] paaudio: fix playback glitches Kővágó, Zoltán
2015-08-20 22:08   ` Marc-André Lureau
2015-08-21 13:18     ` Kővágó Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 20/25] audio: remove mixeng specific code from backends Kővágó, Zoltán
2015-08-19 19:07   ` Gerd Hoffmann
2015-08-20 22:28     ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 21/25] audio: common rate control code for timer based outputs Kővágó, Zoltán
2015-08-20 22:37   ` Marc-André Lureau
2015-08-21 13:34     ` Kővágó Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 22/25] audio: add mixeng option (documentation) Kővágó, Zoltán
2015-08-20 22:49   ` Marc-André Lureau
2015-08-21 13:46     ` Kővágó Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 23/25] audio: make mixeng optional Kővágó, Zoltán
2015-08-20 22:56   ` Marc-André Lureau
2015-08-06 18:28 ` [Qemu-devel] [PATCH 24/25] paaudio: get/put_buffer functions Kővágó, Zoltán
2015-08-06 18:28 ` [Qemu-devel] [PATCH 25/25] audio: split ctl_* functions into enable_* and volume_* Kővágó, Zoltán
2015-08-20 23:05   ` Marc-André Lureau

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.