get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2196830/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2196830,
    "url": "http://patchwork.ozlabs.org/api/patches/2196830/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260216-audio-v1-40-e676662e4514@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260216-audio-v1-40-e676662e4514@redhat.com>",
    "list_archive_url": null,
    "date": "2026-02-16T11:15:29",
    "name": "[40/85] audio: split AudioMixengBackend code in audio-mixeng-be.c",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "3aad0d8cc579f391cbed19cf2ac3b8c77050d71c",
    "submitter": {
        "id": 66774,
        "url": "http://patchwork.ozlabs.org/api/people/66774/?format=api",
        "name": "Marc-André Lureau",
        "email": "marcandre.lureau@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260216-audio-v1-40-e676662e4514@redhat.com/mbox/",
    "series": [
        {
            "id": 492294,
            "url": "http://patchwork.ozlabs.org/api/series/492294/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=492294",
            "date": "2026-02-16T11:14:52",
            "name": "audio: cleanups & add a manual test",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/492294/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2196830/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2196830/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=afs/Byea;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from lists.gnu.org (lists.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fF0ks4wrYz1xpY\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 16 Feb 2026 22:23:17 +1100 (AEDT)",
            "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1vrweH-000321-Uc; Mon, 16 Feb 2026 06:20:18 -0500",
            "from eggs.gnu.org ([2001:470:142:3::10])\n by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1vrwbt-00066N-6F\n for qemu-devel@nongnu.org; Mon, 16 Feb 2026 06:17:50 -0500",
            "from us-smtp-delivery-124.mimecast.com ([170.10.129.124])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <marcandre.lureau@redhat.com>)\n id 1vrwbn-00085a-Uv\n for qemu-devel@nongnu.org; Mon, 16 Feb 2026 06:17:48 -0500",
            "from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-335-dCZx-Z68PK2mogcB9TQqZw-1; Mon,\n 16 Feb 2026 06:17:32 -0500",
            "from mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.93])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 7F78819560B2; Mon, 16 Feb 2026 11:17:30 +0000 (UTC)",
            "from localhost (unknown [10.45.242.26])\n by mx-prod-int-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 00BBA18004AD; Mon, 16 Feb 2026 11:17:28 +0000 (UTC)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1771240656;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=II/peA+q82xMlUb9oma13QrmJYqBB9KQPJ/lpc1p7P4=;\n b=afs/Byeasl9qq6Sf1goRtl91LEO2Gp+P/YU+SEaGzkR1nW4ljE8UJVBZ8pZt+ihDUbs1qK\n czZHdswDq9eSRmPfSs6kPhdDLlN8xmT1HiC3oIahr5U+A4qIM+EoAmlov7h0xowNyzSPHi\n J14QwlPyiAid5JfwmmvdsOlstgSdRfo=",
        "X-MC-Unique": "dCZx-Z68PK2mogcB9TQqZw-1",
        "X-Mimecast-MFC-AGG-ID": "dCZx-Z68PK2mogcB9TQqZw_1771240650",
        "From": "=?utf-8?q?Marc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>",
        "Date": "Mon, 16 Feb 2026 12:15:29 +0100",
        "Subject": "[PATCH 40/85] audio: split AudioMixengBackend code in\n audio-mixeng-be.c",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"utf-8\"",
        "Content-Transfer-Encoding": "8bit",
        "Message-Id": "<20260216-audio-v1-40-e676662e4514@redhat.com>",
        "References": "<20260216-audio-v1-0-e676662e4514@redhat.com>",
        "In-Reply-To": "<20260216-audio-v1-0-e676662e4514@redhat.com>",
        "To": "qemu-devel@nongnu.org",
        "Cc": "Gerd Hoffmann <kraxel@redhat.com>,  Eduardo Habkost <eduardo@habkost.net>,\n Paolo Bonzini <pbonzini@redhat.com>,\n =?utf-8?q?Daniel_P=2E_Berrang=C3=A9?= <berrange@redhat.com>, =?utf-8?q?Phil?=\n\t=?utf-8?q?ippe_Mathieu-Daud=C3=A9?= <philmd@linaro.org>,\n  John Snow <jsnow@redhat.com>, Cleber Rosa <crosa@redhat.com>,\n  Christian Schoenebeck <qemu_oss@crudebyte.com>,\n  Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>,\n  Thomas Huth <huth@tuxfamily.org>, Alexandre Ratchov <alex@caoua.org>,\n\t=?utf-8?q?Alex_Benn=C3=A9e?= <alex.bennee@linaro.org>,\n  Laurent Vivier <laurent@vivier.eu>, \"Michael S. Tsirkin\" <mst@redhat.com>,\n  Manos Pitsidianakis <manos.pitsidianakis@linaro.org>,\n  Alistair Francis <alistair@alistair23.me>,\n  \"Edgar E. Iglesias\" <edgar.iglesias@gmail.com>,\n  Peter Maydell <peter.maydell@linaro.org>, qemu-arm@nongnu.org, =?utf-8?q?M?=\n\t=?utf-8?q?arc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>,\n  Mark Cave-Ayland <mark.caveayland@nutanix.com>",
        "X-Developer-Signature": "v=1; a=openpgp-sha256; l=129255;\n i=marcandre.lureau@redhat.com; h=from:subject:message-id;\n bh=SYwwr9Fv/ErOF8w3ompPE10xHRSMs0ku9X6Rs2Ehs6A=;\n b=owEBbQKS/ZANAwAKAdro4Ql1lpzlAcsmYgBpkvxQTD463Yp8QBtSLnkm4/6SBt8F22XjZYSyF\n /ZgarWBmQGJAjMEAAEKAB0WIQSHqb2TP4fGBtJ29i3a6OEJdZac5QUCaZL8UAAKCRDa6OEJdZac\n 5U67D/4vu0Wva0slaEFvvhM/f6Uw8lBafldZJFqLdI7riSLvqNSsKNSq0CNmgp09cvlPnwZohcM\n cmqDARptiopkoUk+AUUmfWNasw2N7+irK8WYGP0DOMTWmLnk+s5Nao0bx/jIcoaL455myBtbZ6B\n g893XYU8KPe8+Q/dGWE+uTt78dCe61Ok3ZnqFX+tl+h+2f9QbxF//cbjiWSBJ0A5oAIx8pFPwQ7\n s+tcAypQARLiCOK+gRaTFlfKSs8M2qfzjXiPszyo26w6dLGvnBZToNDGjjJl+WQM6fA54qZb64j\n y7Fiv4eNdPB/RW7lm+hh68Hg9XWt6NKtzqhRcXuzMHyPtkjcwuDPAnuRHiE/4uc+fYgi+NQ7yfR\n Qu5WRtGHaq2SRkc6FoWXJWOImBT7LmMy1JXjwu8Yzm9dpjglNFzPYPuHYa5S2Kt/sjVQq7OFlfp\n YJ3lPFYPI+0YOsfderMMjwNNU76HQ+BuRCwGAHRBaBRCW0vBsMk5fvgXxoWJ3s61JZFuh8yg6Te\n Gv2l75X9ZN853a+HX+gXSCtDj1dJmctkktw6lax1dKKmXP7Geers8VQTDrmHy2apuHM+gVzj+2/\n ZkHhetkPrD8RasWP2cXES8jCzBuDoodUnL4F+eNkR93XYMeTaYiEcHOTBmiooJ6OyPn116hnDt0\n oFj8N1+Kh+1qPCw==",
        "X-Developer-Key": "i=marcandre.lureau@redhat.com; a=openpgp;\n fpr=87A9BD933F87C606D276F62DDAE8E10975969CE5",
        "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.93",
        "Received-SPF": "pass client-ip=170.10.129.124;\n envelope-from=marcandre.lureau@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com",
        "X-Spam_score_int": "-20",
        "X-Spam_score": "-2.1",
        "X-Spam_bar": "--",
        "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001,\n RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001,\n SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no",
        "X-Spam_action": "no action",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "qemu development <qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"
    },
    "content": "Allow to build the audio/ base classes without the\nresampling/mixing/queuing code.\n\nSigned-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>\nReviewed-by: Mark Cave-Ayland <mark.caveayland@nutanix.com>\n---\n audio/audio_int.h       |    2 -\n include/qemu/audio.h    |    2 +\n audio/audio-be.c        |    2 +\n audio/audio-mixeng-be.c | 1976 +++++++++++++++++++++++++++++++++++++++++\n audio/audio.c           | 2223 ++++-------------------------------------------\n audio/meson.build       |    1 +\n 6 files changed, 2158 insertions(+), 2048 deletions(-)",
    "diff": "diff --git a/audio/audio_int.h b/audio/audio_int.h\nindex 250fd45238d..6ecd75c4fbf 100644\n--- a/audio/audio_int.h\n+++ b/audio/audio_int.h\n@@ -266,8 +266,6 @@ int audio_bug (const char *funcname, int cond);\n \n void audio_run(AudioMixengBackend *s, const char *msg);\n \n-const char *audio_application_name(void);\n-\n typedef struct RateCtl {\n     int64_t start_ticks;\n     int64_t bytes_sent;\ndiff --git a/include/qemu/audio.h b/include/qemu/audio.h\nindex 42f97f732a6..dfe247ab8c4 100644\n--- a/include/qemu/audio.h\n+++ b/include/qemu/audio.h\n@@ -183,6 +183,8 @@ bool audio_be_set_dbus_server(AudioBackend *be,\n                               Error **errp);\n #endif\n \n+const char *audio_application_name(void);\n+\n #define DEFINE_AUDIO_PROPERTIES(_s, _f)         \\\n     DEFINE_PROP_AUDIODEV(\"audiodev\", _s, _f)\n \ndiff --git a/audio/audio-be.c b/audio/audio-be.c\nindex 3203bb80e05..8094a3196c7 100644\n--- a/audio/audio-be.c\n+++ b/audio/audio-be.c\n@@ -5,6 +5,7 @@\n #include \"qemu/audio-capture.h\"\n #include \"qapi/error.h\"\n #include \"trace.h\"\n+#include \"qapi-types-audio.h\"\n \n bool audio_be_check(AudioBackend **be, Error **errp)\n {\n@@ -248,6 +249,7 @@ AudioBackend *audio_be_new(Audiodev *dev, Error **errp)\n \n     if (!be) {\n         error_setg(errp, \"Unknown audio driver `%s'\", drvname);\n+        qapi_free_Audiodev(dev);\n         return NULL;\n     }\n \ndiff --git a/audio/audio-mixeng-be.c b/audio/audio-mixeng-be.c\nnew file mode 100644\nindex 00000000000..f906c84bf7c\n--- /dev/null\n+++ b/audio/audio-mixeng-be.c\n@@ -0,0 +1,1976 @@\n+/*\n+ * SPDX-License-Identifier: MIT\n+ *\n+ * Copyright (c) 2003-2005 Vassili Karpov (malc)\n+ */\n+\n+#include \"qemu/osdep.h\"\n+#include \"qemu/audio.h\"\n+#include \"migration/vmstate.h\"\n+#include \"qemu/bswap.h\"\n+#include \"qemu/timer.h\"\n+#include \"qapi/error.h\"\n+#include \"qapi/clone-visitor.h\"\n+#include \"qapi/qobject-input-visitor.h\"\n+#include \"qapi/qapi-visit-audio.h\"\n+#include \"qapi/qapi-commands-audio.h\"\n+#include \"qobject/qdict.h\"\n+#include \"qemu/error-report.h\"\n+#include \"qemu/log.h\"\n+#include \"qemu/module.h\"\n+#include \"qemu/help_option.h\"\n+#include \"qom/object.h\"\n+#include \"system/system.h\"\n+#include \"system/replay.h\"\n+#include \"system/runstate.h\"\n+#include \"trace.h\"\n+\n+#define AUDIO_CAP \"audio\"\n+#include \"audio_int.h\"\n+\n+/* #define DEBUG_OUT */\n+/* #define DEBUG_CAPTURE */\n+/* #define DEBUG_POLL */\n+\n+#define SW_NAME(sw) (sw)->name ? (sw)->name : \"unknown\"\n+\n+const struct mixeng_volume nominal_volume = {\n+    .mute = 0,\n+#ifdef FLOAT_MIXENG\n+    .r = 1.0,\n+    .l = 1.0,\n+#else\n+    .r = 1ULL << 32,\n+    .l = 1ULL << 32,\n+#endif\n+};\n+\n+int audio_bug (const char *funcname, int cond)\n+{\n+    if (cond) {\n+        static int shown;\n+\n+        AUD_log (NULL, \"A bug was just triggered in %s\\n\", funcname);\n+        if (!shown) {\n+            shown = 1;\n+            AUD_log (NULL, \"Save all your work and restart without audio\\n\");\n+            AUD_log (NULL, \"I am sorry\\n\");\n+        }\n+        AUD_log (NULL, \"Context:\\n\");\n+    }\n+\n+    return cond;\n+}\n+\n+static inline int audio_bits_to_index (int bits)\n+{\n+    switch (bits) {\n+    case 8:\n+        return 0;\n+\n+    case 16:\n+        return 1;\n+\n+    case 32:\n+        return 2;\n+\n+    default:\n+        audio_bug (\"bits_to_index\", 1);\n+        AUD_log (NULL, \"invalid bits %d\\n\", bits);\n+        return 0;\n+    }\n+}\n+\n+void AUD_vlog (const char *cap, const char *fmt, va_list ap)\n+{\n+    if (cap) {\n+        fprintf(stderr, \"%s: \", cap);\n+    }\n+\n+    vfprintf(stderr, fmt, ap);\n+}\n+\n+void AUD_log (const char *cap, const char *fmt, ...)\n+{\n+    va_list ap;\n+\n+    va_start (ap, fmt);\n+    AUD_vlog (cap, fmt, ap);\n+    va_end (ap);\n+}\n+\n+static void audio_print_settings (const struct audsettings *as)\n+{\n+    dolog (\"frequency=%d nchannels=%d fmt=\", as->freq, as->nchannels);\n+\n+    switch (as->fmt) {\n+    case AUDIO_FORMAT_S8:\n+        AUD_log (NULL, \"S8\");\n+        break;\n+    case AUDIO_FORMAT_U8:\n+        AUD_log (NULL, \"U8\");\n+        break;\n+    case AUDIO_FORMAT_S16:\n+        AUD_log (NULL, \"S16\");\n+        break;\n+    case AUDIO_FORMAT_U16:\n+        AUD_log (NULL, \"U16\");\n+        break;\n+    case AUDIO_FORMAT_S32:\n+        AUD_log (NULL, \"S32\");\n+        break;\n+    case AUDIO_FORMAT_U32:\n+        AUD_log (NULL, \"U32\");\n+        break;\n+    case AUDIO_FORMAT_F32:\n+        AUD_log (NULL, \"F32\");\n+        break;\n+    default:\n+        AUD_log (NULL, \"invalid(%d)\", as->fmt);\n+        break;\n+    }\n+\n+    AUD_log (NULL, \" endianness=\");\n+    switch (as->endianness) {\n+    case 0:\n+        AUD_log (NULL, \"little\");\n+        break;\n+    case 1:\n+        AUD_log (NULL, \"big\");\n+        break;\n+    default:\n+        AUD_log (NULL, \"invalid\");\n+        break;\n+    }\n+    AUD_log (NULL, \"\\n\");\n+}\n+\n+static int audio_validate_settings (const struct audsettings *as)\n+{\n+    int invalid;\n+\n+    invalid = as->nchannels < 1;\n+    invalid |= as->endianness != 0 && as->endianness != 1;\n+\n+    switch (as->fmt) {\n+    case AUDIO_FORMAT_S8:\n+    case AUDIO_FORMAT_U8:\n+    case AUDIO_FORMAT_S16:\n+    case AUDIO_FORMAT_U16:\n+    case AUDIO_FORMAT_S32:\n+    case AUDIO_FORMAT_U32:\n+    case AUDIO_FORMAT_F32:\n+        break;\n+    default:\n+        invalid = 1;\n+        break;\n+    }\n+\n+    invalid |= as->freq <= 0;\n+    return invalid ? -1 : 0;\n+}\n+\n+static int audio_pcm_info_eq (struct audio_pcm_info *info, const struct audsettings *as)\n+{\n+    int bits = 8;\n+    bool is_signed = false, is_float = false;\n+\n+    switch (as->fmt) {\n+    case AUDIO_FORMAT_S8:\n+        is_signed = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_U8:\n+        break;\n+\n+    case AUDIO_FORMAT_S16:\n+        is_signed = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_U16:\n+        bits = 16;\n+        break;\n+\n+    case AUDIO_FORMAT_F32:\n+        is_float = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_S32:\n+        is_signed = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_U32:\n+        bits = 32;\n+        break;\n+\n+    default:\n+        abort();\n+    }\n+    return info->freq == as->freq\n+        && info->nchannels == as->nchannels\n+        && info->is_signed == is_signed\n+        && info->is_float == is_float\n+        && info->bits == bits\n+        && info->swap_endianness == (as->endianness != HOST_BIG_ENDIAN);\n+}\n+\n+void audio_pcm_init_info (struct audio_pcm_info *info, const struct audsettings *as)\n+{\n+    int bits = 8, mul;\n+    bool is_signed = false, is_float = false;\n+\n+    switch (as->fmt) {\n+    case AUDIO_FORMAT_S8:\n+        is_signed = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_U8:\n+        mul = 1;\n+        break;\n+\n+    case AUDIO_FORMAT_S16:\n+        is_signed = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_U16:\n+        bits = 16;\n+        mul = 2;\n+        break;\n+\n+    case AUDIO_FORMAT_F32:\n+        is_float = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_S32:\n+        is_signed = true;\n+        /* fall through */\n+    case AUDIO_FORMAT_U32:\n+        bits = 32;\n+        mul = 4;\n+        break;\n+\n+    default:\n+        abort();\n+    }\n+\n+    info->freq = as->freq;\n+    info->bits = bits;\n+    info->is_signed = is_signed;\n+    info->is_float = is_float;\n+    info->nchannels = as->nchannels;\n+    info->bytes_per_frame = as->nchannels * mul;\n+    info->bytes_per_second = info->freq * info->bytes_per_frame;\n+    info->swap_endianness = (as->endianness != HOST_BIG_ENDIAN);\n+}\n+\n+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)\n+{\n+    if (!len) {\n+        return;\n+    }\n+\n+    if (info->is_signed || info->is_float) {\n+        memset(buf, 0x00, len * info->bytes_per_frame);\n+    } else {\n+        switch (info->bits) {\n+        case 8:\n+            memset(buf, 0x80, len * info->bytes_per_frame);\n+            break;\n+\n+        case 16:\n+            {\n+                int i;\n+                uint16_t *p = buf;\n+                short s = INT16_MAX;\n+\n+                if (info->swap_endianness) {\n+                    s = bswap16 (s);\n+                }\n+\n+                for (i = 0; i < len * info->nchannels; i++) {\n+                    p[i] = s;\n+                }\n+            }\n+            break;\n+\n+        case 32:\n+            {\n+                int i;\n+                uint32_t *p = buf;\n+                int32_t s = INT32_MAX;\n+\n+                if (info->swap_endianness) {\n+                    s = bswap32 (s);\n+                }\n+\n+                for (i = 0; i < len * info->nchannels; i++) {\n+                    p[i] = s;\n+                }\n+            }\n+            break;\n+\n+        default:\n+            AUD_log (NULL, \"audio_pcm_info_clear_buf: invalid bits %d\\n\",\n+                     info->bits);\n+            break;\n+        }\n+    }\n+}\n+\n+/*\n+ * Capture\n+ */\n+static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioMixengBackend *s,\n+                                                        struct audsettings *as)\n+{\n+    CaptureVoiceOut *cap;\n+\n+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {\n+        if (audio_pcm_info_eq (&cap->hw.info, as)) {\n+            return cap;\n+        }\n+    }\n+    return NULL;\n+}\n+\n+static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)\n+{\n+    struct capture_callback *cb;\n+\n+#ifdef DEBUG_CAPTURE\n+    dolog (\"notification %d sent\\n\", cmd);\n+#endif\n+    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n+        cb->ops.notify (cb->opaque, cmd);\n+    }\n+}\n+\n+static void audio_capture_maybe_changed(CaptureVoiceOut *cap, bool enabled)\n+{\n+    if (cap->hw.enabled != enabled) {\n+        audcnotification_e cmd;\n+        cap->hw.enabled = enabled;\n+        cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;\n+        audio_notify_capture (cap, cmd);\n+    }\n+}\n+\n+static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)\n+{\n+    HWVoiceOut *hw = &cap->hw;\n+    SWVoiceOut *sw;\n+    bool enabled = false;\n+\n+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+        if (sw->active) {\n+            enabled = true;\n+            break;\n+        }\n+    }\n+    audio_capture_maybe_changed (cap, enabled);\n+}\n+\n+static void audio_detach_capture (HWVoiceOut *hw)\n+{\n+    SWVoiceCap *sc = hw->cap_head.lh_first;\n+\n+    while (sc) {\n+        SWVoiceCap *sc1 = sc->entries.le_next;\n+        SWVoiceOut *sw = &sc->sw;\n+        CaptureVoiceOut *cap = sc->cap;\n+        int was_active = sw->active;\n+\n+        if (sw->rate) {\n+            st_rate_stop (sw->rate);\n+            sw->rate = NULL;\n+        }\n+\n+        QLIST_REMOVE (sw, entries);\n+        QLIST_REMOVE (sc, entries);\n+        g_free (sc);\n+        if (was_active) {\n+            /* We have removed soft voice from the capture:\n+               this might have changed the overall status of the capture\n+               since this might have been the only active voice */\n+            audio_recalc_and_notify_capture (cap);\n+        }\n+        sc = sc1;\n+    }\n+}\n+\n+static int audio_attach_capture (HWVoiceOut *hw)\n+{\n+    AudioMixengBackend *s = hw->s;\n+    CaptureVoiceOut *cap;\n+\n+    audio_detach_capture (hw);\n+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {\n+        SWVoiceCap *sc;\n+        SWVoiceOut *sw;\n+        HWVoiceOut *hw_cap = &cap->hw;\n+\n+        sc = g_malloc0(sizeof(*sc));\n+\n+        sc->cap = cap;\n+        sw = &sc->sw;\n+        sw->hw = hw_cap;\n+        sw->info = hw->info;\n+        sw->empty = true;\n+        sw->active = hw->enabled;\n+        sw->vol = nominal_volume;\n+        sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);\n+        QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);\n+        QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);\n+#ifdef DEBUG_CAPTURE\n+        sw->name = g_strdup_printf (\"for %p %d,%d,%d\",\n+                                    hw, sw->info.freq, sw->info.bits,\n+                                    sw->info.nchannels);\n+        dolog (\"Added %s active = %d\\n\", sw->name, sw->active);\n+#endif\n+        if (sw->active) {\n+            audio_capture_maybe_changed (cap, 1);\n+        }\n+    }\n+    return 0;\n+}\n+\n+/*\n+ * Hard voice (capture)\n+ */\n+static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)\n+{\n+    SWVoiceIn *sw;\n+    size_t m = hw->total_samples_captured;\n+\n+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+        if (sw->active) {\n+            m = MIN (m, sw->total_hw_samples_acquired);\n+        }\n+    }\n+    return m;\n+}\n+\n+static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)\n+{\n+    size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);\n+    if (audio_bug(__func__, live > hw->conv_buf.size)) {\n+        dolog(\"live=%zu hw->conv_buf.size=%zu\\n\", live, hw->conv_buf.size);\n+        return 0;\n+    }\n+    return live;\n+}\n+\n+static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)\n+{\n+    size_t conv = 0;\n+    STSampleBuffer *conv_buf = &hw->conv_buf;\n+\n+    while (samples) {\n+        uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);\n+        size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);\n+\n+        hw->conv(conv_buf->buffer + conv_buf->pos, src, proc);\n+        conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;\n+        samples -= proc;\n+        conv += proc;\n+    }\n+\n+    return conv;\n+}\n+\n+/*\n+ * Soft voice (capture)\n+ */\n+static void audio_pcm_sw_resample_in(SWVoiceIn *sw,\n+    size_t frames_in_max, size_t frames_out_max,\n+    size_t *total_in, size_t *total_out)\n+{\n+    HWVoiceIn *hw = sw->hw;\n+    struct st_sample *src, *dst;\n+    size_t live, rpos, frames_in, frames_out;\n+\n+    live = hw->total_samples_captured - sw->total_hw_samples_acquired;\n+    rpos = audio_ring_posb(hw->conv_buf.pos, live, hw->conv_buf.size);\n+\n+    /* resample conv_buf from rpos to end of buffer */\n+    src = hw->conv_buf.buffer + rpos;\n+    frames_in = MIN(frames_in_max, hw->conv_buf.size - rpos);\n+    dst = sw->resample_buf.buffer;\n+    frames_out = frames_out_max;\n+    st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);\n+    rpos += frames_in;\n+    *total_in = frames_in;\n+    *total_out = frames_out;\n+\n+    /* resample conv_buf from start of buffer if there are input frames left */\n+    if (frames_in_max - frames_in && rpos == hw->conv_buf.size) {\n+        src = hw->conv_buf.buffer;\n+        frames_in = frames_in_max - frames_in;\n+        dst += frames_out;\n+        frames_out = frames_out_max - frames_out;\n+        st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);\n+        *total_in += frames_in;\n+        *total_out += frames_out;\n+    }\n+}\n+\n+static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t buf_len)\n+{\n+    HWVoiceIn *hw = sw->hw;\n+    size_t live, frames_out_max, total_in, total_out;\n+\n+    live = hw->total_samples_captured - sw->total_hw_samples_acquired;\n+    if (!live) {\n+        return 0;\n+    }\n+    if (audio_bug(__func__, live > hw->conv_buf.size)) {\n+        dolog(\"live_in=%zu hw->conv_buf.size=%zu\\n\", live, hw->conv_buf.size);\n+        return 0;\n+    }\n+\n+    frames_out_max = MIN(buf_len / sw->info.bytes_per_frame,\n+                         sw->resample_buf.size);\n+\n+    audio_pcm_sw_resample_in(sw, live, frames_out_max, &total_in, &total_out);\n+\n+    if (!hw->pcm_ops->volume_in) {\n+        mixeng_volume(sw->resample_buf.buffer, total_out, &sw->vol);\n+    }\n+    sw->clip(buf, sw->resample_buf.buffer, total_out);\n+\n+    sw->total_hw_samples_acquired += total_in;\n+    return total_out * sw->info.bytes_per_frame;\n+}\n+\n+/*\n+ * Hard voice (playback)\n+ */\n+static size_t audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)\n+{\n+    SWVoiceOut *sw;\n+    size_t m = SIZE_MAX;\n+    int nb_live = 0;\n+\n+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+        if (sw->active || !sw->empty) {\n+            m = MIN (m, sw->total_hw_samples_mixed);\n+            nb_live += 1;\n+        }\n+    }\n+\n+    *nb_livep = nb_live;\n+    return m;\n+}\n+\n+static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)\n+{\n+    size_t smin;\n+    int nb_live1;\n+\n+    smin = audio_pcm_hw_find_min_out (hw, &nb_live1);\n+    if (nb_live) {\n+        *nb_live = nb_live1;\n+    }\n+\n+    if (nb_live1) {\n+        size_t live = smin;\n+\n+        if (audio_bug(__func__, live > hw->mix_buf.size)) {\n+            dolog(\"live=%zu hw->mix_buf.size=%zu\\n\", live, hw->mix_buf.size);\n+            return 0;\n+        }\n+        return live;\n+    }\n+    return 0;\n+}\n+\n+static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)\n+{\n+    return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :\n+            INT_MAX) / hw->info.bytes_per_frame;\n+}\n+\n+static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)\n+{\n+    size_t clipped = 0;\n+    size_t pos = hw->mix_buf.pos;\n+\n+    while (len) {\n+        st_sample *src = hw->mix_buf.buffer + pos;\n+        uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);\n+        size_t samples_till_end_of_buf = hw->mix_buf.size - pos;\n+        size_t samples_to_clip = MIN(len, samples_till_end_of_buf);\n+\n+        hw->clip(dst, src, samples_to_clip);\n+\n+        pos = (pos + samples_to_clip) % hw->mix_buf.size;\n+        len -= samples_to_clip;\n+        clipped += samples_to_clip;\n+    }\n+}\n+\n+/*\n+ * Soft voice (playback)\n+ */\n+static void audio_pcm_sw_resample_out(SWVoiceOut *sw,\n+    size_t frames_in_max, size_t frames_out_max,\n+    size_t *total_in, size_t *total_out)\n+{\n+    HWVoiceOut *hw = sw->hw;\n+    struct st_sample *src, *dst;\n+    size_t live, wpos, frames_in, frames_out;\n+\n+    live = sw->total_hw_samples_mixed;\n+    wpos = (hw->mix_buf.pos + live) % hw->mix_buf.size;\n+\n+    /* write to mix_buf from wpos to end of buffer */\n+    src = sw->resample_buf.buffer;\n+    frames_in = frames_in_max;\n+    dst = hw->mix_buf.buffer + wpos;\n+    frames_out = MIN(frames_out_max, hw->mix_buf.size - wpos);\n+    st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);\n+    wpos += frames_out;\n+    *total_in = frames_in;\n+    *total_out = frames_out;\n+\n+    /* write to mix_buf from start of buffer if there are input frames left */\n+    if (frames_in_max - frames_in > 0 && wpos == hw->mix_buf.size) {\n+        src += frames_in;\n+        frames_in = frames_in_max - frames_in;\n+        dst = hw->mix_buf.buffer;\n+        frames_out = frames_out_max - frames_out;\n+        st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);\n+        *total_in += frames_in;\n+        *total_out += frames_out;\n+    }\n+}\n+\n+static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)\n+{\n+    HWVoiceOut *hw = sw->hw;\n+    size_t live, dead, hw_free, sw_max, fe_max;\n+    size_t frames_in_max, frames_out_max, total_in, total_out;\n+\n+    live = sw->total_hw_samples_mixed;\n+    if (audio_bug(__func__, live > hw->mix_buf.size)) {\n+        dolog(\"live=%zu hw->mix_buf.size=%zu\\n\", live, hw->mix_buf.size);\n+        return 0;\n+    }\n+\n+    if (live == hw->mix_buf.size) {\n+#ifdef DEBUG_OUT\n+        dolog (\"%s is full %zu\\n\", sw->name, live);\n+#endif\n+        return 0;\n+    }\n+\n+    dead = hw->mix_buf.size - live;\n+    hw_free = audio_pcm_hw_get_free(hw);\n+    hw_free = hw_free > live ? hw_free - live : 0;\n+    frames_out_max = MIN(dead, hw_free);\n+    sw_max = st_rate_frames_in(sw->rate, frames_out_max);\n+    fe_max = MIN(buf_len / sw->info.bytes_per_frame + sw->resample_buf.pos,\n+                 sw->resample_buf.size);\n+    frames_in_max = MIN(sw_max, fe_max);\n+\n+    if (!frames_in_max) {\n+        return 0;\n+    }\n+\n+    if (frames_in_max > sw->resample_buf.pos) {\n+        sw->conv(sw->resample_buf.buffer + sw->resample_buf.pos,\n+                 buf, frames_in_max - sw->resample_buf.pos);\n+        if (!sw->hw->pcm_ops->volume_out) {\n+            mixeng_volume(sw->resample_buf.buffer + sw->resample_buf.pos,\n+                          frames_in_max - sw->resample_buf.pos, &sw->vol);\n+        }\n+    }\n+\n+    audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max,\n+                              &total_in, &total_out);\n+\n+    sw->total_hw_samples_mixed += total_out;\n+    sw->empty = sw->total_hw_samples_mixed == 0;\n+\n+    /*\n+     * Upsampling may leave one audio frame in the resample buffer. Decrement\n+     * total_in by one if there was a leftover frame from the previous resample\n+     * pass in the resample buffer. Increment total_in by one if the current\n+     * resample pass left one frame in the resample buffer.\n+     */\n+    if (frames_in_max - total_in == 1) {\n+        /* copy one leftover audio frame to the beginning of the buffer */\n+        *sw->resample_buf.buffer = *(sw->resample_buf.buffer + total_in);\n+        total_in += 1 - sw->resample_buf.pos;\n+        sw->resample_buf.pos = 1;\n+    } else if (total_in >= sw->resample_buf.pos) {\n+        total_in -= sw->resample_buf.pos;\n+        sw->resample_buf.pos = 0;\n+    }\n+\n+#ifdef DEBUG_OUT\n+    dolog (\n+        \"%s: write size %zu written %zu total mixed %zu\\n\",\n+        SW_NAME(sw),\n+        buf_len / sw->info.bytes_per_frame,\n+        total_in,\n+        sw->total_hw_samples_mixed\n+        );\n+#endif\n+\n+    return total_in * sw->info.bytes_per_frame;\n+}\n+\n+#ifdef DEBUG_AUDIO\n+static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)\n+{\n+    dolog(\"%s: bits %d, sign %d, float %d, freq %d, nchan %d\\n\",\n+          cap, info->bits, info->is_signed, info->is_float, info->freq,\n+          info->nchannels);\n+}\n+#endif\n+\n+#define DAC\n+#include \"audio_template.h\"\n+#undef DAC\n+#include \"audio_template.h\"\n+\n+/*\n+ * Timer\n+ */\n+static int audio_is_timer_needed(AudioMixengBackend *s)\n+{\n+    HWVoiceIn *hwi = NULL;\n+    HWVoiceOut *hwo = NULL;\n+\n+    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {\n+        if (!hwo->poll_mode) {\n+            return 1;\n+        }\n+    }\n+    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {\n+        if (!hwi->poll_mode) {\n+            return 1;\n+        }\n+    }\n+    return 0;\n+}\n+\n+static void audio_reset_timer(AudioMixengBackend *s)\n+{\n+    if (audio_is_timer_needed(s)) {\n+        timer_mod_anticipate_ns(s->ts,\n+            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);\n+        if (!s->timer_running) {\n+            s->timer_running = true;\n+            s->timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n+            trace_audio_timer_start(s->period_ticks / SCALE_MS);\n+        }\n+    } else {\n+        timer_del(s->ts);\n+        if (s->timer_running) {\n+            s->timer_running = false;\n+            trace_audio_timer_stop();\n+        }\n+    }\n+}\n+\n+static void audio_timer (void *opaque)\n+{\n+    int64_t now, diff;\n+    AudioMixengBackend *s = opaque;\n+\n+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n+    diff = now - s->timer_last;\n+    if (diff > s->period_ticks * 3 / 2) {\n+        trace_audio_timer_delayed(diff / SCALE_MS);\n+    }\n+    s->timer_last = now;\n+\n+    audio_run(s, \"timer\");\n+    audio_reset_timer(s);\n+}\n+\n+/*\n+ * Public API\n+ */\n+static size_t audio_mixeng_backend_write(AudioBackend *be, SWVoiceOut *sw,\n+                                         void *buf, size_t size)\n+{\n+    HWVoiceOut *hw;\n+\n+    if (!sw) {\n+        /* XXX: Consider options */\n+        return size;\n+    }\n+    hw = sw->hw;\n+\n+    if (!hw->enabled) {\n+        dolog(\"Writing to disabled voice %s\\n\", SW_NAME(sw));\n+        return 0;\n+    }\n+\n+    if (audio_get_pdo_out(hw->s->dev)->mixing_engine) {\n+        return audio_pcm_sw_write(sw, buf, size);\n+    } else {\n+        return hw->pcm_ops->write(hw, buf, size);\n+    }\n+}\n+\n+static size_t audio_mixeng_backend_read(AudioBackend *be,\n+                                        SWVoiceIn *sw, void *buf, size_t size)\n+{\n+    HWVoiceIn *hw;\n+\n+    if (!sw) {\n+        /* XXX: Consider options */\n+        return size;\n+    }\n+    hw = sw->hw;\n+\n+    if (!hw->enabled) {\n+        dolog(\"Reading from disabled voice %s\\n\", SW_NAME(sw));\n+        return 0;\n+    }\n+\n+    if (audio_get_pdo_in(hw->s->dev)->mixing_engine) {\n+        return audio_pcm_sw_read(sw, buf, size);\n+    } else {\n+        return hw->pcm_ops->read(hw, buf, size);\n+    }\n+\n+}\n+\n+static int audio_mixeng_backend_get_buffer_size_out(AudioBackend *be, SWVoiceOut *sw)\n+{\n+    if (!sw) {\n+        return 0;\n+    }\n+\n+    if (audio_get_pdo_out(sw->s->dev)->mixing_engine) {\n+        return sw->resample_buf.size * sw->info.bytes_per_frame;\n+    }\n+\n+    return sw->hw->samples * sw->hw->info.bytes_per_frame;\n+}\n+\n+static void audio_mixeng_backend_set_active_out(AudioBackend *be, SWVoiceOut *sw,\n+                                                bool on)\n+{\n+    HWVoiceOut *hw;\n+\n+    if (!sw) {\n+        return;\n+    }\n+\n+    hw = sw->hw;\n+    if (sw->active != on) {\n+        AudioMixengBackend *s = sw->s;\n+        SWVoiceOut *temp_sw;\n+        SWVoiceCap *sc;\n+\n+        if (on) {\n+            hw->pending_disable = 0;\n+            if (!hw->enabled) {\n+                hw->enabled = true;\n+                if (runstate_is_running()) {\n+                    if (hw->pcm_ops->enable_out) {\n+                        hw->pcm_ops->enable_out(hw, true);\n+                    }\n+                    audio_reset_timer (s);\n+                }\n+            }\n+        } else {\n+            if (hw->enabled) {\n+                int nb_active = 0;\n+\n+                for (temp_sw = hw->sw_head.lh_first; temp_sw;\n+                     temp_sw = temp_sw->entries.le_next) {\n+                    nb_active += temp_sw->active != 0;\n+                }\n+\n+                hw->pending_disable = nb_active == 1;\n+            }\n+        }\n+\n+        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n+            sc->sw.active = hw->enabled;\n+            if (hw->enabled) {\n+                audio_capture_maybe_changed (sc->cap, 1);\n+            }\n+        }\n+        sw->active = on;\n+    }\n+\n+}\n+\n+static void audio_mixeng_backend_set_active_in(AudioBackend *be, SWVoiceIn *sw, bool on)\n+{\n+    HWVoiceIn *hw;\n+\n+    if (!sw) {\n+        return;\n+    }\n+\n+    hw = sw->hw;\n+    if (sw->active != on) {\n+        AudioMixengBackend *s = sw->s;\n+        SWVoiceIn *temp_sw;\n+\n+        if (on) {\n+            if (!hw->enabled) {\n+                hw->enabled = true;\n+                if (runstate_is_running()) {\n+                    if (hw->pcm_ops->enable_in) {\n+                        hw->pcm_ops->enable_in(hw, true);\n+                    }\n+                    audio_reset_timer (s);\n+                }\n+            }\n+            sw->total_hw_samples_acquired = hw->total_samples_captured;\n+        } else {\n+            if (hw->enabled) {\n+                int nb_active = 0;\n+\n+                for (temp_sw = hw->sw_head.lh_first; temp_sw;\n+                     temp_sw = temp_sw->entries.le_next) {\n+                    nb_active += temp_sw->active != 0;\n+                }\n+\n+                if (nb_active == 1) {\n+                    hw->enabled = false;\n+                    if (hw->pcm_ops->enable_in) {\n+                        hw->pcm_ops->enable_in(hw, false);\n+                    }\n+                }\n+            }\n+        }\n+        sw->active = on;\n+    }\n+}\n+\n+static size_t audio_get_avail(SWVoiceIn *sw)\n+{\n+    size_t live;\n+\n+    if (!sw) {\n+        return 0;\n+    }\n+\n+    live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;\n+    if (audio_bug(__func__, live > sw->hw->conv_buf.size)) {\n+        dolog(\"live=%zu sw->hw->conv_buf.size=%zu\\n\", live,\n+              sw->hw->conv_buf.size);\n+        return 0;\n+    }\n+\n+    ldebug (\n+        \"%s: get_avail live %zu frontend frames %u\\n\",\n+        SW_NAME (sw),\n+        live, st_rate_frames_out(sw->rate, live)\n+        );\n+\n+    return live;\n+}\n+\n+static size_t audio_get_free(SWVoiceOut *sw)\n+{\n+    size_t live, dead;\n+\n+    if (!sw) {\n+        return 0;\n+    }\n+\n+    live = sw->total_hw_samples_mixed;\n+\n+    if (audio_bug(__func__, live > sw->hw->mix_buf.size)) {\n+        dolog(\"live=%zu sw->hw->mix_buf.size=%zu\\n\", live,\n+              sw->hw->mix_buf.size);\n+        return 0;\n+    }\n+\n+    dead = sw->hw->mix_buf.size - live;\n+\n+#ifdef DEBUG_OUT\n+    dolog(\"%s: get_free live %zu dead %zu frontend frames %u\\n\",\n+          SW_NAME(sw), live, dead, st_rate_frames_in(sw->rate, dead));\n+#endif\n+\n+    return dead;\n+}\n+\n+static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,\n+                                        size_t samples)\n+{\n+    size_t n;\n+\n+    if (hw->enabled) {\n+        SWVoiceCap *sc;\n+\n+        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n+            SWVoiceOut *sw = &sc->sw;\n+            size_t rpos2 = rpos;\n+\n+            n = samples;\n+            while (n) {\n+                size_t till_end_of_hw = hw->mix_buf.size - rpos2;\n+                size_t to_read = MIN(till_end_of_hw, n);\n+                size_t live, frames_in, frames_out;\n+\n+                sw->resample_buf.buffer = hw->mix_buf.buffer + rpos2;\n+                sw->resample_buf.size = to_read;\n+                live = sw->total_hw_samples_mixed;\n+\n+                audio_pcm_sw_resample_out(sw,\n+                                          to_read, sw->hw->mix_buf.size - live,\n+                                          &frames_in, &frames_out);\n+\n+                sw->total_hw_samples_mixed += frames_out;\n+                sw->empty = sw->total_hw_samples_mixed == 0;\n+\n+                if (to_read - frames_in) {\n+                    dolog(\"Could not mix %zu frames into a capture \"\n+                          \"buffer, mixed %zu\\n\",\n+                          to_read, frames_in);\n+                    break;\n+                }\n+                n -= to_read;\n+                rpos2 = (rpos2 + to_read) % hw->mix_buf.size;\n+            }\n+        }\n+    }\n+\n+    n = MIN(samples, hw->mix_buf.size - rpos);\n+    mixeng_clear(hw->mix_buf.buffer + rpos, n);\n+    mixeng_clear(hw->mix_buf.buffer, samples - n);\n+}\n+\n+static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)\n+{\n+    size_t clipped = 0;\n+\n+    while (live) {\n+        size_t size = live * hw->info.bytes_per_frame;\n+        size_t decr, proc;\n+        void *buf = hw->pcm_ops->get_buffer_out(hw, &size);\n+\n+        if (size == 0) {\n+            break;\n+        }\n+\n+        decr = MIN(size / hw->info.bytes_per_frame, live);\n+        if (buf) {\n+            audio_pcm_hw_clip_out(hw, buf, decr);\n+        }\n+        proc = hw->pcm_ops->put_buffer_out(hw, buf,\n+                                           decr * hw->info.bytes_per_frame) /\n+            hw->info.bytes_per_frame;\n+\n+        live -= proc;\n+        clipped += proc;\n+        hw->mix_buf.pos = (hw->mix_buf.pos + proc) % hw->mix_buf.size;\n+\n+        if (proc == 0 || proc < decr) {\n+            break;\n+        }\n+    }\n+\n+    if (hw->pcm_ops->run_buffer_out) {\n+        hw->pcm_ops->run_buffer_out(hw);\n+    }\n+\n+    return clipped;\n+}\n+\n+static void audio_run_out(AudioMixengBackend *s)\n+{\n+    HWVoiceOut *hw = NULL;\n+    SWVoiceOut *sw;\n+\n+    while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {\n+        size_t played, live, prev_rpos;\n+        size_t hw_free = audio_pcm_hw_get_free(hw);\n+        int nb_live;\n+\n+        if (!audio_get_pdo_out(s->dev)->mixing_engine) {\n+            /* there is exactly 1 sw for each hw with no mixeng */\n+            sw = hw->sw_head.lh_first;\n+\n+            if (hw->pending_disable) {\n+                hw->enabled = false;\n+                hw->pending_disable = false;\n+                if (hw->pcm_ops->enable_out) {\n+                    hw->pcm_ops->enable_out(hw, false);\n+                }\n+            }\n+\n+            if (sw->active) {\n+                sw->callback.fn(sw->callback.opaque,\n+                                hw_free * sw->info.bytes_per_frame);\n+            }\n+\n+            if (hw->pcm_ops->run_buffer_out) {\n+                hw->pcm_ops->run_buffer_out(hw);\n+            }\n+\n+            continue;\n+        }\n+\n+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+            if (sw->active) {\n+                size_t sw_free = audio_get_free(sw);\n+                size_t free;\n+\n+                if (hw_free > sw->total_hw_samples_mixed) {\n+                    free = st_rate_frames_in(sw->rate,\n+                        MIN(sw_free, hw_free - sw->total_hw_samples_mixed));\n+                } else {\n+                    free = 0;\n+                }\n+                if (free > sw->resample_buf.pos) {\n+                    free = MIN(free, sw->resample_buf.size)\n+                           - sw->resample_buf.pos;\n+                    sw->callback.fn(sw->callback.opaque,\n+                                    free * sw->info.bytes_per_frame);\n+                }\n+            }\n+        }\n+\n+        live = audio_pcm_hw_get_live_out (hw, &nb_live);\n+        if (!nb_live) {\n+            live = 0;\n+        }\n+\n+        if (audio_bug(__func__, live > hw->mix_buf.size)) {\n+            dolog(\"live=%zu hw->mix_buf.size=%zu\\n\", live, hw->mix_buf.size);\n+            continue;\n+        }\n+\n+        if (hw->pending_disable && !nb_live) {\n+            SWVoiceCap *sc;\n+#ifdef DEBUG_OUT\n+            dolog (\"Disabling voice\\n\");\n+#endif\n+            hw->enabled = false;\n+            hw->pending_disable = false;\n+            if (hw->pcm_ops->enable_out) {\n+                hw->pcm_ops->enable_out(hw, false);\n+            }\n+            for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n+                sc->sw.active = false;\n+                audio_recalc_and_notify_capture (sc->cap);\n+            }\n+            continue;\n+        }\n+\n+        if (!live) {\n+            if (hw->pcm_ops->run_buffer_out) {\n+                hw->pcm_ops->run_buffer_out(hw);\n+            }\n+            continue;\n+        }\n+\n+        prev_rpos = hw->mix_buf.pos;\n+        played = audio_pcm_hw_run_out(hw, live);\n+        replay_audio_out(&played);\n+        if (audio_bug(__func__, hw->mix_buf.pos >= hw->mix_buf.size)) {\n+            dolog(\"hw->mix_buf.pos=%zu hw->mix_buf.size=%zu played=%zu\\n\",\n+                  hw->mix_buf.pos, hw->mix_buf.size, played);\n+            hw->mix_buf.pos = 0;\n+        }\n+\n+#ifdef DEBUG_OUT\n+        dolog(\"played=%zu\\n\", played);\n+#endif\n+\n+        if (played) {\n+            audio_capture_mix_and_clear (hw, prev_rpos, played);\n+        }\n+\n+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+            if (!sw->active && sw->empty) {\n+                continue;\n+            }\n+\n+            if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) {\n+                dolog(\"played=%zu sw->total_hw_samples_mixed=%zu\\n\",\n+                      played, sw->total_hw_samples_mixed);\n+                played = sw->total_hw_samples_mixed;\n+            }\n+\n+            sw->total_hw_samples_mixed -= played;\n+\n+            if (!sw->total_hw_samples_mixed) {\n+                sw->empty = true;\n+            }\n+        }\n+    }\n+}\n+\n+static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)\n+{\n+    size_t conv = 0;\n+\n+    if (hw->pcm_ops->run_buffer_in) {\n+        hw->pcm_ops->run_buffer_in(hw);\n+    }\n+\n+    while (samples) {\n+        size_t proc;\n+        size_t size = samples * hw->info.bytes_per_frame;\n+        void *buf = hw->pcm_ops->get_buffer_in(hw, &size);\n+\n+        assert(size % hw->info.bytes_per_frame == 0);\n+        if (size == 0) {\n+            break;\n+        }\n+\n+        proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);\n+\n+        samples -= proc;\n+        conv += proc;\n+        hw->pcm_ops->put_buffer_in(hw, buf, proc * hw->info.bytes_per_frame);\n+    }\n+\n+    return conv;\n+}\n+\n+static void audio_run_in(AudioMixengBackend *s)\n+{\n+    HWVoiceIn *hw = NULL;\n+\n+    if (!audio_get_pdo_in(s->dev)->mixing_engine) {\n+        while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {\n+            /* there is exactly 1 sw for each hw with no mixeng */\n+            SWVoiceIn *sw = hw->sw_head.lh_first;\n+            if (sw->active) {\n+                sw->callback.fn(sw->callback.opaque, INT_MAX);\n+            }\n+        }\n+        return;\n+    }\n+\n+    while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {\n+        SWVoiceIn *sw;\n+        size_t captured = 0, min;\n+        int pos;\n+\n+        if (replay_mode != REPLAY_MODE_PLAY) {\n+            captured = audio_pcm_hw_run_in(\n+                hw, hw->conv_buf.size - audio_pcm_hw_get_live_in(hw));\n+        }\n+\n+        replay_audio_in_start(&captured);\n+        assert(captured <= hw->conv_buf.size);\n+        if (replay_mode == REPLAY_MODE_PLAY) {\n+            hw->conv_buf.pos = (hw->conv_buf.pos + captured) % hw->conv_buf.size;\n+        }\n+        for (pos = (hw->conv_buf.pos - captured + hw->conv_buf.size) % hw->conv_buf.size;\n+             pos != hw->conv_buf.pos;\n+             pos = (pos + 1) % hw->conv_buf.size) {\n+                uint64_t left, right;\n+\n+                if (replay_mode == REPLAY_MODE_RECORD) {\n+                    audio_sample_to_uint64(hw->conv_buf.buffer, pos, &left, &right);\n+                }\n+                replay_audio_in_sample_lr(&left, &right);\n+                if (replay_mode == REPLAY_MODE_PLAY) {\n+                    audio_sample_from_uint64(hw->conv_buf.buffer, pos, left, right);\n+                }\n+        }\n+        replay_audio_in_finish();\n+\n+        min = audio_pcm_hw_find_min_in (hw);\n+        hw->total_samples_captured += captured - min;\n+\n+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+            sw->total_hw_samples_acquired -= min;\n+\n+            if (sw->active) {\n+                size_t sw_avail = audio_get_avail(sw);\n+                size_t avail;\n+\n+                avail = st_rate_frames_out(sw->rate, sw_avail);\n+                if (avail > 0) {\n+                    avail = MIN(avail, sw->resample_buf.size);\n+                    sw->callback.fn(sw->callback.opaque,\n+                                    avail * sw->info.bytes_per_frame);\n+                }\n+            }\n+        }\n+    }\n+}\n+\n+static void audio_run_capture(AudioMixengBackend *s)\n+{\n+    CaptureVoiceOut *cap;\n+\n+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {\n+        size_t live, rpos, captured;\n+        HWVoiceOut *hw = &cap->hw;\n+        SWVoiceOut *sw;\n+\n+        captured = live = audio_pcm_hw_get_live_out (hw, NULL);\n+        rpos = hw->mix_buf.pos;\n+        while (live) {\n+            size_t left = hw->mix_buf.size - rpos;\n+            size_t to_capture = MIN(live, left);\n+            struct st_sample *src;\n+            struct capture_callback *cb;\n+\n+            src = hw->mix_buf.buffer + rpos;\n+            hw->clip (cap->buf, src, to_capture);\n+            mixeng_clear (src, to_capture);\n+\n+            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n+                cb->ops.capture (cb->opaque, cap->buf,\n+                                 to_capture * hw->info.bytes_per_frame);\n+            }\n+            rpos = (rpos + to_capture) % hw->mix_buf.size;\n+            live -= to_capture;\n+        }\n+        hw->mix_buf.pos = rpos;\n+\n+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n+            if (!sw->active && sw->empty) {\n+                continue;\n+            }\n+\n+            if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) {\n+                dolog(\"captured=%zu sw->total_hw_samples_mixed=%zu\\n\",\n+                      captured, sw->total_hw_samples_mixed);\n+                captured = sw->total_hw_samples_mixed;\n+            }\n+\n+            sw->total_hw_samples_mixed -= captured;\n+            sw->empty = sw->total_hw_samples_mixed == 0;\n+        }\n+    }\n+}\n+\n+void audio_run(AudioMixengBackend *s, const char *msg)\n+{\n+    audio_run_out(s);\n+    audio_run_in(s);\n+    audio_run_capture(s);\n+\n+#ifdef DEBUG_POLL\n+    {\n+        static double prevtime;\n+        double currtime;\n+        struct timeval tv;\n+\n+        if (gettimeofday (&tv, NULL)) {\n+            perror (\"audio_run: gettimeofday\");\n+            return;\n+        }\n+\n+        currtime = tv.tv_sec + tv.tv_usec * 1e-6;\n+        dolog (\"Elapsed since last %s: %f\\n\", msg, currtime - prevtime);\n+        prevtime = currtime;\n+    }\n+#endif\n+}\n+\n+void audio_generic_run_buffer_in(HWVoiceIn *hw)\n+{\n+    if (unlikely(!hw->buf_emul)) {\n+        hw->size_emul = hw->samples * hw->info.bytes_per_frame;\n+        hw->buf_emul = g_malloc(hw->size_emul);\n+        hw->pos_emul = hw->pending_emul = 0;\n+    }\n+\n+    while (hw->pending_emul < hw->size_emul) {\n+        size_t read_len = MIN(hw->size_emul - hw->pos_emul,\n+                              hw->size_emul - hw->pending_emul);\n+        size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,\n+                                        read_len);\n+        hw->pending_emul += read;\n+        hw->pos_emul = (hw->pos_emul + read) % hw->size_emul;\n+        if (read < read_len) {\n+            break;\n+        }\n+    }\n+}\n+\n+void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)\n+{\n+    size_t start;\n+\n+    start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);\n+    assert(start < hw->size_emul);\n+\n+    *size = MIN(*size, hw->pending_emul);\n+    *size = MIN(*size, hw->size_emul - start);\n+    return hw->buf_emul + start;\n+}\n+\n+void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)\n+{\n+    assert(size <= hw->pending_emul);\n+    hw->pending_emul -= size;\n+}\n+\n+size_t audio_generic_buffer_get_free(HWVoiceOut *hw)\n+{\n+    if (hw->buf_emul) {\n+        return hw->size_emul - hw->pending_emul;\n+    } else {\n+        return hw->samples * hw->info.bytes_per_frame;\n+    }\n+}\n+\n+void audio_generic_run_buffer_out(HWVoiceOut *hw)\n+{\n+    while (hw->pending_emul) {\n+        size_t write_len, written, start;\n+\n+        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);\n+        assert(start < hw->size_emul);\n+\n+        write_len = MIN(hw->pending_emul, hw->size_emul - start);\n+\n+        written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);\n+        hw->pending_emul -= written;\n+\n+        if (written < write_len) {\n+            break;\n+        }\n+    }\n+}\n+\n+void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)\n+{\n+    if (unlikely(!hw->buf_emul)) {\n+        hw->size_emul = hw->samples * hw->info.bytes_per_frame;\n+        hw->buf_emul = g_malloc(hw->size_emul);\n+        hw->pos_emul = hw->pending_emul = 0;\n+    }\n+\n+    *size = MIN(hw->size_emul - hw->pending_emul,\n+                hw->size_emul - hw->pos_emul);\n+    return hw->buf_emul + hw->pos_emul;\n+}\n+\n+size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)\n+{\n+    assert(buf == hw->buf_emul + hw->pos_emul &&\n+           size + hw->pending_emul <= hw->size_emul);\n+\n+    hw->pending_emul += size;\n+    hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;\n+\n+    return size;\n+}\n+\n+size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)\n+{\n+    size_t total = 0;\n+\n+    if (hw->pcm_ops->buffer_get_free) {\n+        size_t free = hw->pcm_ops->buffer_get_free(hw);\n+\n+        size = MIN(size, free);\n+    }\n+\n+    while (total < size) {\n+        size_t dst_size = size - total;\n+        size_t copy_size, proc;\n+        void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);\n+\n+        if (dst_size == 0) {\n+            break;\n+        }\n+\n+        copy_size = MIN(size - total, dst_size);\n+        if (dst) {\n+            memcpy(dst, (char *)buf + total, copy_size);\n+        }\n+        proc = hw->pcm_ops->put_buffer_out(hw, dst, copy_size);\n+        total += proc;\n+\n+        if (proc == 0 || proc < copy_size) {\n+            break;\n+        }\n+    }\n+\n+    return total;\n+}\n+\n+size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)\n+{\n+    size_t total = 0;\n+\n+    if (hw->pcm_ops->run_buffer_in) {\n+        hw->pcm_ops->run_buffer_in(hw);\n+    }\n+\n+    while (total < size) {\n+        size_t src_size = size - total;\n+        void *src = hw->pcm_ops->get_buffer_in(hw, &src_size);\n+\n+        if (src_size == 0) {\n+            break;\n+        }\n+\n+        memcpy((char *)buf + total, src, src_size);\n+        hw->pcm_ops->put_buffer_in(hw, src, src_size);\n+        total += src_size;\n+    }\n+\n+    return total;\n+}\n+\n+static bool audio_mixeng_backend_realize(AudioBackend *abe,\n+                                         Audiodev *dev, Error **errp)\n+{\n+    AudioMixengBackend *be = AUDIO_MIXENG_BACKEND(abe);\n+    audio_driver *drv = AUDIO_MIXENG_BACKEND_GET_CLASS(be)->driver;\n+\n+    be->dev = dev;\n+    be->drv_opaque = drv->init(be->dev, errp);\n+    if (!be->drv_opaque) {\n+        return false;\n+    }\n+\n+    if (!drv->pcm_ops->get_buffer_in) {\n+        drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;\n+        drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;\n+    }\n+    if (!drv->pcm_ops->get_buffer_out) {\n+        drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;\n+        drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;\n+    }\n+\n+    audio_init_nb_voices_out(be, drv, 1);\n+    audio_init_nb_voices_in(be, drv, 0);\n+    be->drv = drv;\n+\n+    if (be->dev->timer_period <= 0) {\n+        be->period_ticks = 1;\n+    } else {\n+        be->period_ticks = be->dev->timer_period * (int64_t)SCALE_US;\n+    }\n+\n+    return true;\n+}\n+\n+static void audio_vm_change_state_handler (void *opaque, bool running,\n+                                           RunState state)\n+{\n+    AudioMixengBackend *s = opaque;\n+    HWVoiceOut *hwo = NULL;\n+    HWVoiceIn *hwi = NULL;\n+\n+    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {\n+        if (hwo->pcm_ops->enable_out) {\n+            hwo->pcm_ops->enable_out(hwo, running);\n+        }\n+    }\n+\n+    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {\n+        if (hwi->pcm_ops->enable_in) {\n+            hwi->pcm_ops->enable_in(hwi, running);\n+        }\n+    }\n+    audio_reset_timer (s);\n+}\n+\n+static const VMStateDescription vmstate_audio;\n+\n+static const char *audio_mixeng_backend_get_id(AudioBackend *be)\n+{\n+    return AUDIO_MIXENG_BACKEND(be)->dev->id;\n+}\n+\n+static CaptureVoiceOut *audio_mixeng_backend_add_capture(\n+    AudioBackend *be,\n+    struct audsettings *as,\n+    struct audio_capture_ops *ops,\n+    void *cb_opaque);\n+\n+static void audio_mixeng_backend_del_capture(\n+    AudioBackend *be,\n+    CaptureVoiceOut *cap,\n+    void *cb_opaque);\n+\n+static void audio_mixeng_backend_set_volume_out(AudioBackend *be, SWVoiceOut *sw,\n+                                                Volume *vol);\n+static void audio_mixeng_backend_set_volume_in(AudioBackend *be, SWVoiceIn *sw,\n+                                               Volume *vol);\n+\n+static void audio_mixeng_backend_class_init(ObjectClass *klass, const void *data)\n+{\n+    AudioBackendClass *be = AUDIO_BACKEND_CLASS(klass);\n+\n+    be->realize = audio_mixeng_backend_realize;\n+    be->get_id = audio_mixeng_backend_get_id;\n+    be->open_in = audio_mixeng_backend_open_in;\n+    be->open_out = audio_mixeng_backend_open_out;\n+    be->close_in = audio_mixeng_backend_close_in;\n+    be->close_out = audio_mixeng_backend_close_out;\n+    be->is_active_out = audio_mixeng_backend_is_active_out;\n+    be->is_active_in = audio_mixeng_backend_is_active_in;\n+    be->set_active_out = audio_mixeng_backend_set_active_out;\n+    be->set_active_in = audio_mixeng_backend_set_active_in;\n+    be->set_volume_out = audio_mixeng_backend_set_volume_out;\n+    be->set_volume_in = audio_mixeng_backend_set_volume_in;\n+    be->read = audio_mixeng_backend_read;\n+    be->write = audio_mixeng_backend_write;\n+    be->get_buffer_size_out = audio_mixeng_backend_get_buffer_size_out;\n+    be->add_capture = audio_mixeng_backend_add_capture;\n+    be->del_capture = audio_mixeng_backend_del_capture;\n+}\n+\n+static void audio_mixeng_backend_init(Object *obj)\n+{\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(obj);\n+\n+    QLIST_INIT(&s->hw_head_out);\n+    QLIST_INIT(&s->hw_head_in);\n+    QLIST_INIT(&s->cap_head);\n+    s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);\n+\n+    s->vmse = qemu_add_vm_change_state_handler(audio_vm_change_state_handler, s);\n+    assert(s->vmse != NULL);\n+\n+    vmstate_register_any(NULL, &vmstate_audio, s);\n+}\n+\n+static void audio_mixeng_backend_finalize(Object *obj)\n+{\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(obj);\n+    HWVoiceOut *hwo, *hwon;\n+    HWVoiceIn *hwi, *hwin;\n+\n+    QLIST_FOREACH_SAFE(hwo, &s->hw_head_out, entries, hwon) {\n+        SWVoiceCap *sc;\n+\n+        if (hwo->enabled && hwo->pcm_ops->enable_out) {\n+            hwo->pcm_ops->enable_out(hwo, false);\n+        }\n+        hwo->pcm_ops->fini_out (hwo);\n+\n+        for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n+            CaptureVoiceOut *cap = sc->cap;\n+            struct capture_callback *cb;\n+\n+            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n+                cb->ops.destroy (cb->opaque);\n+            }\n+        }\n+        QLIST_REMOVE(hwo, entries);\n+    }\n+\n+    QLIST_FOREACH_SAFE(hwi, &s->hw_head_in, entries, hwin) {\n+        if (hwi->enabled && hwi->pcm_ops->enable_in) {\n+            hwi->pcm_ops->enable_in(hwi, false);\n+        }\n+        hwi->pcm_ops->fini_in (hwi);\n+        QLIST_REMOVE(hwi, entries);\n+    }\n+\n+    if (s->drv) {\n+        s->drv->fini (s->drv_opaque);\n+        s->drv = NULL;\n+    }\n+\n+    if (s->dev) {\n+        qapi_free_Audiodev(s->dev);\n+        s->dev = NULL;\n+    }\n+\n+    if (s->ts) {\n+        timer_free(s->ts);\n+        s->ts = NULL;\n+    }\n+\n+    if (s->vmse) {\n+        qemu_del_vm_change_state_handler(s->vmse);\n+        s->vmse = NULL;\n+    }\n+\n+    vmstate_unregister(NULL, &vmstate_audio, s);\n+}\n+\n+static bool vmstate_audio_needed(void *opaque)\n+{\n+    /*\n+     * Never needed, this vmstate only exists in case\n+     * an old qemu sends it to us.\n+     */\n+    return false;\n+}\n+\n+static const VMStateDescription vmstate_audio = {\n+    .name = \"audio\",\n+    .version_id = 1,\n+    .minimum_version_id = 1,\n+    .needed = vmstate_audio_needed,\n+    .fields = (const VMStateField[]) {\n+        VMSTATE_END_OF_LIST()\n+    }\n+};\n+\n+static struct audio_pcm_ops capture_pcm_ops;\n+\n+static CaptureVoiceOut *audio_mixeng_backend_add_capture(\n+    AudioBackend *be,\n+    struct audsettings *as,\n+    struct audio_capture_ops *ops,\n+    void *cb_opaque)\n+{\n+    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(be);\n+    CaptureVoiceOut *cap;\n+    struct capture_callback *cb;\n+\n+    if (!s) {\n+        error_report(\"Capturing without setting an audiodev is not supported\");\n+        abort();\n+    }\n+\n+    if (!audio_get_pdo_out(s->dev)->mixing_engine) {\n+        dolog(\"Can't capture with mixeng disabled\\n\");\n+        return NULL;\n+    }\n+\n+    if (audio_validate_settings (as)) {\n+        dolog (\"Invalid settings were passed when trying to add capture\\n\");\n+        audio_print_settings (as);\n+        return NULL;\n+    }\n+\n+    cb = g_malloc0(sizeof(*cb));\n+    cb->ops = *ops;\n+    cb->opaque = cb_opaque;\n+\n+    cap = audio_pcm_capture_find_specific(s, as);\n+    if (cap) {\n+        QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);\n+    } else {\n+        HWVoiceOut *hw;\n+\n+        cap = g_malloc0(sizeof(*cap));\n+\n+        hw = &cap->hw;\n+        hw->s = s;\n+        hw->pcm_ops = &capture_pcm_ops;\n+        QLIST_INIT (&hw->sw_head);\n+        QLIST_INIT (&cap->cb_head);\n+\n+        /* XXX find a more elegant way */\n+        hw->samples = 4096 * 4;\n+        audio_pcm_hw_alloc_resources_out(hw);\n+\n+        audio_pcm_init_info (&hw->info, as);\n+\n+        cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame);\n+\n+        if (hw->info.is_float) {\n+            hw->clip = mixeng_clip_float[hw->info.nchannels == 2]\n+                [hw->info.swap_endianness];\n+        } else {\n+            hw->clip = mixeng_clip\n+                [hw->info.nchannels == 2]\n+                [hw->info.is_signed]\n+                [hw->info.swap_endianness]\n+                [audio_bits_to_index(hw->info.bits)];\n+        }\n+\n+        QLIST_INSERT_HEAD (&s->cap_head, cap, entries);\n+        QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);\n+\n+        QLIST_FOREACH(hw, &s->hw_head_out, entries) {\n+            audio_attach_capture (hw);\n+        }\n+    }\n+\n+    return cap;\n+}\n+\n+static void audio_mixeng_backend_del_capture(\n+    AudioBackend *be,\n+    CaptureVoiceOut *cap,\n+    void *cb_opaque)\n+{\n+    struct capture_callback *cb;\n+\n+    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n+        if (cb->opaque == cb_opaque) {\n+            cb->ops.destroy (cb_opaque);\n+            QLIST_REMOVE (cb, entries);\n+            g_free (cb);\n+\n+            if (!cap->cb_head.lh_first) {\n+                SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;\n+\n+                while (sw) {\n+                    SWVoiceCap *sc = (SWVoiceCap *) sw;\n+#ifdef DEBUG_CAPTURE\n+                    dolog (\"freeing %s\\n\", sw->name);\n+#endif\n+\n+                    sw1 = sw->entries.le_next;\n+                    if (sw->rate) {\n+                        st_rate_stop (sw->rate);\n+                        sw->rate = NULL;\n+                    }\n+                    QLIST_REMOVE (sw, entries);\n+                    QLIST_REMOVE (sc, entries);\n+                    g_free (sc);\n+                    sw = sw1;\n+                }\n+                QLIST_REMOVE (cap, entries);\n+                g_free(cap->hw.mix_buf.buffer);\n+                g_free (cap->buf);\n+                g_free (cap);\n+            }\n+            return;\n+        }\n+    }\n+}\n+\n+static void audio_mixeng_backend_set_volume_out(AudioBackend *be, SWVoiceOut *sw,\n+                                                Volume *vol)\n+{\n+    if (sw) {\n+        HWVoiceOut *hw = sw->hw;\n+\n+        sw->vol.mute = vol->mute;\n+        sw->vol.l = nominal_volume.l * vol->vol[0] / 255;\n+        sw->vol.r = nominal_volume.l * vol->vol[vol->channels > 1 ? 1 : 0] /\n+            255;\n+\n+        if (hw->pcm_ops->volume_out) {\n+            hw->pcm_ops->volume_out(hw, vol);\n+        }\n+    }\n+}\n+\n+static void audio_mixeng_backend_set_volume_in(AudioBackend *be, SWVoiceIn *sw,\n+                                               Volume *vol)\n+{\n+    if (sw) {\n+        HWVoiceIn *hw = sw->hw;\n+\n+        sw->vol.mute = vol->mute;\n+        sw->vol.l = nominal_volume.l * vol->vol[0] / 255;\n+        sw->vol.r = nominal_volume.r * vol->vol[vol->channels > 1 ? 1 : 0] /\n+            255;\n+\n+        if (hw->pcm_ops->volume_in) {\n+            hw->pcm_ops->volume_in(hw, vol);\n+        }\n+    }\n+}\n+\n+audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)\n+{\n+    return (audsettings) {\n+        .freq = pdo->frequency,\n+        .nchannels = pdo->channels,\n+        .fmt = pdo->format,\n+        .endianness = HOST_BIG_ENDIAN,\n+    };\n+}\n+\n+int audioformat_bytes_per_sample(AudioFormat fmt)\n+{\n+    switch (fmt) {\n+    case AUDIO_FORMAT_U8:\n+    case AUDIO_FORMAT_S8:\n+        return 1;\n+\n+    case AUDIO_FORMAT_U16:\n+    case AUDIO_FORMAT_S16:\n+        return 2;\n+\n+    case AUDIO_FORMAT_U32:\n+    case AUDIO_FORMAT_S32:\n+    case AUDIO_FORMAT_F32:\n+        return 4;\n+\n+    case AUDIO_FORMAT__MAX:\n+        ;\n+    }\n+    abort();\n+}\n+\n+\n+/* frames = freq * usec / 1e6 */\n+int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,\n+                        audsettings *as, int def_usecs)\n+{\n+    uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs;\n+    return (as->freq * usecs + 500000) / 1000000;\n+}\n+\n+/* samples = channels * frames = channels * freq * usec / 1e6 */\n+int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,\n+                         audsettings *as, int def_usecs)\n+{\n+    return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);\n+}\n+\n+/*\n+ * bytes = bytes_per_sample * samples =\n+ *     bytes_per_sample * channels * freq * usec / 1e6\n+ */\n+int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,\n+                       audsettings *as, int def_usecs)\n+{\n+    return audio_buffer_samples(pdo, as, def_usecs) *\n+        audioformat_bytes_per_sample(as->fmt);\n+}\n+\n+void audio_rate_start(RateCtl *rate)\n+{\n+    memset(rate, 0, sizeof(RateCtl));\n+    rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n+}\n+\n+size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)\n+{\n+    int64_t now;\n+    int64_t ticks;\n+    int64_t bytes;\n+    int64_t frames;\n+\n+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n+    ticks = now - rate->start_ticks;\n+    bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);\n+    frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;\n+    rate->peeked_frames = frames;\n+\n+    return frames < 0 ? 0 : frames * info->bytes_per_frame;\n+}\n+\n+void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)\n+{\n+    if (rate->peeked_frames < 0 || rate->peeked_frames > 65536) {\n+        AUD_log(NULL, \"Resetting rate control (%\" PRId64 \" frames)\\n\",\n+                rate->peeked_frames);\n+        audio_rate_start(rate);\n+    }\n+\n+    rate->bytes_sent += bytes_used;\n+}\n+\n+size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,\n+                            size_t bytes_avail)\n+{\n+    size_t bytes;\n+\n+    bytes = audio_rate_peek_bytes(rate, info);\n+    bytes = MIN(bytes, bytes_avail);\n+    audio_rate_add_bytes(rate, bytes);\n+\n+    return bytes;\n+}\n+\n+static const TypeInfo audio_types[] = {\n+    {\n+        .name = TYPE_AUDIO_MIXENG_BACKEND,\n+        .parent = TYPE_AUDIO_BACKEND,\n+        .instance_size = sizeof(AudioMixengBackend),\n+        .instance_init = audio_mixeng_backend_init,\n+        .instance_finalize = audio_mixeng_backend_finalize,\n+        .class_size = sizeof(AudioMixengBackendClass),\n+        .class_init = audio_mixeng_backend_class_init,\n+    },\n+};\n+\n+DEFINE_TYPES(audio_types)\ndiff --git a/audio/audio.c b/audio/audio.c\nindex 22e55ac9758..6f10ee470ee 100644\n--- a/audio/audio.c\n+++ b/audio/audio.c\n@@ -1,1751 +1,55 @@\n-/*\n- * QEMU Audio subsystem\n- *\n- * Copyright (c) 2003-2005 Vassili Karpov (malc)\n- *\n- * Permission is hereby granted, free of charge, to any person obtaining a copy\n- * of this software and associated documentation files (the \"Software\"), to deal\n- * in the Software without restriction, including without limitation the rights\n- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n- * copies of the Software, and to permit persons to whom the Software is\n- * furnished to do so, subject to the following conditions:\n- *\n- * The above copyright notice and this permission notice shall be included in\n- * all copies or substantial portions of the Software.\n- *\n- * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n- * THE SOFTWARE.\n- */\n-\n-#include \"qemu/osdep.h\"\n-#include \"qemu/audio.h\"\n-#include \"migration/vmstate.h\"\n-#include \"qemu/timer.h\"\n-#include \"qapi/error.h\"\n-#include \"qapi/clone-visitor.h\"\n-#include \"qapi/qobject-input-visitor.h\"\n-#include \"qapi/qapi-visit-audio.h\"\n-#include \"qapi/qapi-commands-audio.h\"\n-#include \"qobject/qdict.h\"\n-#include \"qemu/bswap.h\"\n-#include \"qemu/error-report.h\"\n-#include \"qemu/log.h\"\n-#include \"qemu/module.h\"\n-#include \"qemu/help_option.h\"\n-#include \"qom/object.h\"\n-#include \"system/system.h\"\n-#include \"system/replay.h\"\n-#include \"system/runstate.h\"\n-#include \"trace.h\"\n-\n-#define AUDIO_CAP \"audio\"\n-#include \"audio_int.h\"\n-\n-/* #define DEBUG_LIVE */\n-/* #define DEBUG_OUT */\n-/* #define DEBUG_CAPTURE */\n-/* #define DEBUG_POLL */\n-\n-#define SW_NAME(sw) (sw)->name ? (sw)->name : \"unknown\"\n-\n-\n-/* Order of CONFIG_AUDIO_DRIVERS is import.\n-   The 1st one is the one used by default, that is the reason\n-    that we generate the list.\n-*/\n-const char *audio_prio_list[] = {\n-#ifdef CONFIG_GIO\n-    \"dbus\",\n-#endif\n-    \"spice\",\n-    CONFIG_AUDIO_DRIVERS\n-    \"none\",\n-    NULL\n-};\n-\n-typedef struct AudiodevListEntry {\n-    Audiodev *dev;\n-    QSIMPLEQ_ENTRY(AudiodevListEntry) next;\n-} AudiodevListEntry;\n-\n-typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;\n-\n-static AudiodevListHead audiodevs =\n-    QSIMPLEQ_HEAD_INITIALIZER(audiodevs);\n-static AudiodevListHead default_audiodevs =\n-    QSIMPLEQ_HEAD_INITIALIZER(default_audiodevs);\n-\n-static AudioBackendClass *audio_be_class_by_name(const char *name)\n-{\n-    g_autofree char *tname = g_strconcat(\"audio-\", name, NULL);\n-    ObjectClass *oc = module_object_class_by_name(tname);\n-\n-    if (!oc || !object_class_dynamic_cast(oc, TYPE_AUDIO_BACKEND)) {\n-        return NULL;\n-    }\n-\n-    return AUDIO_BACKEND_CLASS(oc);\n-}\n-\n-static AudioBackend *default_audio_be;\n-\n-const struct mixeng_volume nominal_volume = {\n-    .mute = 0,\n-#ifdef FLOAT_MIXENG\n-    .r = 1.0,\n-    .l = 1.0,\n-#else\n-    .r = 1ULL << 32,\n-    .l = 1ULL << 32,\n-#endif\n-};\n-\n-int audio_bug (const char *funcname, int cond)\n-{\n-    if (cond) {\n-        static int shown;\n-\n-        AUD_log (NULL, \"A bug was just triggered in %s\\n\", funcname);\n-        if (!shown) {\n-            shown = 1;\n-            AUD_log (NULL, \"Save all your work and restart without audio\\n\");\n-            AUD_log (NULL, \"I am sorry\\n\");\n-        }\n-        AUD_log (NULL, \"Context:\\n\");\n-    }\n-\n-    return cond;\n-}\n-\n-static inline int audio_bits_to_index (int bits)\n-{\n-    switch (bits) {\n-    case 8:\n-        return 0;\n-\n-    case 16:\n-        return 1;\n-\n-    case 32:\n-        return 2;\n-\n-    default:\n-        audio_bug (\"bits_to_index\", 1);\n-        AUD_log (NULL, \"invalid bits %d\\n\", bits);\n-        return 0;\n-    }\n-}\n-\n-void AUD_vlog (const char *cap, const char *fmt, va_list ap)\n-{\n-    if (cap) {\n-        fprintf(stderr, \"%s: \", cap);\n-    }\n-\n-    vfprintf(stderr, fmt, ap);\n-}\n-\n-void AUD_log (const char *cap, const char *fmt, ...)\n-{\n-    va_list ap;\n-\n-    va_start (ap, fmt);\n-    AUD_vlog (cap, fmt, ap);\n-    va_end (ap);\n-}\n-\n-static void audio_print_settings (const struct audsettings *as)\n-{\n-    dolog (\"frequency=%d nchannels=%d fmt=\", as->freq, as->nchannels);\n-\n-    switch (as->fmt) {\n-    case AUDIO_FORMAT_S8:\n-        AUD_log (NULL, \"S8\");\n-        break;\n-    case AUDIO_FORMAT_U8:\n-        AUD_log (NULL, \"U8\");\n-        break;\n-    case AUDIO_FORMAT_S16:\n-        AUD_log (NULL, \"S16\");\n-        break;\n-    case AUDIO_FORMAT_U16:\n-        AUD_log (NULL, \"U16\");\n-        break;\n-    case AUDIO_FORMAT_S32:\n-        AUD_log (NULL, \"S32\");\n-        break;\n-    case AUDIO_FORMAT_U32:\n-        AUD_log (NULL, \"U32\");\n-        break;\n-    case AUDIO_FORMAT_F32:\n-        AUD_log (NULL, \"F32\");\n-        break;\n-    default:\n-        AUD_log (NULL, \"invalid(%d)\", as->fmt);\n-        break;\n-    }\n-\n-    AUD_log (NULL, \" endianness=\");\n-    switch (as->endianness) {\n-    case 0:\n-        AUD_log (NULL, \"little\");\n-        break;\n-    case 1:\n-        AUD_log (NULL, \"big\");\n-        break;\n-    default:\n-        AUD_log (NULL, \"invalid\");\n-        break;\n-    }\n-    AUD_log (NULL, \"\\n\");\n-}\n-\n-static int audio_validate_settings (const struct audsettings *as)\n-{\n-    int invalid;\n-\n-    invalid = as->nchannels < 1;\n-    invalid |= as->endianness != 0 && as->endianness != 1;\n-\n-    switch (as->fmt) {\n-    case AUDIO_FORMAT_S8:\n-    case AUDIO_FORMAT_U8:\n-    case AUDIO_FORMAT_S16:\n-    case AUDIO_FORMAT_U16:\n-    case AUDIO_FORMAT_S32:\n-    case AUDIO_FORMAT_U32:\n-    case AUDIO_FORMAT_F32:\n-        break;\n-    default:\n-        invalid = 1;\n-        break;\n-    }\n-\n-    invalid |= as->freq <= 0;\n-    return invalid ? -1 : 0;\n-}\n-\n-static int audio_pcm_info_eq (struct audio_pcm_info *info, const struct audsettings *as)\n-{\n-    int bits = 8;\n-    bool is_signed = false, is_float = false;\n-\n-    switch (as->fmt) {\n-    case AUDIO_FORMAT_S8:\n-        is_signed = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_U8:\n-        break;\n-\n-    case AUDIO_FORMAT_S16:\n-        is_signed = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_U16:\n-        bits = 16;\n-        break;\n-\n-    case AUDIO_FORMAT_F32:\n-        is_float = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_S32:\n-        is_signed = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_U32:\n-        bits = 32;\n-        break;\n-\n-    default:\n-        abort();\n-    }\n-    return info->freq == as->freq\n-        && info->nchannels == as->nchannels\n-        && info->is_signed == is_signed\n-        && info->is_float == is_float\n-        && info->bits == bits\n-        && info->swap_endianness == (as->endianness != HOST_BIG_ENDIAN);\n-}\n-\n-void audio_pcm_init_info (struct audio_pcm_info *info, const struct audsettings *as)\n-{\n-    int bits = 8, mul;\n-    bool is_signed = false, is_float = false;\n-\n-    switch (as->fmt) {\n-    case AUDIO_FORMAT_S8:\n-        is_signed = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_U8:\n-        mul = 1;\n-        break;\n-\n-    case AUDIO_FORMAT_S16:\n-        is_signed = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_U16:\n-        bits = 16;\n-        mul = 2;\n-        break;\n-\n-    case AUDIO_FORMAT_F32:\n-        is_float = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_S32:\n-        is_signed = true;\n-        /* fall through */\n-    case AUDIO_FORMAT_U32:\n-        bits = 32;\n-        mul = 4;\n-        break;\n-\n-    default:\n-        abort();\n-    }\n-\n-    info->freq = as->freq;\n-    info->bits = bits;\n-    info->is_signed = is_signed;\n-    info->is_float = is_float;\n-    info->nchannels = as->nchannels;\n-    info->bytes_per_frame = as->nchannels * mul;\n-    info->bytes_per_second = info->freq * info->bytes_per_frame;\n-    info->swap_endianness = (as->endianness != HOST_BIG_ENDIAN);\n-}\n-\n-void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)\n-{\n-    if (!len) {\n-        return;\n-    }\n-\n-    if (info->is_signed || info->is_float) {\n-        memset(buf, 0x00, len * info->bytes_per_frame);\n-    } else {\n-        switch (info->bits) {\n-        case 8:\n-            memset(buf, 0x80, len * info->bytes_per_frame);\n-            break;\n-\n-        case 16:\n-            {\n-                int i;\n-                uint16_t *p = buf;\n-                short s = INT16_MAX;\n-\n-                if (info->swap_endianness) {\n-                    s = bswap16 (s);\n-                }\n-\n-                for (i = 0; i < len * info->nchannels; i++) {\n-                    p[i] = s;\n-                }\n-            }\n-            break;\n-\n-        case 32:\n-            {\n-                int i;\n-                uint32_t *p = buf;\n-                int32_t s = INT32_MAX;\n-\n-                if (info->swap_endianness) {\n-                    s = bswap32 (s);\n-                }\n-\n-                for (i = 0; i < len * info->nchannels; i++) {\n-                    p[i] = s;\n-                }\n-            }\n-            break;\n-\n-        default:\n-            AUD_log (NULL, \"audio_pcm_info_clear_buf: invalid bits %d\\n\",\n-                     info->bits);\n-            break;\n-        }\n-    }\n-}\n-\n-/*\n- * Capture\n- */\n-static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioMixengBackend *s,\n-                                                        struct audsettings *as)\n-{\n-    CaptureVoiceOut *cap;\n-\n-    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {\n-        if (audio_pcm_info_eq (&cap->hw.info, as)) {\n-            return cap;\n-        }\n-    }\n-    return NULL;\n-}\n-\n-static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)\n-{\n-    struct capture_callback *cb;\n-\n-#ifdef DEBUG_CAPTURE\n-    dolog (\"notification %d sent\\n\", cmd);\n-#endif\n-    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n-        cb->ops.notify (cb->opaque, cmd);\n-    }\n-}\n-\n-static void audio_capture_maybe_changed(CaptureVoiceOut *cap, bool enabled)\n-{\n-    if (cap->hw.enabled != enabled) {\n-        audcnotification_e cmd;\n-        cap->hw.enabled = enabled;\n-        cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;\n-        audio_notify_capture (cap, cmd);\n-    }\n-}\n-\n-static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)\n-{\n-    HWVoiceOut *hw = &cap->hw;\n-    SWVoiceOut *sw;\n-    bool enabled = false;\n-\n-    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-        if (sw->active) {\n-            enabled = true;\n-            break;\n-        }\n-    }\n-    audio_capture_maybe_changed (cap, enabled);\n-}\n-\n-static void audio_detach_capture (HWVoiceOut *hw)\n-{\n-    SWVoiceCap *sc = hw->cap_head.lh_first;\n-\n-    while (sc) {\n-        SWVoiceCap *sc1 = sc->entries.le_next;\n-        SWVoiceOut *sw = &sc->sw;\n-        CaptureVoiceOut *cap = sc->cap;\n-        int was_active = sw->active;\n-\n-        if (sw->rate) {\n-            st_rate_stop (sw->rate);\n-            sw->rate = NULL;\n-        }\n-\n-        QLIST_REMOVE (sw, entries);\n-        QLIST_REMOVE (sc, entries);\n-        g_free (sc);\n-        if (was_active) {\n-            /* We have removed soft voice from the capture:\n-               this might have changed the overall status of the capture\n-               since this might have been the only active voice */\n-            audio_recalc_and_notify_capture (cap);\n-        }\n-        sc = sc1;\n-    }\n-}\n-\n-static int audio_attach_capture (HWVoiceOut *hw)\n-{\n-    AudioMixengBackend *s = hw->s;\n-    CaptureVoiceOut *cap;\n-\n-    audio_detach_capture (hw);\n-    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {\n-        SWVoiceCap *sc;\n-        SWVoiceOut *sw;\n-        HWVoiceOut *hw_cap = &cap->hw;\n-\n-        sc = g_malloc0(sizeof(*sc));\n-\n-        sc->cap = cap;\n-        sw = &sc->sw;\n-        sw->hw = hw_cap;\n-        sw->info = hw->info;\n-        sw->empty = true;\n-        sw->active = hw->enabled;\n-        sw->vol = nominal_volume;\n-        sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);\n-        QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);\n-        QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);\n-#ifdef DEBUG_CAPTURE\n-        sw->name = g_strdup_printf (\"for %p %d,%d,%d\",\n-                                    hw, sw->info.freq, sw->info.bits,\n-                                    sw->info.nchannels);\n-        dolog (\"Added %s active = %d\\n\", sw->name, sw->active);\n-#endif\n-        if (sw->active) {\n-            audio_capture_maybe_changed (cap, 1);\n-        }\n-    }\n-    return 0;\n-}\n-\n-/*\n- * Hard voice (capture)\n- */\n-static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)\n-{\n-    SWVoiceIn *sw;\n-    size_t m = hw->total_samples_captured;\n-\n-    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-        if (sw->active) {\n-            m = MIN (m, sw->total_hw_samples_acquired);\n-        }\n-    }\n-    return m;\n-}\n-\n-static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)\n-{\n-    size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);\n-    if (audio_bug(__func__, live > hw->conv_buf.size)) {\n-        dolog(\"live=%zu hw->conv_buf.size=%zu\\n\", live, hw->conv_buf.size);\n-        return 0;\n-    }\n-    return live;\n-}\n-\n-static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)\n-{\n-    size_t conv = 0;\n-    STSampleBuffer *conv_buf = &hw->conv_buf;\n-\n-    while (samples) {\n-        uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);\n-        size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);\n-\n-        hw->conv(conv_buf->buffer + conv_buf->pos, src, proc);\n-        conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;\n-        samples -= proc;\n-        conv += proc;\n-    }\n-\n-    return conv;\n-}\n-\n-/*\n- * Soft voice (capture)\n- */\n-static void audio_pcm_sw_resample_in(SWVoiceIn *sw,\n-    size_t frames_in_max, size_t frames_out_max,\n-    size_t *total_in, size_t *total_out)\n-{\n-    HWVoiceIn *hw = sw->hw;\n-    struct st_sample *src, *dst;\n-    size_t live, rpos, frames_in, frames_out;\n-\n-    live = hw->total_samples_captured - sw->total_hw_samples_acquired;\n-    rpos = audio_ring_posb(hw->conv_buf.pos, live, hw->conv_buf.size);\n-\n-    /* resample conv_buf from rpos to end of buffer */\n-    src = hw->conv_buf.buffer + rpos;\n-    frames_in = MIN(frames_in_max, hw->conv_buf.size - rpos);\n-    dst = sw->resample_buf.buffer;\n-    frames_out = frames_out_max;\n-    st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);\n-    rpos += frames_in;\n-    *total_in = frames_in;\n-    *total_out = frames_out;\n-\n-    /* resample conv_buf from start of buffer if there are input frames left */\n-    if (frames_in_max - frames_in && rpos == hw->conv_buf.size) {\n-        src = hw->conv_buf.buffer;\n-        frames_in = frames_in_max - frames_in;\n-        dst += frames_out;\n-        frames_out = frames_out_max - frames_out;\n-        st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);\n-        *total_in += frames_in;\n-        *total_out += frames_out;\n-    }\n-}\n-\n-static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t buf_len)\n-{\n-    HWVoiceIn *hw = sw->hw;\n-    size_t live, frames_out_max, total_in, total_out;\n-\n-    live = hw->total_samples_captured - sw->total_hw_samples_acquired;\n-    if (!live) {\n-        return 0;\n-    }\n-    if (audio_bug(__func__, live > hw->conv_buf.size)) {\n-        dolog(\"live_in=%zu hw->conv_buf.size=%zu\\n\", live, hw->conv_buf.size);\n-        return 0;\n-    }\n-\n-    frames_out_max = MIN(buf_len / sw->info.bytes_per_frame,\n-                         sw->resample_buf.size);\n-\n-    audio_pcm_sw_resample_in(sw, live, frames_out_max, &total_in, &total_out);\n-\n-    if (!hw->pcm_ops->volume_in) {\n-        mixeng_volume(sw->resample_buf.buffer, total_out, &sw->vol);\n-    }\n-    sw->clip(buf, sw->resample_buf.buffer, total_out);\n-\n-    sw->total_hw_samples_acquired += total_in;\n-    return total_out * sw->info.bytes_per_frame;\n-}\n-\n-/*\n- * Hard voice (playback)\n- */\n-static size_t audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)\n-{\n-    SWVoiceOut *sw;\n-    size_t m = SIZE_MAX;\n-    int nb_live = 0;\n-\n-    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-        if (sw->active || !sw->empty) {\n-            m = MIN (m, sw->total_hw_samples_mixed);\n-            nb_live += 1;\n-        }\n-    }\n-\n-    *nb_livep = nb_live;\n-    return m;\n-}\n-\n-static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)\n-{\n-    size_t smin;\n-    int nb_live1;\n-\n-    smin = audio_pcm_hw_find_min_out (hw, &nb_live1);\n-    if (nb_live) {\n-        *nb_live = nb_live1;\n-    }\n-\n-    if (nb_live1) {\n-        size_t live = smin;\n-\n-        if (audio_bug(__func__, live > hw->mix_buf.size)) {\n-            dolog(\"live=%zu hw->mix_buf.size=%zu\\n\", live, hw->mix_buf.size);\n-            return 0;\n-        }\n-        return live;\n-    }\n-    return 0;\n-}\n-\n-static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)\n-{\n-    return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :\n-            INT_MAX) / hw->info.bytes_per_frame;\n-}\n-\n-static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)\n-{\n-    size_t clipped = 0;\n-    size_t pos = hw->mix_buf.pos;\n-\n-    while (len) {\n-        st_sample *src = hw->mix_buf.buffer + pos;\n-        uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);\n-        size_t samples_till_end_of_buf = hw->mix_buf.size - pos;\n-        size_t samples_to_clip = MIN(len, samples_till_end_of_buf);\n-\n-        hw->clip(dst, src, samples_to_clip);\n-\n-        pos = (pos + samples_to_clip) % hw->mix_buf.size;\n-        len -= samples_to_clip;\n-        clipped += samples_to_clip;\n-    }\n-}\n-\n-/*\n- * Soft voice (playback)\n- */\n-static void audio_pcm_sw_resample_out(SWVoiceOut *sw,\n-    size_t frames_in_max, size_t frames_out_max,\n-    size_t *total_in, size_t *total_out)\n-{\n-    HWVoiceOut *hw = sw->hw;\n-    struct st_sample *src, *dst;\n-    size_t live, wpos, frames_in, frames_out;\n-\n-    live = sw->total_hw_samples_mixed;\n-    wpos = (hw->mix_buf.pos + live) % hw->mix_buf.size;\n-\n-    /* write to mix_buf from wpos to end of buffer */\n-    src = sw->resample_buf.buffer;\n-    frames_in = frames_in_max;\n-    dst = hw->mix_buf.buffer + wpos;\n-    frames_out = MIN(frames_out_max, hw->mix_buf.size - wpos);\n-    st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);\n-    wpos += frames_out;\n-    *total_in = frames_in;\n-    *total_out = frames_out;\n-\n-    /* write to mix_buf from start of buffer if there are input frames left */\n-    if (frames_in_max - frames_in > 0 && wpos == hw->mix_buf.size) {\n-        src += frames_in;\n-        frames_in = frames_in_max - frames_in;\n-        dst = hw->mix_buf.buffer;\n-        frames_out = frames_out_max - frames_out;\n-        st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);\n-        *total_in += frames_in;\n-        *total_out += frames_out;\n-    }\n-}\n-\n-static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)\n-{\n-    HWVoiceOut *hw = sw->hw;\n-    size_t live, dead, hw_free, sw_max, fe_max;\n-    size_t frames_in_max, frames_out_max, total_in, total_out;\n-\n-    live = sw->total_hw_samples_mixed;\n-    if (audio_bug(__func__, live > hw->mix_buf.size)) {\n-        dolog(\"live=%zu hw->mix_buf.size=%zu\\n\", live, hw->mix_buf.size);\n-        return 0;\n-    }\n-\n-    if (live == hw->mix_buf.size) {\n-#ifdef DEBUG_OUT\n-        dolog (\"%s is full %zu\\n\", sw->name, live);\n-#endif\n-        return 0;\n-    }\n-\n-    dead = hw->mix_buf.size - live;\n-    hw_free = audio_pcm_hw_get_free(hw);\n-    hw_free = hw_free > live ? hw_free - live : 0;\n-    frames_out_max = MIN(dead, hw_free);\n-    sw_max = st_rate_frames_in(sw->rate, frames_out_max);\n-    fe_max = MIN(buf_len / sw->info.bytes_per_frame + sw->resample_buf.pos,\n-                 sw->resample_buf.size);\n-    frames_in_max = MIN(sw_max, fe_max);\n-\n-    if (!frames_in_max) {\n-        return 0;\n-    }\n-\n-    if (frames_in_max > sw->resample_buf.pos) {\n-        sw->conv(sw->resample_buf.buffer + sw->resample_buf.pos,\n-                 buf, frames_in_max - sw->resample_buf.pos);\n-        if (!sw->hw->pcm_ops->volume_out) {\n-            mixeng_volume(sw->resample_buf.buffer + sw->resample_buf.pos,\n-                          frames_in_max - sw->resample_buf.pos, &sw->vol);\n-        }\n-    }\n-\n-    audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max,\n-                              &total_in, &total_out);\n-\n-    sw->total_hw_samples_mixed += total_out;\n-    sw->empty = sw->total_hw_samples_mixed == 0;\n-\n-    /*\n-     * Upsampling may leave one audio frame in the resample buffer. Decrement\n-     * total_in by one if there was a leftover frame from the previous resample\n-     * pass in the resample buffer. Increment total_in by one if the current\n-     * resample pass left one frame in the resample buffer.\n-     */\n-    if (frames_in_max - total_in == 1) {\n-        /* copy one leftover audio frame to the beginning of the buffer */\n-        *sw->resample_buf.buffer = *(sw->resample_buf.buffer + total_in);\n-        total_in += 1 - sw->resample_buf.pos;\n-        sw->resample_buf.pos = 1;\n-    } else if (total_in >= sw->resample_buf.pos) {\n-        total_in -= sw->resample_buf.pos;\n-        sw->resample_buf.pos = 0;\n-    }\n-\n-#ifdef DEBUG_OUT\n-    dolog (\n-        \"%s: write size %zu written %zu total mixed %zu\\n\",\n-        SW_NAME(sw),\n-        buf_len / sw->info.bytes_per_frame,\n-        total_in,\n-        sw->total_hw_samples_mixed\n-        );\n-#endif\n-\n-    return total_in * sw->info.bytes_per_frame;\n-}\n-\n-#ifdef DEBUG_AUDIO\n-static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)\n-{\n-    dolog(\"%s: bits %d, sign %d, float %d, freq %d, nchan %d\\n\",\n-          cap, info->bits, info->is_signed, info->is_float, info->freq,\n-          info->nchannels);\n-}\n-#endif\n-\n-#define DAC\n-#include \"audio_template.h\"\n-#undef DAC\n-#include \"audio_template.h\"\n-\n-/*\n- * Timer\n- */\n-static int audio_is_timer_needed(AudioMixengBackend *s)\n-{\n-    HWVoiceIn *hwi = NULL;\n-    HWVoiceOut *hwo = NULL;\n-\n-    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {\n-        if (!hwo->poll_mode) {\n-            return 1;\n-        }\n-    }\n-    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {\n-        if (!hwi->poll_mode) {\n-            return 1;\n-        }\n-    }\n-    return 0;\n-}\n-\n-static void audio_reset_timer(AudioMixengBackend *s)\n-{\n-    if (audio_is_timer_needed(s)) {\n-        timer_mod_anticipate_ns(s->ts,\n-            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);\n-        if (!s->timer_running) {\n-            s->timer_running = true;\n-            s->timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n-            trace_audio_timer_start(s->period_ticks / SCALE_MS);\n-        }\n-    } else {\n-        timer_del(s->ts);\n-        if (s->timer_running) {\n-            s->timer_running = false;\n-            trace_audio_timer_stop();\n-        }\n-    }\n-}\n-\n-static void audio_timer (void *opaque)\n-{\n-    int64_t now, diff;\n-    AudioMixengBackend *s = opaque;\n-\n-    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n-    diff = now - s->timer_last;\n-    if (diff > s->period_ticks * 3 / 2) {\n-        trace_audio_timer_delayed(diff / SCALE_MS);\n-    }\n-    s->timer_last = now;\n-\n-    audio_run(s, \"timer\");\n-    audio_reset_timer(s);\n-}\n-\n-/*\n- * Public API\n- */\n-static size_t audio_mixeng_backend_write(AudioBackend *be, SWVoiceOut *sw,\n-                                         void *buf, size_t size)\n-{\n-    HWVoiceOut *hw;\n-\n-    if (!sw) {\n-        /* XXX: Consider options */\n-        return size;\n-    }\n-    hw = sw->hw;\n-\n-    if (!hw->enabled) {\n-        dolog(\"Writing to disabled voice %s\\n\", SW_NAME(sw));\n-        return 0;\n-    }\n-\n-    if (audio_get_pdo_out(hw->s->dev)->mixing_engine) {\n-        return audio_pcm_sw_write(sw, buf, size);\n-    } else {\n-        return hw->pcm_ops->write(hw, buf, size);\n-    }\n-}\n-\n-static size_t audio_mixeng_backend_read(AudioBackend *be,\n-                                        SWVoiceIn *sw, void *buf, size_t size)\n-{\n-    HWVoiceIn *hw;\n-\n-    if (!sw) {\n-        /* XXX: Consider options */\n-        return size;\n-    }\n-    hw = sw->hw;\n-\n-    if (!hw->enabled) {\n-        dolog(\"Reading from disabled voice %s\\n\", SW_NAME(sw));\n-        return 0;\n-    }\n-\n-    if (audio_get_pdo_in(hw->s->dev)->mixing_engine) {\n-        return audio_pcm_sw_read(sw, buf, size);\n-    } else {\n-        return hw->pcm_ops->read(hw, buf, size);\n-    }\n-\n-}\n-\n-static int audio_mixeng_backend_get_buffer_size_out(AudioBackend *be, SWVoiceOut *sw)\n-{\n-    if (!sw) {\n-        return 0;\n-    }\n-\n-    if (audio_get_pdo_out(sw->s->dev)->mixing_engine) {\n-        return sw->resample_buf.size * sw->info.bytes_per_frame;\n-    }\n-\n-    return sw->hw->samples * sw->hw->info.bytes_per_frame;\n-}\n-\n-static void audio_mixeng_backend_set_active_out(AudioBackend *be, SWVoiceOut *sw,\n-                                                bool on)\n-{\n-    HWVoiceOut *hw;\n-\n-    if (!sw) {\n-        return;\n-    }\n-\n-    hw = sw->hw;\n-    if (sw->active != on) {\n-        AudioMixengBackend *s = sw->s;\n-        SWVoiceOut *temp_sw;\n-        SWVoiceCap *sc;\n-\n-        if (on) {\n-            hw->pending_disable = 0;\n-            if (!hw->enabled) {\n-                hw->enabled = true;\n-                if (runstate_is_running()) {\n-                    if (hw->pcm_ops->enable_out) {\n-                        hw->pcm_ops->enable_out(hw, true);\n-                    }\n-                    audio_reset_timer (s);\n-                }\n-            }\n-        } else {\n-            if (hw->enabled) {\n-                int nb_active = 0;\n-\n-                for (temp_sw = hw->sw_head.lh_first; temp_sw;\n-                     temp_sw = temp_sw->entries.le_next) {\n-                    nb_active += temp_sw->active != 0;\n-                }\n-\n-                hw->pending_disable = nb_active == 1;\n-            }\n-        }\n-\n-        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n-            sc->sw.active = hw->enabled;\n-            if (hw->enabled) {\n-                audio_capture_maybe_changed (sc->cap, 1);\n-            }\n-        }\n-        sw->active = on;\n-    }\n-\n-}\n-\n-static void audio_mixeng_backend_set_active_in(AudioBackend *be, SWVoiceIn *sw, bool on)\n-{\n-    HWVoiceIn *hw;\n-\n-    if (!sw) {\n-        return;\n-    }\n-\n-    hw = sw->hw;\n-    if (sw->active != on) {\n-        AudioMixengBackend *s = sw->s;\n-        SWVoiceIn *temp_sw;\n-\n-        if (on) {\n-            if (!hw->enabled) {\n-                hw->enabled = true;\n-                if (runstate_is_running()) {\n-                    if (hw->pcm_ops->enable_in) {\n-                        hw->pcm_ops->enable_in(hw, true);\n-                    }\n-                    audio_reset_timer (s);\n-                }\n-            }\n-            sw->total_hw_samples_acquired = hw->total_samples_captured;\n-        } else {\n-            if (hw->enabled) {\n-                int nb_active = 0;\n-\n-                for (temp_sw = hw->sw_head.lh_first; temp_sw;\n-                     temp_sw = temp_sw->entries.le_next) {\n-                    nb_active += temp_sw->active != 0;\n-                }\n-\n-                if (nb_active == 1) {\n-                    hw->enabled = false;\n-                    if (hw->pcm_ops->enable_in) {\n-                        hw->pcm_ops->enable_in(hw, false);\n-                    }\n-                }\n-            }\n-        }\n-        sw->active = on;\n-    }\n-}\n-\n-static size_t audio_get_avail(SWVoiceIn *sw)\n-{\n-    size_t live;\n-\n-    if (!sw) {\n-        return 0;\n-    }\n-\n-    live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;\n-    if (audio_bug(__func__, live > sw->hw->conv_buf.size)) {\n-        dolog(\"live=%zu sw->hw->conv_buf.size=%zu\\n\", live,\n-              sw->hw->conv_buf.size);\n-        return 0;\n-    }\n-\n-    ldebug (\n-        \"%s: get_avail live %zu frontend frames %u\\n\",\n-        SW_NAME (sw),\n-        live, st_rate_frames_out(sw->rate, live)\n-        );\n-\n-    return live;\n-}\n-\n-static size_t audio_get_free(SWVoiceOut *sw)\n-{\n-    size_t live, dead;\n-\n-    if (!sw) {\n-        return 0;\n-    }\n-\n-    live = sw->total_hw_samples_mixed;\n-\n-    if (audio_bug(__func__, live > sw->hw->mix_buf.size)) {\n-        dolog(\"live=%zu sw->hw->mix_buf.size=%zu\\n\", live,\n-              sw->hw->mix_buf.size);\n-        return 0;\n-    }\n-\n-    dead = sw->hw->mix_buf.size - live;\n-\n-#ifdef DEBUG_OUT\n-    dolog(\"%s: get_free live %zu dead %zu frontend frames %u\\n\",\n-          SW_NAME(sw), live, dead, st_rate_frames_in(sw->rate, dead));\n-#endif\n-\n-    return dead;\n-}\n-\n-static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,\n-                                        size_t samples)\n-{\n-    size_t n;\n-\n-    if (hw->enabled) {\n-        SWVoiceCap *sc;\n-\n-        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n-            SWVoiceOut *sw = &sc->sw;\n-            size_t rpos2 = rpos;\n-\n-            n = samples;\n-            while (n) {\n-                size_t till_end_of_hw = hw->mix_buf.size - rpos2;\n-                size_t to_read = MIN(till_end_of_hw, n);\n-                size_t live, frames_in, frames_out;\n-\n-                sw->resample_buf.buffer = hw->mix_buf.buffer + rpos2;\n-                sw->resample_buf.size = to_read;\n-                live = sw->total_hw_samples_mixed;\n-\n-                audio_pcm_sw_resample_out(sw,\n-                                          to_read, sw->hw->mix_buf.size - live,\n-                                          &frames_in, &frames_out);\n-\n-                sw->total_hw_samples_mixed += frames_out;\n-                sw->empty = sw->total_hw_samples_mixed == 0;\n-\n-                if (to_read - frames_in) {\n-                    dolog(\"Could not mix %zu frames into a capture \"\n-                          \"buffer, mixed %zu\\n\",\n-                          to_read, frames_in);\n-                    break;\n-                }\n-                n -= to_read;\n-                rpos2 = (rpos2 + to_read) % hw->mix_buf.size;\n-            }\n-        }\n-    }\n-\n-    n = MIN(samples, hw->mix_buf.size - rpos);\n-    mixeng_clear(hw->mix_buf.buffer + rpos, n);\n-    mixeng_clear(hw->mix_buf.buffer, samples - n);\n-}\n-\n-static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)\n-{\n-    size_t clipped = 0;\n-\n-    while (live) {\n-        size_t size = live * hw->info.bytes_per_frame;\n-        size_t decr, proc;\n-        void *buf = hw->pcm_ops->get_buffer_out(hw, &size);\n-\n-        if (size == 0) {\n-            break;\n-        }\n-\n-        decr = MIN(size / hw->info.bytes_per_frame, live);\n-        if (buf) {\n-            audio_pcm_hw_clip_out(hw, buf, decr);\n-        }\n-        proc = hw->pcm_ops->put_buffer_out(hw, buf,\n-                                           decr * hw->info.bytes_per_frame) /\n-            hw->info.bytes_per_frame;\n-\n-        live -= proc;\n-        clipped += proc;\n-        hw->mix_buf.pos = (hw->mix_buf.pos + proc) % hw->mix_buf.size;\n-\n-        if (proc == 0 || proc < decr) {\n-            break;\n-        }\n-    }\n-\n-    if (hw->pcm_ops->run_buffer_out) {\n-        hw->pcm_ops->run_buffer_out(hw);\n-    }\n-\n-    return clipped;\n-}\n-\n-static void audio_run_out(AudioMixengBackend *s)\n-{\n-    HWVoiceOut *hw = NULL;\n-    SWVoiceOut *sw;\n-\n-    while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {\n-        size_t played, live, prev_rpos;\n-        size_t hw_free = audio_pcm_hw_get_free(hw);\n-        int nb_live;\n-\n-        if (!audio_get_pdo_out(s->dev)->mixing_engine) {\n-            /* there is exactly 1 sw for each hw with no mixeng */\n-            sw = hw->sw_head.lh_first;\n-\n-            if (hw->pending_disable) {\n-                hw->enabled = false;\n-                hw->pending_disable = false;\n-                if (hw->pcm_ops->enable_out) {\n-                    hw->pcm_ops->enable_out(hw, false);\n-                }\n-            }\n-\n-            if (sw->active) {\n-                sw->callback.fn(sw->callback.opaque,\n-                                hw_free * sw->info.bytes_per_frame);\n-            }\n-\n-            if (hw->pcm_ops->run_buffer_out) {\n-                hw->pcm_ops->run_buffer_out(hw);\n-            }\n-\n-            continue;\n-        }\n-\n-        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-            if (sw->active) {\n-                size_t sw_free = audio_get_free(sw);\n-                size_t free;\n-\n-                if (hw_free > sw->total_hw_samples_mixed) {\n-                    free = st_rate_frames_in(sw->rate,\n-                        MIN(sw_free, hw_free - sw->total_hw_samples_mixed));\n-                } else {\n-                    free = 0;\n-                }\n-                if (free > sw->resample_buf.pos) {\n-                    free = MIN(free, sw->resample_buf.size)\n-                           - sw->resample_buf.pos;\n-                    sw->callback.fn(sw->callback.opaque,\n-                                    free * sw->info.bytes_per_frame);\n-                }\n-            }\n-        }\n-\n-        live = audio_pcm_hw_get_live_out (hw, &nb_live);\n-        if (!nb_live) {\n-            live = 0;\n-        }\n-\n-        if (audio_bug(__func__, live > hw->mix_buf.size)) {\n-            dolog(\"live=%zu hw->mix_buf.size=%zu\\n\", live, hw->mix_buf.size);\n-            continue;\n-        }\n-\n-        if (hw->pending_disable && !nb_live) {\n-            SWVoiceCap *sc;\n-#ifdef DEBUG_OUT\n-            dolog (\"Disabling voice\\n\");\n-#endif\n-            hw->enabled = false;\n-            hw->pending_disable = false;\n-            if (hw->pcm_ops->enable_out) {\n-                hw->pcm_ops->enable_out(hw, false);\n-            }\n-            for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n-                sc->sw.active = false;\n-                audio_recalc_and_notify_capture (sc->cap);\n-            }\n-            continue;\n-        }\n-\n-        if (!live) {\n-            if (hw->pcm_ops->run_buffer_out) {\n-                hw->pcm_ops->run_buffer_out(hw);\n-            }\n-            continue;\n-        }\n-\n-        prev_rpos = hw->mix_buf.pos;\n-        played = audio_pcm_hw_run_out(hw, live);\n-        replay_audio_out(&played);\n-        if (audio_bug(__func__, hw->mix_buf.pos >= hw->mix_buf.size)) {\n-            dolog(\"hw->mix_buf.pos=%zu hw->mix_buf.size=%zu played=%zu\\n\",\n-                  hw->mix_buf.pos, hw->mix_buf.size, played);\n-            hw->mix_buf.pos = 0;\n-        }\n-\n-#ifdef DEBUG_OUT\n-        dolog(\"played=%zu\\n\", played);\n-#endif\n-\n-        if (played) {\n-            audio_capture_mix_and_clear (hw, prev_rpos, played);\n-        }\n-\n-        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-            if (!sw->active && sw->empty) {\n-                continue;\n-            }\n-\n-            if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) {\n-                dolog(\"played=%zu sw->total_hw_samples_mixed=%zu\\n\",\n-                      played, sw->total_hw_samples_mixed);\n-                played = sw->total_hw_samples_mixed;\n-            }\n-\n-            sw->total_hw_samples_mixed -= played;\n-\n-            if (!sw->total_hw_samples_mixed) {\n-                sw->empty = true;\n-            }\n-        }\n-    }\n-}\n-\n-static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)\n-{\n-    size_t conv = 0;\n-\n-    if (hw->pcm_ops->run_buffer_in) {\n-        hw->pcm_ops->run_buffer_in(hw);\n-    }\n-\n-    while (samples) {\n-        size_t proc;\n-        size_t size = samples * hw->info.bytes_per_frame;\n-        void *buf = hw->pcm_ops->get_buffer_in(hw, &size);\n-\n-        assert(size % hw->info.bytes_per_frame == 0);\n-        if (size == 0) {\n-            break;\n-        }\n-\n-        proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);\n-\n-        samples -= proc;\n-        conv += proc;\n-        hw->pcm_ops->put_buffer_in(hw, buf, proc * hw->info.bytes_per_frame);\n-    }\n-\n-    return conv;\n-}\n-\n-static void audio_run_in(AudioMixengBackend *s)\n-{\n-    HWVoiceIn *hw = NULL;\n-\n-    if (!audio_get_pdo_in(s->dev)->mixing_engine) {\n-        while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {\n-            /* there is exactly 1 sw for each hw with no mixeng */\n-            SWVoiceIn *sw = hw->sw_head.lh_first;\n-            if (sw->active) {\n-                sw->callback.fn(sw->callback.opaque, INT_MAX);\n-            }\n-        }\n-        return;\n-    }\n-\n-    while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {\n-        SWVoiceIn *sw;\n-        size_t captured = 0, min;\n-        int pos;\n-\n-        if (replay_mode != REPLAY_MODE_PLAY) {\n-            captured = audio_pcm_hw_run_in(\n-                hw, hw->conv_buf.size - audio_pcm_hw_get_live_in(hw));\n-        }\n-\n-        replay_audio_in_start(&captured);\n-        assert(captured <= hw->conv_buf.size);\n-        if (replay_mode == REPLAY_MODE_PLAY) {\n-            hw->conv_buf.pos = (hw->conv_buf.pos + captured) % hw->conv_buf.size;\n-        }\n-        for (pos = (hw->conv_buf.pos - captured + hw->conv_buf.size) % hw->conv_buf.size;\n-             pos != hw->conv_buf.pos;\n-             pos = (pos + 1) % hw->conv_buf.size) {\n-                uint64_t left, right;\n-\n-                if (replay_mode == REPLAY_MODE_RECORD) {\n-                    audio_sample_to_uint64(hw->conv_buf.buffer, pos, &left, &right);\n-                }\n-                replay_audio_in_sample_lr(&left, &right);\n-                if (replay_mode == REPLAY_MODE_PLAY) {\n-                    audio_sample_from_uint64(hw->conv_buf.buffer, pos, left, right);\n-                }\n-        }\n-        replay_audio_in_finish();\n-\n-        min = audio_pcm_hw_find_min_in (hw);\n-        hw->total_samples_captured += captured - min;\n-\n-        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-            sw->total_hw_samples_acquired -= min;\n-\n-            if (sw->active) {\n-                size_t sw_avail = audio_get_avail(sw);\n-                size_t avail;\n-\n-                avail = st_rate_frames_out(sw->rate, sw_avail);\n-                if (avail > 0) {\n-                    avail = MIN(avail, sw->resample_buf.size);\n-                    sw->callback.fn(sw->callback.opaque,\n-                                    avail * sw->info.bytes_per_frame);\n-                }\n-            }\n-        }\n-    }\n-}\n-\n-static void audio_run_capture(AudioMixengBackend *s)\n-{\n-    CaptureVoiceOut *cap;\n-\n-    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {\n-        size_t live, rpos, captured;\n-        HWVoiceOut *hw = &cap->hw;\n-        SWVoiceOut *sw;\n-\n-        captured = live = audio_pcm_hw_get_live_out (hw, NULL);\n-        rpos = hw->mix_buf.pos;\n-        while (live) {\n-            size_t left = hw->mix_buf.size - rpos;\n-            size_t to_capture = MIN(live, left);\n-            struct st_sample *src;\n-            struct capture_callback *cb;\n-\n-            src = hw->mix_buf.buffer + rpos;\n-            hw->clip (cap->buf, src, to_capture);\n-            mixeng_clear (src, to_capture);\n-\n-            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n-                cb->ops.capture (cb->opaque, cap->buf,\n-                                 to_capture * hw->info.bytes_per_frame);\n-            }\n-            rpos = (rpos + to_capture) % hw->mix_buf.size;\n-            live -= to_capture;\n-        }\n-        hw->mix_buf.pos = rpos;\n-\n-        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {\n-            if (!sw->active && sw->empty) {\n-                continue;\n-            }\n-\n-            if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) {\n-                dolog(\"captured=%zu sw->total_hw_samples_mixed=%zu\\n\",\n-                      captured, sw->total_hw_samples_mixed);\n-                captured = sw->total_hw_samples_mixed;\n-            }\n-\n-            sw->total_hw_samples_mixed -= captured;\n-            sw->empty = sw->total_hw_samples_mixed == 0;\n-        }\n-    }\n-}\n-\n-void audio_run(AudioMixengBackend *s, const char *msg)\n-{\n-    audio_run_out(s);\n-    audio_run_in(s);\n-    audio_run_capture(s);\n-\n-#ifdef DEBUG_POLL\n-    {\n-        static double prevtime;\n-        double currtime;\n-        struct timeval tv;\n-\n-        if (gettimeofday (&tv, NULL)) {\n-            perror (\"audio_run: gettimeofday\");\n-            return;\n-        }\n-\n-        currtime = tv.tv_sec + tv.tv_usec * 1e-6;\n-        dolog (\"Elapsed since last %s: %f\\n\", msg, currtime - prevtime);\n-        prevtime = currtime;\n-    }\n-#endif\n-}\n-\n-void audio_generic_run_buffer_in(HWVoiceIn *hw)\n-{\n-    if (unlikely(!hw->buf_emul)) {\n-        hw->size_emul = hw->samples * hw->info.bytes_per_frame;\n-        hw->buf_emul = g_malloc(hw->size_emul);\n-        hw->pos_emul = hw->pending_emul = 0;\n-    }\n-\n-    while (hw->pending_emul < hw->size_emul) {\n-        size_t read_len = MIN(hw->size_emul - hw->pos_emul,\n-                              hw->size_emul - hw->pending_emul);\n-        size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,\n-                                        read_len);\n-        hw->pending_emul += read;\n-        hw->pos_emul = (hw->pos_emul + read) % hw->size_emul;\n-        if (read < read_len) {\n-            break;\n-        }\n-    }\n-}\n-\n-void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)\n-{\n-    size_t start;\n-\n-    start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);\n-    assert(start < hw->size_emul);\n-\n-    *size = MIN(*size, hw->pending_emul);\n-    *size = MIN(*size, hw->size_emul - start);\n-    return hw->buf_emul + start;\n-}\n-\n-void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)\n-{\n-    assert(size <= hw->pending_emul);\n-    hw->pending_emul -= size;\n-}\n-\n-size_t audio_generic_buffer_get_free(HWVoiceOut *hw)\n-{\n-    if (hw->buf_emul) {\n-        return hw->size_emul - hw->pending_emul;\n-    } else {\n-        return hw->samples * hw->info.bytes_per_frame;\n-    }\n-}\n-\n-void audio_generic_run_buffer_out(HWVoiceOut *hw)\n-{\n-    while (hw->pending_emul) {\n-        size_t write_len, written, start;\n-\n-        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);\n-        assert(start < hw->size_emul);\n-\n-        write_len = MIN(hw->pending_emul, hw->size_emul - start);\n-\n-        written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);\n-        hw->pending_emul -= written;\n-\n-        if (written < write_len) {\n-            break;\n-        }\n-    }\n-}\n-\n-void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)\n-{\n-    if (unlikely(!hw->buf_emul)) {\n-        hw->size_emul = hw->samples * hw->info.bytes_per_frame;\n-        hw->buf_emul = g_malloc(hw->size_emul);\n-        hw->pos_emul = hw->pending_emul = 0;\n-    }\n-\n-    *size = MIN(hw->size_emul - hw->pending_emul,\n-                hw->size_emul - hw->pos_emul);\n-    return hw->buf_emul + hw->pos_emul;\n-}\n-\n-size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)\n-{\n-    assert(buf == hw->buf_emul + hw->pos_emul &&\n-           size + hw->pending_emul <= hw->size_emul);\n-\n-    hw->pending_emul += size;\n-    hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;\n-\n-    return size;\n-}\n-\n-size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)\n-{\n-    size_t total = 0;\n-\n-    if (hw->pcm_ops->buffer_get_free) {\n-        size_t free = hw->pcm_ops->buffer_get_free(hw);\n-\n-        size = MIN(size, free);\n-    }\n-\n-    while (total < size) {\n-        size_t dst_size = size - total;\n-        size_t copy_size, proc;\n-        void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);\n-\n-        if (dst_size == 0) {\n-            break;\n-        }\n-\n-        copy_size = MIN(size - total, dst_size);\n-        if (dst) {\n-            memcpy(dst, (char *)buf + total, copy_size);\n-        }\n-        proc = hw->pcm_ops->put_buffer_out(hw, dst, copy_size);\n-        total += proc;\n-\n-        if (proc == 0 || proc < copy_size) {\n-            break;\n-        }\n-    }\n-\n-    return total;\n-}\n-\n-size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)\n-{\n-    size_t total = 0;\n-\n-    if (hw->pcm_ops->run_buffer_in) {\n-        hw->pcm_ops->run_buffer_in(hw);\n-    }\n-\n-    while (total < size) {\n-        size_t src_size = size - total;\n-        void *src = hw->pcm_ops->get_buffer_in(hw, &src_size);\n-\n-        if (src_size == 0) {\n-            break;\n-        }\n-\n-        memcpy((char *)buf + total, src, src_size);\n-        hw->pcm_ops->put_buffer_in(hw, src, src_size);\n-        total += src_size;\n-    }\n-\n-    return total;\n-}\n-\n-static bool audio_mixeng_backend_realize(AudioBackend *abe,\n-                                         Audiodev *dev, Error **errp)\n-{\n-    AudioMixengBackend *be = AUDIO_MIXENG_BACKEND(abe);\n-    audio_driver *drv = AUDIO_MIXENG_BACKEND_GET_CLASS(be)->driver;\n-\n-    be->dev = dev;\n-    be->drv_opaque = drv->init(be->dev, errp);\n-    if (!be->drv_opaque) {\n-        return false;\n-    }\n-\n-    if (!drv->pcm_ops->get_buffer_in) {\n-        drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;\n-        drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;\n-    }\n-    if (!drv->pcm_ops->get_buffer_out) {\n-        drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;\n-        drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;\n-    }\n-\n-    audio_init_nb_voices_out(be, drv, 1);\n-    audio_init_nb_voices_in(be, drv, 0);\n-    be->drv = drv;\n-\n-    if (be->dev->timer_period <= 0) {\n-        be->period_ticks = 1;\n-    } else {\n-        be->period_ticks = be->dev->timer_period * (int64_t)SCALE_US;\n-    }\n-\n-    return true;\n-}\n-\n-static void audio_vm_change_state_handler (void *opaque, bool running,\n-                                           RunState state)\n-{\n-    AudioMixengBackend *s = opaque;\n-    HWVoiceOut *hwo = NULL;\n-    HWVoiceIn *hwi = NULL;\n-\n-    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {\n-        if (hwo->pcm_ops->enable_out) {\n-            hwo->pcm_ops->enable_out(hwo, running);\n-        }\n-    }\n-\n-    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {\n-        if (hwi->pcm_ops->enable_in) {\n-            hwi->pcm_ops->enable_in(hwi, running);\n-        }\n-    }\n-    audio_reset_timer (s);\n-}\n-\n-static const VMStateDescription vmstate_audio;\n-\n-static const char *audio_mixeng_backend_get_id(AudioBackend *be)\n-{\n-    return AUDIO_MIXENG_BACKEND(be)->dev->id;\n-}\n-\n-static CaptureVoiceOut *audio_mixeng_backend_add_capture(\n-    AudioBackend *be,\n-    struct audsettings *as,\n-    struct audio_capture_ops *ops,\n-    void *cb_opaque);\n-\n-static void audio_mixeng_backend_del_capture(\n-    AudioBackend *be,\n-    CaptureVoiceOut *cap,\n-    void *cb_opaque);\n-\n-static void audio_mixeng_backend_set_volume_out(AudioBackend *be, SWVoiceOut *sw,\n-                                                Volume *vol);\n-static void audio_mixeng_backend_set_volume_in(AudioBackend *be, SWVoiceIn *sw,\n-                                               Volume *vol);\n-\n-static void audio_mixeng_backend_class_init(ObjectClass *klass, const void *data)\n-{\n-    AudioBackendClass *be = AUDIO_BACKEND_CLASS(klass);\n-\n-    be->realize = audio_mixeng_backend_realize;\n-    be->get_id = audio_mixeng_backend_get_id;\n-    be->open_in = audio_mixeng_backend_open_in;\n-    be->open_out = audio_mixeng_backend_open_out;\n-    be->close_in = audio_mixeng_backend_close_in;\n-    be->close_out = audio_mixeng_backend_close_out;\n-    be->is_active_out = audio_mixeng_backend_is_active_out;\n-    be->is_active_in = audio_mixeng_backend_is_active_in;\n-    be->set_active_out = audio_mixeng_backend_set_active_out;\n-    be->set_active_in = audio_mixeng_backend_set_active_in;\n-    be->set_volume_out = audio_mixeng_backend_set_volume_out;\n-    be->set_volume_in = audio_mixeng_backend_set_volume_in;\n-    be->read = audio_mixeng_backend_read;\n-    be->write = audio_mixeng_backend_write;\n-    be->get_buffer_size_out = audio_mixeng_backend_get_buffer_size_out;\n-    be->add_capture = audio_mixeng_backend_add_capture;\n-    be->del_capture = audio_mixeng_backend_del_capture;\n-}\n-\n-static void audio_mixeng_backend_init(Object *obj)\n-{\n-    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(obj);\n-\n-    QLIST_INIT(&s->hw_head_out);\n-    QLIST_INIT(&s->hw_head_in);\n-    QLIST_INIT(&s->cap_head);\n-    s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);\n-\n-    s->vmse = qemu_add_vm_change_state_handler(audio_vm_change_state_handler, s);\n-    assert(s->vmse != NULL);\n-\n-    vmstate_register_any(NULL, &vmstate_audio, s);\n-}\n-\n-static void audio_mixeng_backend_finalize(Object *obj)\n-{\n-    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(obj);\n-    HWVoiceOut *hwo, *hwon;\n-    HWVoiceIn *hwi, *hwin;\n-\n-    QLIST_FOREACH_SAFE(hwo, &s->hw_head_out, entries, hwon) {\n-        SWVoiceCap *sc;\n+/* SPDX-License-Identifier: MIT */\n \n-        if (hwo->enabled && hwo->pcm_ops->enable_out) {\n-            hwo->pcm_ops->enable_out(hwo, false);\n-        }\n-        hwo->pcm_ops->fini_out (hwo);\n-\n-        for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {\n-            CaptureVoiceOut *cap = sc->cap;\n-            struct capture_callback *cb;\n+#include \"qemu/osdep.h\"\n+#include \"qemu/audio.h\"\n+#include \"qemu/help_option.h\"\n+#include \"qapi/clone-visitor.h\"\n+#include \"qapi/qobject-input-visitor.h\"\n+#include \"qapi/qapi-visit-audio.h\"\n+#include \"qapi/qapi-commands-audio.h\"\n+#include \"qobject/qdict.h\"\n+#include \"system/system.h\"\n \n-            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n-                cb->ops.destroy (cb->opaque);\n-            }\n-        }\n-        QLIST_REMOVE(hwo, entries);\n-    }\n+/* Order of CONFIG_AUDIO_DRIVERS is import.\n+   The 1st one is the one used by default, that is the reason\n+    that we generate the list.\n+*/\n+const char *audio_prio_list[] = {\n+#ifdef CONFIG_GIO\n+    \"dbus\",\n+#endif\n+    \"spice\",\n+    CONFIG_AUDIO_DRIVERS\n+    \"none\",\n+    NULL\n+};\n \n-    QLIST_FOREACH_SAFE(hwi, &s->hw_head_in, entries, hwin) {\n-        if (hwi->enabled && hwi->pcm_ops->enable_in) {\n-            hwi->pcm_ops->enable_in(hwi, false);\n-        }\n-        hwi->pcm_ops->fini_in (hwi);\n-        QLIST_REMOVE(hwi, entries);\n-    }\n+typedef struct AudiodevListEntry {\n+    Audiodev *dev;\n+    QSIMPLEQ_ENTRY(AudiodevListEntry) next;\n+} AudiodevListEntry;\n \n-    if (s->drv) {\n-        s->drv->fini (s->drv_opaque);\n-        s->drv = NULL;\n-    }\n+typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;\n \n-    if (s->dev) {\n-        qapi_free_Audiodev(s->dev);\n-        s->dev = NULL;\n-    }\n+static AudiodevListHead audiodevs =\n+    QSIMPLEQ_HEAD_INITIALIZER(audiodevs);\n+static AudiodevListHead default_audiodevs =\n+    QSIMPLEQ_HEAD_INITIALIZER(default_audiodevs);\n \n-    if (s->ts) {\n-        timer_free(s->ts);\n-        s->ts = NULL;\n-    }\n+static AudioBackendClass *audio_be_class_by_name(const char *name)\n+{\n+    g_autofree char *tname = g_strconcat(\"audio-\", name, NULL);\n+    ObjectClass *oc = module_object_class_by_name(tname);\n \n-    if (s->vmse) {\n-        qemu_del_vm_change_state_handler(s->vmse);\n-        s->vmse = NULL;\n+    if (!oc || !object_class_dynamic_cast(oc, TYPE_AUDIO_BACKEND)) {\n+        return NULL;\n     }\n \n-    vmstate_unregister(NULL, &vmstate_audio, s);\n+    return AUDIO_BACKEND_CLASS(oc);\n }\n \n+static AudioBackend *default_audio_be;\n+\n static Object *get_audiodevs_root(void)\n {\n     return object_get_container(\"audiodevs\");\n@@ -1758,25 +62,6 @@ void audio_cleanup(void)\n     object_unparent(get_audiodevs_root());\n }\n \n-static bool vmstate_audio_needed(void *opaque)\n-{\n-    /*\n-     * Never needed, this vmstate only exists in case\n-     * an old qemu sends it to us.\n-     */\n-    return false;\n-}\n-\n-static const VMStateDescription vmstate_audio = {\n-    .name = \"audio\",\n-    .version_id = 1,\n-    .minimum_version_id = 1,\n-    .needed = vmstate_audio_needed,\n-    .fields = (const VMStateField[]) {\n-        VMSTATE_END_OF_LIST()\n-    }\n-};\n-\n void audio_create_default_audiodevs(void)\n {\n     for (int i = 0; audio_prio_list[i]; i++) {\n@@ -1817,15 +102,13 @@ static AudioBackend *audio_init(Audiodev *dev, Error **errp)\n         assert(!default_audio_be);\n         for (;;) {\n             AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);\n-\n             if (!e) {\n                 error_setg(errp, \"no default audio driver available\");\n                 return NULL;\n             }\n-            dev = e->dev;\n             QSIMPLEQ_REMOVE_HEAD(&default_audiodevs, next);\n+            be = audio_be_new(e->dev, NULL);\n             g_free(e);\n-            be = audio_be_new(dev, NULL);\n             if (be) {\n                 break;\n             }\n@@ -1856,156 +139,35 @@ AudioBackend *audio_get_default_audio_be(Error **errp)\n     return default_audio_be;\n }\n \n-static struct audio_pcm_ops capture_pcm_ops;\n-\n-static CaptureVoiceOut *audio_mixeng_backend_add_capture(\n-    AudioBackend *be,\n-    struct audsettings *as,\n-    struct audio_capture_ops *ops,\n-    void *cb_opaque)\n-{\n-    AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(be);\n-    CaptureVoiceOut *cap;\n-    struct capture_callback *cb;\n-\n-    if (!s) {\n-        error_report(\"Capturing without setting an audiodev is not supported\");\n-        abort();\n-    }\n-\n-    if (!audio_get_pdo_out(s->dev)->mixing_engine) {\n-        dolog(\"Can't capture with mixeng disabled\\n\");\n-        return NULL;\n-    }\n-\n-    if (audio_validate_settings (as)) {\n-        dolog (\"Invalid settings were passed when trying to add capture\\n\");\n-        audio_print_settings (as);\n-        return NULL;\n-    }\n-\n-    cb = g_malloc0(sizeof(*cb));\n-    cb->ops = *ops;\n-    cb->opaque = cb_opaque;\n-\n-    cap = audio_pcm_capture_find_specific(s, as);\n-    if (cap) {\n-        QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);\n-    } else {\n-        HWVoiceOut *hw;\n-\n-        cap = g_malloc0(sizeof(*cap));\n-\n-        hw = &cap->hw;\n-        hw->s = s;\n-        hw->pcm_ops = &capture_pcm_ops;\n-        QLIST_INIT (&hw->sw_head);\n-        QLIST_INIT (&cap->cb_head);\n-\n-        /* XXX find a more elegant way */\n-        hw->samples = 4096 * 4;\n-        audio_pcm_hw_alloc_resources_out(hw);\n-\n-        audio_pcm_init_info (&hw->info, as);\n-\n-        cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame);\n-\n-        if (hw->info.is_float) {\n-            hw->clip = mixeng_clip_float[hw->info.nchannels == 2]\n-                [hw->info.swap_endianness];\n-        } else {\n-            hw->clip = mixeng_clip\n-                [hw->info.nchannels == 2]\n-                [hw->info.is_signed]\n-                [hw->info.swap_endianness]\n-                [audio_bits_to_index(hw->info.bits)];\n-        }\n-\n-        QLIST_INSERT_HEAD (&s->cap_head, cap, entries);\n-        QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);\n-\n-        QLIST_FOREACH(hw, &s->hw_head_out, entries) {\n-            audio_attach_capture (hw);\n-        }\n-    }\n-\n-    return cap;\n-}\n-\n-static void audio_mixeng_backend_del_capture(\n-    AudioBackend *be,\n-    CaptureVoiceOut *cap,\n-    void *cb_opaque)\n+void audio_help(void)\n {\n-    struct capture_callback *cb;\n-\n-    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {\n-        if (cb->opaque == cb_opaque) {\n-            cb->ops.destroy (cb_opaque);\n-            QLIST_REMOVE (cb, entries);\n-            g_free (cb);\n+    int i;\n \n-            if (!cap->cb_head.lh_first) {\n-                SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;\n+    printf(\"Available audio drivers:\\n\");\n \n-                while (sw) {\n-                    SWVoiceCap *sc = (SWVoiceCap *) sw;\n-#ifdef DEBUG_CAPTURE\n-                    dolog (\"freeing %s\\n\", sw->name);\n-#endif\n+    for (i = 0; i < AUDIODEV_DRIVER__MAX; i++) {\n+        const char *name = AudiodevDriver_str(i);\n+        AudioBackendClass *be = audio_be_class_by_name(name);\n \n-                    sw1 = sw->entries.le_next;\n-                    if (sw->rate) {\n-                        st_rate_stop (sw->rate);\n-                        sw->rate = NULL;\n-                    }\n-                    QLIST_REMOVE (sw, entries);\n-                    QLIST_REMOVE (sc, entries);\n-                    g_free (sc);\n-                    sw = sw1;\n-                }\n-                QLIST_REMOVE (cap, entries);\n-                g_free(cap->hw.mix_buf.buffer);\n-                g_free (cap->buf);\n-                g_free (cap);\n-            }\n-            return;\n+        if (be) {\n+            printf(\"%s\\n\", name);\n         }\n     }\n }\n \n-static void audio_mixeng_backend_set_volume_out(AudioBackend *be, SWVoiceOut *sw,\n-                                                Volume *vol)\n+void audio_parse_option(const char *opt)\n {\n-    if (sw) {\n-        HWVoiceOut *hw = sw->hw;\n-\n-        sw->vol.mute = vol->mute;\n-        sw->vol.l = nominal_volume.l * vol->vol[0] / 255;\n-        sw->vol.r = nominal_volume.l * vol->vol[vol->channels > 1 ? 1 : 0] /\n-            255;\n+    Audiodev *dev = NULL;\n \n-        if (hw->pcm_ops->volume_out) {\n-            hw->pcm_ops->volume_out(hw, vol);\n-        }\n+    if (is_help_option(opt)) {\n+        audio_help();\n+        exit(EXIT_SUCCESS);\n     }\n-}\n-\n-static void audio_mixeng_backend_set_volume_in(AudioBackend *be, SWVoiceIn *sw,\n-                                               Volume *vol)\n-{\n-    if (sw) {\n-        HWVoiceIn *hw = sw->hw;\n-\n-        sw->vol.mute = vol->mute;\n-        sw->vol.l = nominal_volume.l * vol->vol[0] / 255;\n-        sw->vol.r = nominal_volume.r * vol->vol[vol->channels > 1 ? 1 : 0] /\n-            255;\n+    Visitor *v = qobject_input_visitor_new_str(opt, \"driver\", &error_fatal);\n+    visit_type_Audiodev(v, NULL, &dev, &error_fatal);\n+    visit_free(v);\n \n-        if (hw->pcm_ops->volume_in) {\n-            hw->pcm_ops->volume_in(hw, vol);\n-        }\n-    }\n+    audio_add_audiodev(dev);\n }\n \n static void audio_create_pdos(Audiodev *dev)\n@@ -2104,6 +266,124 @@ static void audio_validate_per_direction_opts(\n     }\n }\n \n+static AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev)\n+{\n+    switch (dev->driver) {\n+    case AUDIODEV_DRIVER_NONE:\n+        return dev->u.none.out;\n+#ifdef CONFIG_AUDIO_ALSA\n+    case AUDIODEV_DRIVER_ALSA:\n+        return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.out);\n+#endif\n+#ifdef CONFIG_AUDIO_COREAUDIO\n+    case AUDIODEV_DRIVER_COREAUDIO:\n+        return qapi_AudiodevCoreaudioPerDirectionOptions_base(\n+            dev->u.coreaudio.out);\n+#endif\n+#ifdef CONFIG_DBUS_DISPLAY\n+    case AUDIODEV_DRIVER_DBUS:\n+        return dev->u.dbus.out;\n+#endif\n+#ifdef CONFIG_AUDIO_DSOUND\n+    case AUDIODEV_DRIVER_DSOUND:\n+        return dev->u.dsound.out;\n+#endif\n+#ifdef CONFIG_AUDIO_JACK\n+    case AUDIODEV_DRIVER_JACK:\n+        return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.out);\n+#endif\n+#ifdef CONFIG_AUDIO_OSS\n+    case AUDIODEV_DRIVER_OSS:\n+        return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.out);\n+#endif\n+#ifdef CONFIG_AUDIO_PA\n+    case AUDIODEV_DRIVER_PA:\n+        return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out);\n+#endif\n+#ifdef CONFIG_AUDIO_PIPEWIRE\n+    case AUDIODEV_DRIVER_PIPEWIRE:\n+        return qapi_AudiodevPipewirePerDirectionOptions_base(dev->u.pipewire.out);\n+#endif\n+#ifdef CONFIG_AUDIO_SDL\n+    case AUDIODEV_DRIVER_SDL:\n+        return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.out);\n+#endif\n+#ifdef CONFIG_AUDIO_SNDIO\n+    case AUDIODEV_DRIVER_SNDIO:\n+        return dev->u.sndio.out;\n+#endif\n+#ifdef CONFIG_SPICE\n+    case AUDIODEV_DRIVER_SPICE:\n+        return dev->u.spice.out;\n+#endif\n+    case AUDIODEV_DRIVER_WAV:\n+        return dev->u.wav.out;\n+\n+    case AUDIODEV_DRIVER__MAX:\n+        break;\n+    }\n+    abort();\n+}\n+\n+static AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev)\n+{\n+    switch (dev->driver) {\n+    case AUDIODEV_DRIVER_NONE:\n+        return dev->u.none.in;\n+#ifdef CONFIG_AUDIO_ALSA\n+    case AUDIODEV_DRIVER_ALSA:\n+        return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.in);\n+#endif\n+#ifdef CONFIG_AUDIO_COREAUDIO\n+    case AUDIODEV_DRIVER_COREAUDIO:\n+        return qapi_AudiodevCoreaudioPerDirectionOptions_base(\n+            dev->u.coreaudio.in);\n+#endif\n+#ifdef CONFIG_DBUS_DISPLAY\n+    case AUDIODEV_DRIVER_DBUS:\n+        return dev->u.dbus.in;\n+#endif\n+#ifdef CONFIG_AUDIO_DSOUND\n+    case AUDIODEV_DRIVER_DSOUND:\n+        return dev->u.dsound.in;\n+#endif\n+#ifdef CONFIG_AUDIO_JACK\n+    case AUDIODEV_DRIVER_JACK:\n+        return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.in);\n+#endif\n+#ifdef CONFIG_AUDIO_OSS\n+    case AUDIODEV_DRIVER_OSS:\n+        return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.in);\n+#endif\n+#ifdef CONFIG_AUDIO_PA\n+    case AUDIODEV_DRIVER_PA:\n+        return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in);\n+#endif\n+#ifdef CONFIG_AUDIO_PIPEWIRE\n+    case AUDIODEV_DRIVER_PIPEWIRE:\n+        return qapi_AudiodevPipewirePerDirectionOptions_base(dev->u.pipewire.in);\n+#endif\n+#ifdef CONFIG_AUDIO_SDL\n+    case AUDIODEV_DRIVER_SDL:\n+        return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.in);\n+#endif\n+#ifdef CONFIG_AUDIO_SNDIO\n+    case AUDIODEV_DRIVER_SNDIO:\n+        return dev->u.sndio.in;\n+#endif\n+#ifdef CONFIG_SPICE\n+    case AUDIODEV_DRIVER_SPICE:\n+        return dev->u.spice.in;\n+#endif\n+    case AUDIODEV_DRIVER_WAV:\n+        return dev->u.wav.in;\n+\n+    case AUDIODEV_DRIVER__MAX:\n+        break;\n+    }\n+    abort();\n+}\n+\n static void audio_validate_opts(Audiodev *dev, Error **errp)\n {\n     Error *err = NULL;\n@@ -2128,37 +408,6 @@ static void audio_validate_opts(Audiodev *dev, Error **errp)\n     }\n }\n \n-void audio_help(void)\n-{\n-    int i;\n-\n-    printf(\"Available audio drivers:\\n\");\n-\n-    for (i = 0; i < AUDIODEV_DRIVER__MAX; i++) {\n-        const char *name = AudiodevDriver_str(i);\n-        AudioBackendClass *be = audio_be_class_by_name(name);\n-\n-        if (be) {\n-            printf(\"%s\\n\", name);\n-        }\n-    }\n-}\n-\n-void audio_parse_option(const char *opt)\n-{\n-    Audiodev *dev = NULL;\n-\n-    if (is_help_option(opt)) {\n-        audio_help();\n-        exit(EXIT_SUCCESS);\n-    }\n-    Visitor *v = qobject_input_visitor_new_str(opt, \"driver\", &error_fatal);\n-    visit_type_Audiodev(v, NULL, &dev, &error_fatal);\n-    visit_free(v);\n-\n-    audio_add_audiodev(dev);\n-}\n-\n void audio_add_audiodev(Audiodev *dev)\n {\n     AudiodevListEntry *e;\n@@ -2190,65 +439,6 @@ void audio_init_audiodevs(void)\n     }\n }\n \n-audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)\n-{\n-    return (audsettings) {\n-        .freq = pdo->frequency,\n-        .nchannels = pdo->channels,\n-        .fmt = pdo->format,\n-        .endianness = HOST_BIG_ENDIAN,\n-    };\n-}\n-\n-int audioformat_bytes_per_sample(AudioFormat fmt)\n-{\n-    switch (fmt) {\n-    case AUDIO_FORMAT_U8:\n-    case AUDIO_FORMAT_S8:\n-        return 1;\n-\n-    case AUDIO_FORMAT_U16:\n-    case AUDIO_FORMAT_S16:\n-        return 2;\n-\n-    case AUDIO_FORMAT_U32:\n-    case AUDIO_FORMAT_S32:\n-    case AUDIO_FORMAT_F32:\n-        return 4;\n-\n-    case AUDIO_FORMAT__MAX:\n-        ;\n-    }\n-    abort();\n-}\n-\n-\n-/* frames = freq * usec / 1e6 */\n-int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,\n-                        audsettings *as, int def_usecs)\n-{\n-    uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs;\n-    return (as->freq * usecs + 500000) / 1000000;\n-}\n-\n-/* samples = channels * frames = channels * freq * usec / 1e6 */\n-int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,\n-                         audsettings *as, int def_usecs)\n-{\n-    return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);\n-}\n-\n-/*\n- * bytes = bytes_per_sample * samples =\n- *     bytes_per_sample * channels * freq * usec / 1e6\n- */\n-int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,\n-                       audsettings *as, int def_usecs)\n-{\n-    return audio_buffer_samples(pdo, as, def_usecs) *\n-        audioformat_bytes_per_sample(as->fmt);\n-}\n-\n AudioBackend *audio_be_by_name(const char *name, Error **errp)\n {\n     Object *obj = object_resolve_path_component(get_audiodevs_root(), name);\n@@ -2269,51 +459,6 @@ const char *audio_application_name(void)\n     return vm_name ? vm_name : \"qemu\";\n }\n \n-void audio_rate_start(RateCtl *rate)\n-{\n-    memset(rate, 0, sizeof(RateCtl));\n-    rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n-}\n-\n-size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)\n-{\n-    int64_t now;\n-    int64_t ticks;\n-    int64_t bytes;\n-    int64_t frames;\n-\n-    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);\n-    ticks = now - rate->start_ticks;\n-    bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);\n-    frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;\n-    rate->peeked_frames = frames;\n-\n-    return frames < 0 ? 0 : frames * info->bytes_per_frame;\n-}\n-\n-void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)\n-{\n-    if (rate->peeked_frames < 0 || rate->peeked_frames > 65536) {\n-        AUD_log(NULL, \"Resetting rate control (%\" PRId64 \" frames)\\n\",\n-                rate->peeked_frames);\n-        audio_rate_start(rate);\n-    }\n-\n-    rate->bytes_sent += bytes_used;\n-}\n-\n-size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,\n-                            size_t bytes_avail)\n-{\n-    size_t bytes;\n-\n-    bytes = audio_rate_peek_bytes(rate, info);\n-    bytes = MIN(bytes, bytes_avail);\n-    audio_rate_add_bytes(rate, bytes);\n-\n-    return bytes;\n-}\n-\n AudiodevList *qmp_query_audiodevs(Error **errp)\n {\n     AudiodevList *ret = NULL;\n@@ -2323,17 +468,3 @@ AudiodevList *qmp_query_audiodevs(Error **errp)\n     }\n     return ret;\n }\n-\n-static const TypeInfo audio_types[] = {\n-    {\n-        .name = TYPE_AUDIO_MIXENG_BACKEND,\n-        .parent = TYPE_AUDIO_BACKEND,\n-        .instance_size = sizeof(AudioMixengBackend),\n-        .instance_init = audio_mixeng_backend_init,\n-        .instance_finalize = audio_mixeng_backend_finalize,\n-        .class_size = sizeof(AudioMixengBackendClass),\n-        .class_init = audio_mixeng_backend_class_init,\n-    }\n-};\n-\n-DEFINE_TYPES(audio_types)\ndiff --git a/audio/meson.build b/audio/meson.build\nindex 17f58369c20..0e33b6f9836 100644\n--- a/audio/meson.build\n+++ b/audio/meson.build\n@@ -2,6 +2,7 @@ audio_ss = ss.source_set()\n audio_ss.add(files(\n   'audio.c',\n   'audio-be.c',\n+  'audio-mixeng-be.c',\n   'mixeng.c',\n   'noaudio.c',\n   'wavaudio.c',\n",
    "prefixes": [
        "40/85"
    ]
}