From nobody Fri Nov 7 03:55:10 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1545599852233329.111400546778; Sun, 23 Dec 2018 13:17:32 -0800 (PST) Received: from localhost ([127.0.0.1]:59865 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gbB7p-0004qr-NI for importer@patchew.org; Sun, 23 Dec 2018 16:17:29 -0500 Received: from eggs.gnu.org ([208.118.235.92]:52132) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gbAjq-0007SF-0V for qemu-devel@nongnu.org; Sun, 23 Dec 2018 15:52:46 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gbAjm-0001ma-9q for qemu-devel@nongnu.org; Sun, 23 Dec 2018 15:52:41 -0500 Received: from mail-wr1-x441.google.com ([2a00:1450:4864:20::441]:38253) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1gbAjl-0001k2-TG for qemu-devel@nongnu.org; Sun, 23 Dec 2018 15:52:38 -0500 Received: by mail-wr1-x441.google.com with SMTP id v13so10103114wrw.5 for ; Sun, 23 Dec 2018 12:52:37 -0800 (PST) Received: from nullptr.home.dirty-ice.org (2a01-036c-0113-24a3-0000-0000-0000-0005.pool6.digikabel.hu. [2a01:36c:113:24a3::5]) by smtp.gmail.com with ESMTPSA id g198sm25456920wmd.23.2018.12.23.12.52.35 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 23 Dec 2018 12:52:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=gxzbrdb3i3QrbJe0ZUOMFwI9ErESnZkXb+YI+ZQKKnI=; b=eKB6qrbq6i6bLq7mwPvPQkEVMqSQiK1b9TiH6HLW80X79KYznAdXfAwj7ujG5QtVVg h8TB0+plpWXznomh4JAufMPVQi1VeMJZCMjO7QglDi5wtG/i0Ne5xPMhfcnIbr2IHclg 2l4nVAnjGoXBkvUfs2N5xU3A4Oe8P2P7aj/eLJKW9UbebvvKeuNysdfdp0US504dvDnf 8FtGSnuBobunGMP0e9GvIZFv9OSpAAqW7q8HWn+px0KPtAeg0oIoIk3C5FJKF2EvzZ3I 2/V3lxA3iQQAGocvSfRnqyBrAGbzKYf5nmEag/X5OJMAi5gP2YNbblRQF7zHORJkffi+ d1kg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=gxzbrdb3i3QrbJe0ZUOMFwI9ErESnZkXb+YI+ZQKKnI=; b=A0U9dqz8ib0Kkvg+kDs0WjhH5n7r8uprF3cYisRe4FDt9ofmy5SOk0hujM64XjassF 4xXzfbvxEF7VNA6VXJ6LC63mIC+lPBHX4oEbfQYkG/XdGoWNvQPk6sD5xZ9474qVlxDN EssOyjBSKJ8pz8V60JIrBGvkaZvt51HJQT1uwopJPSlBMRg0c3W/q2X6vFEst9i7c4Qq GmudzucfnKcdd21R6Jvug2NMmTT6NwmnTKWC0UQOW9+PFKYe5mPUux2iyhO9jFokaaEH nYzrUT2mbYaABTqAFoqQ9Zys1/r0BpBn4UqtV3VUkXIwj4okXOvwyo0LNxLqAD+XC0A1 HNXQ== X-Gm-Message-State: AJcUukcLfZ9hNxlx3CKXwEIlOoIRGcczftoEgsFmxpobtjPAERSQlYRo MN3uIRKnjpiroLtdoV2vgQXtta7FmNQ= X-Google-Smtp-Source: ALg8bN7ljENq43s75OHDw5oScPSwXDrkPt+1OVwnC2u+tIgfnKbaTKyQXeStg9ibi+B0RqD+xlt+lA== X-Received: by 2002:adf:fd87:: with SMTP id d7mr9283979wrr.74.1545598356096; Sun, 23 Dec 2018 12:52:36 -0800 (PST) From: "=?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?=" X-Google-Original-From: =?UTF-8?q?K=C5=91v=C3=A1g=C3=B3=2C=20Zolt=C3=A1n?= To: qemu-devel@nongnu.org Date: Sun, 23 Dec 2018 21:51:42 +0100 Message-Id: <26b300d6c17e6ac015f1519d50151744cf806c03.1545598229.git.DirtY.iCE.hu@gmail.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::441 Subject: [Qemu-devel] [PATCH v2 06/52] audio: -audiodev command line option basic implementation X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , Gerd Hoffmann Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Audio drivers now get an Audiodev * as config paramters, instead of the global audio_option structs. There is some code in audio/audio_legacy.c that converts the old environment variables to audiodev options (this way backends do not have to worry about legacy options). It also contains a replacement of -audio-help, which prints out the equivalent -audiodev based config of the currently specified environment variables. Note that backends are not updated and still rely on environment variables. Also note that (due to moving try-poll from global to backend specific option) currently ALSA and OSS will always try poll mode, regardless of environment variables or -audiodev options. Signed-off-by: K=C5=91v=C3=A1g=C3=B3, Zolt=C3=A1n --- audio/Makefile.objs | 2 +- audio/alsaaudio.c | 2 +- audio/audio.c | 589 ++++++++++++++++------------------------- audio/audio.h | 20 +- audio/audio_int.h | 6 +- audio/audio_legacy.c | 211 +++++++++++++++ audio/audio_template.h | 13 +- audio/coreaudio.c | 2 +- audio/dsoundaudio.c | 2 +- audio/noaudio.c | 2 +- audio/ossaudio.c | 2 +- audio/paaudio.c | 2 +- audio/sdlaudio.c | 2 +- audio/spiceaudio.c | 2 +- audio/wavaudio.c | 2 +- vl.c | 11 +- 16 files changed, 491 insertions(+), 379 deletions(-) create mode 100644 audio/audio_legacy.c diff --git a/audio/Makefile.objs b/audio/Makefile.objs index db4fa7f18f..dca87f6347 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y =3D audio.o noaudio.o wavaudio.o mixeng.o +common-obj-y =3D audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o common-obj-$(CONFIG_SPICE) +=3D spiceaudio.o common-obj-$(CONFIG_AUDIO_COREAUDIO) +=3D coreaudio.o common-obj-$(CONFIG_AUDIO_DSOUND) +=3D dsoundaudio.o diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 5bd034267f..8302f3e882 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -1125,7 +1125,7 @@ static ALSAConf glob_conf =3D { .pcm_name_in =3D "default", }; =20 -static void *alsa_audio_init (void) +static void *alsa_audio_init(Audiodev *dev) { ALSAConf *conf =3D g_malloc(sizeof(ALSAConf)); *conf =3D glob_conf; diff --git a/audio/audio.c b/audio/audio.c index 96cbd57c37..e7f25ea84b 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -25,7 +25,12 @@ #include "hw/hw.h" #include "audio.h" #include "monitor/monitor.h" +#include "qapi/opts-visitor.h" #include "qemu/timer.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-audio.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "sysemu/replay.h" @@ -46,11 +51,12 @@ The 1st one is the one used by default, that is the reason that we generate the list. */ -static const char *audio_prio_list[] =3D { +const char *audio_prio_list[] =3D { "spice", CONFIG_AUDIO_DRIVERS "none", "wav", + NULL }; =20 static QLIST_HEAD(, audio_driver) audio_drivers; @@ -80,61 +86,6 @@ audio_driver *audio_driver_lookup(const char *name) return NULL; } =20 -static void audio_module_load_all(void) -{ - int i; - - for (i =3D 0; i < ARRAY_SIZE(audio_prio_list); i++) { - audio_driver_lookup(audio_prio_list[i]); - } -} - -struct fixed_settings { - int enabled; - int nb_voices; - int greedy; - struct audsettings settings; -}; - -static struct { - struct fixed_settings fixed_out; - struct fixed_settings fixed_in; - union { - int hertz; - int64_t ticks; - } period; - int try_poll_in; - int try_poll_out; -} conf =3D { - .fixed_out =3D { /* DAC fixed settings */ - .enabled =3D 1, - .nb_voices =3D 1, - .greedy =3D 1, - .settings =3D { - .freq =3D 44100, - .nchannels =3D 2, - .fmt =3D AUDIO_FORMAT_S16, - .endianness =3D AUDIO_HOST_ENDIANNESS, - } - }, - - .fixed_in =3D { /* ADC fixed settings */ - .enabled =3D 1, - .nb_voices =3D 1, - .greedy =3D 1, - .settings =3D { - .freq =3D 44100, - .nchannels =3D 2, - .fmt =3D AUDIO_FORMAT_S16, - .endianness =3D AUDIO_HOST_ENDIANNESS, - } - }, - - .period =3D { .hertz =3D 100 }, - .try_poll_in =3D 1, - .try_poll_out =3D 1, -}; - static AudioState glob_audio_state; =20 const struct mixeng_volume nominal_volume =3D { @@ -151,9 +102,6 @@ const struct mixeng_volume nominal_volume =3D { #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #error No its not #else -static void audio_print_options (const char *prefix, - struct audio_option *opt); - int audio_bug (const char *funcname, int cond) { if (cond) { @@ -161,16 +109,9 @@ int audio_bug (const char *funcname, int cond) =20 AUD_log (NULL, "A bug was just triggered in %s\n", funcname); if (!shown) { - struct audio_driver *d; - shown =3D 1; AUD_log (NULL, "Save all your work and restart without audio\n= "); - AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); AUD_log (NULL, "I am sorry\n"); - d =3D glob_audio_state.drv; - if (d) { - audio_print_options (d->name, d->options); - } } AUD_log (NULL, "Context:\n"); =20 @@ -232,31 +173,6 @@ void *audio_calloc (const char *funcname, int nmemb, s= ize_t size) return g_malloc0 (len); } =20 -static char *audio_alloc_prefix (const char *s) -{ - const char qemu_prefix[] =3D "QEMU_"; - size_t len, i; - char *r, *u; - - if (!s) { - return NULL; - } - - len =3D strlen (s); - r =3D g_malloc (len + sizeof (qemu_prefix)); - - u =3D r + sizeof (qemu_prefix) - 1; - - pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix); - pstrcat (r, len + sizeof (qemu_prefix), s); - - for (i =3D 0; i < len; ++i) { - u[i] =3D qemu_toupper(u[i]); - } - - return r; -} - static const char *audio_audfmt_to_string (AudioFormat fmt) { switch (fmt) { @@ -382,78 +298,6 @@ void AUD_log (const char *cap, const char *fmt, ...) va_end (ap); } =20 -static void audio_print_options (const char *prefix, - struct audio_option *opt) -{ - char *uprefix; - - if (!prefix) { - dolog ("No prefix specified\n"); - return; - } - - if (!opt) { - dolog ("No options\n"); - return; - } - - uprefix =3D audio_alloc_prefix (prefix); - - for (; opt->name; opt++) { - const char *state =3D "default"; - printf (" %s_%s: ", uprefix, opt->name); - - if (opt->overriddenp && *opt->overriddenp) { - state =3D "current"; - } - - switch (opt->tag) { - case AUD_OPT_BOOL: - { - int *intp =3D opt->valp; - printf ("boolean, %s =3D %d\n", state, *intp ? 1 : 0); - } - break; - - case AUD_OPT_INT: - { - int *intp =3D opt->valp; - printf ("integer, %s =3D %d\n", state, *intp); - } - break; - - case AUD_OPT_FMT: - { - AudioFormat *fmtp =3D opt->valp; - printf ( - "format, %s =3D %s, (one of: U8 S8 U16 S16 U32 S32)\n", - state, - audio_audfmt_to_string (*fmtp) - ); - } - break; - - case AUD_OPT_STR: - { - const char **strp =3D opt->valp; - printf ("string, %s =3D %s\n", - state, - *strp ? *strp : "(not set)"); - } - break; - - default: - printf ("???\n"); - dolog ("Bad value tag for option %s_%s %d\n", - uprefix, opt->name, opt->tag); - break; - } - printf (" %s\n", opt->descr); - } - - g_free (uprefix); -} - static void audio_process_options (const char *prefix, struct audio_option *opt) { @@ -1161,11 +1005,11 @@ static void audio_reset_timer (AudioState *s) { if (audio_is_timer_needed ()) { timer_mod_anticipate_ns(s->ts, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks); if (!audio_timer_running) { audio_timer_running =3D true; audio_timer_last =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - trace_audio_timer_start(conf.period.ticks / SCALE_MS); + trace_audio_timer_start(s->period_ticks / SCALE_MS); } } else { timer_del(s->ts); @@ -1179,16 +1023,17 @@ static void audio_reset_timer (AudioState *s) static void audio_timer (void *opaque) { int64_t now, diff; + AudioState *s =3D opaque; =20 now =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); diff =3D now - audio_timer_last; - if (diff > conf.period.ticks * 3 / 2) { + if (diff > s->period_ticks * 3 / 2) { trace_audio_timer_delayed(diff / SCALE_MS); } audio_timer_last =3D now; =20 - audio_run ("timer"); - audio_reset_timer (opaque); + audio_run("timer"); + audio_reset_timer(s); } =20 /* @@ -1248,7 +1093,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) if (!hw->enabled) { hw->enabled =3D 1; if (s->vm_running) { - hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_= out); + hw->pcm_ops->ctl_out(hw, VOICE_ENABLE, true /* todo */= ); audio_reset_timer (s); } } @@ -1293,7 +1138,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) if (!hw->enabled) { hw->enabled =3D 1; if (s->vm_running) { - hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_i= n); + hw->pcm_ops->ctl_in(hw, VOICE_ENABLE, true /* todo */); audio_reset_timer (s); } } @@ -1614,169 +1459,13 @@ void audio_run (const char *msg) #endif } =20 -static struct audio_option audio_options[] =3D { - /* DAC */ - { - .name =3D "DAC_FIXED_SETTINGS", - .tag =3D AUD_OPT_BOOL, - .valp =3D &conf.fixed_out.enabled, - .descr =3D "Use fixed settings for host DAC" - }, - { - .name =3D "DAC_FIXED_FREQ", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.fixed_out.settings.freq, - .descr =3D "Frequency for fixed host DAC" - }, - { - .name =3D "DAC_FIXED_FMT", - .tag =3D AUD_OPT_FMT, - .valp =3D &conf.fixed_out.settings.fmt, - .descr =3D "Format for fixed host DAC" - }, - { - .name =3D "DAC_FIXED_CHANNELS", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.fixed_out.settings.nchannels, - .descr =3D "Number of channels for fixed DAC (1 - mono, 2 - stereo= )" - }, - { - .name =3D "DAC_VOICES", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.fixed_out.nb_voices, - .descr =3D "Number of voices for DAC" - }, - { - .name =3D "DAC_TRY_POLL", - .tag =3D AUD_OPT_BOOL, - .valp =3D &conf.try_poll_out, - .descr =3D "Attempt using poll mode for DAC" - }, - /* ADC */ - { - .name =3D "ADC_FIXED_SETTINGS", - .tag =3D AUD_OPT_BOOL, - .valp =3D &conf.fixed_in.enabled, - .descr =3D "Use fixed settings for host ADC" - }, - { - .name =3D "ADC_FIXED_FREQ", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.fixed_in.settings.freq, - .descr =3D "Frequency for fixed host ADC" - }, - { - .name =3D "ADC_FIXED_FMT", - .tag =3D AUD_OPT_FMT, - .valp =3D &conf.fixed_in.settings.fmt, - .descr =3D "Format for fixed host ADC" - }, - { - .name =3D "ADC_FIXED_CHANNELS", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.fixed_in.settings.nchannels, - .descr =3D "Number of channels for fixed ADC (1 - mono, 2 - stereo= )" - }, - { - .name =3D "ADC_VOICES", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.fixed_in.nb_voices, - .descr =3D "Number of voices for ADC" - }, - { - .name =3D "ADC_TRY_POLL", - .tag =3D AUD_OPT_BOOL, - .valp =3D &conf.try_poll_in, - .descr =3D "Attempt using poll mode for ADC" - }, - /* Misc */ - { - .name =3D "TIMER_PERIOD", - .tag =3D AUD_OPT_INT, - .valp =3D &conf.period.hertz, - .descr =3D "Timer period in HZ (0 - use lowest possible)" - }, - { /* End of list */ } -}; - -static void audio_pp_nb_voices (const char *typ, int nb) -{ - switch (nb) { - case 0: - printf ("Does not support %s\n", typ); - break; - case 1: - printf ("One %s voice\n", typ); - break; - case INT_MAX: - printf ("Theoretically supports many %s voices\n", typ); - break; - default: - printf ("Theoretically supports up to %d %s voices\n", nb, typ); - break; - } - -} - -void AUD_help (void) -{ - struct audio_driver *d; - - /* make sure we print the help text for modular drivers too */ - audio_module_load_all(); - - audio_process_options ("AUDIO", audio_options); - QLIST_FOREACH(d, &audio_drivers, next) { - if (d->options) { - audio_process_options (d->name, d->options); - } - } - - printf ("Audio options:\n"); - audio_print_options ("AUDIO", audio_options); - printf ("\n"); - - printf ("Available drivers:\n"); - - QLIST_FOREACH(d, &audio_drivers, next) { - - printf ("Name: %s\n", d->name); - printf ("Description: %s\n", d->descr); - - audio_pp_nb_voices ("playback", d->max_voices_out); - audio_pp_nb_voices ("capture", d->max_voices_in); - - if (d->options) { - printf ("Options:\n"); - audio_print_options (d->name, d->options); - } - else { - printf ("No options\n"); - } - printf ("\n"); - } - - printf ( - "Options are settable through environment variables.\n" - "Example:\n" -#ifdef _WIN32 - " set QEMU_AUDIO_DRV=3Dwav\n" - " set QEMU_WAV_PATH=3Dc:\\tune.wav\n" -#else - " export QEMU_AUDIO_DRV=3Dwav\n" - " export QEMU_WAV_PATH=3D$HOME/tune.wav\n" - "(for csh replace export with setenv in the above)\n" -#endif - " qemu ...\n\n" - ); -} - -static int audio_driver_init (AudioState *s, struct audio_driver *drv) +static int audio_driver_init(AudioState *s, struct audio_driver *drv, + Audiodev *dev) { if (drv->options) { audio_process_options (drv->name, drv->options); } - s->drv_opaque =3D drv->init (); + s->drv_opaque =3D drv->init(dev); =20 if (s->drv_opaque) { audio_init_nb_voices_out (drv); @@ -1800,11 +1489,11 @@ static void audio_vm_change_state_handler (void *op= aque, int running, =20 s->vm_running =3D running; while ((hwo =3D audio_pcm_hw_find_any_enabled_out (hwo))) { - hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out); + hwo->pcm_ops->ctl_out(hwo, op, true /* todo */); } =20 while ((hwi =3D audio_pcm_hw_find_any_enabled_in (hwi))) { - hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); + hwi->pcm_ops->ctl_in(hwi, op, true /* todo */); } audio_reset_timer (s); } @@ -1854,6 +1543,11 @@ void audio_cleanup(void) s->drv->fini (s->drv_opaque); s->drv =3D NULL; } + + if (s->dev) { + qapi_free_Audiodev(s->dev); + s->dev =3D NULL; + } } =20 static const VMStateDescription vmstate_audio =3D { @@ -1865,19 +1559,40 @@ static const VMStateDescription vmstate_audio =3D { } }; =20 -static void audio_init (void) +static Audiodev *parse_option(QemuOpts *opts, Error **errp); +static int audio_init(Audiodev *dev) { size_t i; int done =3D 0; - const char *drvname; + const char *drvname =3D NULL; VMChangeStateEntry *e; AudioState *s =3D &glob_audio_state; struct audio_driver *driver; + /* silence gcc warning about uninitialized variable */ + QemuOptsList *list =3D NULL; =20 if (s->drv) { - return; + if (dev) { + dolog("Cannot create more than one audio backend, sorry\n"); + qapi_free_Audiodev(dev); + } + return -1; } =20 + if (dev) { + /* -audiodev option */ + drvname =3D AudiodevDriver_str(dev->driver); + } else { + /* legacy implicit initialization */ + audio_handle_legacy_opts(); + list =3D qemu_find_opts("audiodev"); + dev =3D parse_option(QTAILQ_FIRST(&list->head), &error_abort); + if (!dev) { + exit(1); + } + } + s->dev =3D dev; + QLIST_INIT (&s->hw_head_out); QLIST_INIT (&s->hw_head_in); QLIST_INIT (&s->cap_head); @@ -1885,10 +1600,8 @@ static void audio_init (void) =20 s->ts =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); =20 - audio_process_options ("AUDIO", audio_options); - - s->nb_hw_voices_out =3D conf.fixed_out.nb_voices; - s->nb_hw_voices_in =3D conf.fixed_in.nb_voices; + s->nb_hw_voices_out =3D dev->out->voices; + s->nb_hw_voices_in =3D dev->in->voices; =20 if (s->nb_hw_voices_out <=3D 0) { dolog ("Bogus number of playback voices %d, setting to 1\n", @@ -1902,46 +1615,46 @@ static void audio_init (void) s->nb_hw_voices_in =3D 0; } =20 - { - int def; - drvname =3D audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); - } - if (drvname) { driver =3D audio_driver_lookup(drvname); if (driver) { - done =3D !audio_driver_init(s, driver); + done =3D !audio_driver_init(s, driver, dev); } else { dolog ("Unknown audio driver `%s'\n", drvname); - dolog ("Run with -audio-help to list available drivers\n"); } - } - - if (!done) { - for (i =3D 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { + } else { + for (i =3D 0; !done && audio_prio_list[i]; i++) { + QemuOpts *opts =3D qemu_opts_find(list, audio_prio_list[i]); driver =3D audio_driver_lookup(audio_prio_list[i]); - if (driver && driver->can_be_default) { - done =3D !audio_driver_init(s, driver); + + if (driver && opts) { + qapi_free_Audiodev(dev); + dev =3D parse_option(opts, &error_abort); + if (!dev) { + exit(1); + } + s->dev =3D dev; + done =3D !audio_driver_init(s, driver, dev); } } } =20 if (!done) { driver =3D audio_driver_lookup("none"); - done =3D !audio_driver_init(s, driver); + done =3D !audio_driver_init(s, driver, dev); assert(done); dolog("warning: Using timer based audio emulation\n"); } =20 - if (conf.period.hertz <=3D 0) { - if (conf.period.hertz < 0) { - dolog ("warning: Timer period is negative - %d " - "treating as zero\n", - conf.period.hertz); + if (dev->timer_period <=3D 0) { + if (dev->timer_period < 0) { + dolog ("warning: Timer period is negative - %" PRId64 + " treating as zero\n", + dev->timer_period); } - conf.period.ticks =3D 1; + s->period_ticks =3D 1; } else { - conf.period.ticks =3D NANOSECONDS_PER_SECOND / conf.period.hertz; + s->period_ticks =3D NANOSECONDS_PER_SECOND / dev->timer_period; } =20 e =3D qemu_add_vm_change_state_handler (audio_vm_change_state_handler,= s); @@ -1952,11 +1665,12 @@ static void audio_init (void) =20 QLIST_INIT (&s->card_head); vmstate_register (NULL, 0, &vmstate_audio, s); + return 0; } =20 void AUD_register_card (const char *name, QEMUSoundCard *card) { - audio_init (); + audio_init(NULL); card->name =3D g_strdup (name); memset (&card->entries, 0, sizeof (card->entries)); QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries); @@ -2127,3 +1841,158 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, ui= nt8_t lvol, uint8_t rvol) } } } + +QemuOptsList qemu_audiodev_opts =3D { + .name =3D "audiodev", + .head =3D QTAILQ_HEAD_INITIALIZER(qemu_audiodev_opts.head), + .implied_opt_name =3D "driver", + .desc =3D { + /* + * no elements =3D> accept any params + * sanity checking will happen later + */ + { /* end of list */ } + }, +}; + +static void validate_per_direction_opts(AudiodevPerDirectionOptions *pdo, + Error **errp) +{ + if (!pdo->has_fixed_settings) { + pdo->has_fixed_settings =3D true; + pdo->fixed_settings =3D true; + } + if (!pdo->fixed_settings && + (pdo->has_frequency || pdo->has_channels || pdo->has_format)) { + error_setg(errp, + "You can't use frequency, channels or format with fixed= -settings=3Doff"); + return; + } + + if (!pdo->has_frequency) { + pdo->has_frequency =3D true; + pdo->frequency =3D 44100; + } + if (!pdo->has_channels) { + pdo->has_channels =3D true; + pdo->channels =3D 2; + } + if (!pdo->has_voices) { + pdo->has_voices =3D true; + pdo->voices =3D 1; + } + if (!pdo->has_format) { + pdo->has_format =3D true; + pdo->format =3D AUDIO_FORMAT_S16; + } +} + +static Audiodev *parse_option(QemuOpts *opts, Error **errp) +{ + Error *local_err =3D NULL; + Visitor *v =3D opts_visitor_new(opts, true); + Audiodev *dev =3D NULL; + visit_type_Audiodev(v, NULL, &dev, &local_err); + visit_free(v); + + if (local_err) { + goto err2; + } + + validate_per_direction_opts(dev->in, &local_err); + if (local_err) { + goto err; + } + validate_per_direction_opts(dev->out, &local_err); + if (local_err) { + goto err; + } + + if (!dev->has_timer_period) { + dev->has_timer_period =3D true; + dev->timer_period =3D 10000; /* 100Hz -> 10ms */ + } + + return dev; + +err: + qapi_free_Audiodev(dev); +err2: + error_propagate(errp, local_err); + return NULL; +} + +static int each_option(void *opaque, QemuOpts *opts, Error **errp) +{ + Audiodev *dev =3D parse_option(opts, errp); + if (!dev) { + return -1; + } + return audio_init(dev); +} + +void audio_set_options(void) +{ + if (qemu_opts_foreach(qemu_find_opts("audiodev"), each_option, NULL, + &error_abort)) { + exit(1); + } +} + +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo) +{ + return (audsettings) { + .freq =3D pdo->frequency, + .nchannels =3D pdo->channels, + .fmt =3D pdo->format, + .endianness =3D AUDIO_HOST_ENDIANNESS, + }; +} + +int audioformat_bytes_per_sample(AudioFormat fmt) +{ + switch (fmt) { + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S8: + return 1; + + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S16: + return 2; + + case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_S32: + return 4; + + case AUDIO_FORMAT__MAX: + ; + } + abort(); +} + + +/* frames =3D freq * usec / 1e6 */ +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + uint64_t usecs =3D pdo->has_buffer_len ? pdo->buffer_len : def_usecs; + return (as->freq * usecs + 500000) / 1000000; +} + +/* samples =3D channels * frames =3D channels * freq * usec / 1e6 */ +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + return as->nchannels * audio_buffer_frames(pdo, as, def_usecs); +} + +/* + * bytes =3D bytes_per_sample * samples =3D + * bytes_per_sample * channels * freq * usec / 1e6 + */ +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + return audio_buffer_samples(pdo, as, def_usecs) * + audioformat_bytes_per_sample(as->fmt); +} diff --git a/audio/audio.h b/audio/audio.h index 02f29a3b3e..7df1b8b249 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -36,12 +36,21 @@ typedef void (*audio_callback_fn) (void *opaque, int av= ail); #define AUDIO_HOST_ENDIANNESS 0 #endif =20 -struct audsettings { +typedef struct audsettings { int freq; int nchannels; AudioFormat fmt; int endianness; -}; +} audsettings; + +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo); +int audioformat_bytes_per_sample(AudioFormat fmt); +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs); +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs); +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs); =20 typedef enum { AUD_CNOTIFY_ENABLE, @@ -78,10 +87,11 @@ typedef struct QEMUAudioTimeStamp { uint64_t old_ts; } QEMUAudioTimeStamp; =20 +extern QemuOptsList qemu_audiodev_opts; + void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(= 2, 0); void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); =20 -void AUD_help (void); void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card); CaptureVoiceOut *AUD_add_capture ( @@ -163,4 +173,8 @@ void audio_sample_to_uint64(void *samples, int pos, void audio_sample_from_uint64(void *samples, int pos, uint64_t left, uint64_t right); =20 +void audio_set_options(void); +void audio_handle_legacy_opts(void); +void audio_legacy_help(void); + #endif /* QEMU_AUDIO_H */ diff --git a/audio/audio_int.h b/audio/audio_int.h index 244b454012..24b8793496 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -146,7 +146,7 @@ struct audio_driver { const char *name; const char *descr; struct audio_option *options; - void *(*init) (void); + void *(*init) (Audiodev *); void (*fini) (void *); struct audio_pcm_ops *pcm_ops; int can_be_default; @@ -193,6 +193,7 @@ struct SWVoiceCap { =20 struct AudioState { struct audio_driver *drv; + Audiodev *dev; void *drv_opaque; =20 QEMUTimer *ts; @@ -203,10 +204,13 @@ struct AudioState { int nb_hw_voices_out; int nb_hw_voices_in; int vm_running; + int64_t period_ticks; }; =20 extern const struct mixeng_volume nominal_volume; =20 +extern const char *audio_prio_list[]; + void audio_driver_register(audio_driver *drv); audio_driver *audio_driver_lookup(const char *name); =20 diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c new file mode 100644 index 0000000000..2242f06a5e --- /dev/null +++ b/audio/audio_legacy.c @@ -0,0 +1,211 @@ +#include "qemu/osdep.h" +#include "audio.h" +#include "audio_int.h" +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "qemu/cutils.h" +#include "qemu/option.h" +#include "qapi/error.h" + +#define AUDIO_CAP "audio-legacy" +#include "audio_int.h" + +typedef enum EnvTransform { + ENV_TRANSFORM_NONE, + ENV_TRANSFORM_BOOL, + ENV_TRANSFORM_FMT, + ENV_TRANSFORM_FRAMES_TO_USECS_IN, + ENV_TRANSFORM_FRAMES_TO_USECS_OUT, + ENV_TRANSFORM_SAMPLES_TO_USECS_IN, + ENV_TRANSFORM_SAMPLES_TO_USECS_OUT, + ENV_TRANSFORM_BYTES_TO_USECS_IN, + ENV_TRANSFORM_BYTES_TO_USECS_OUT, + ENV_TRANSFORM_MILLIS_TO_USECS, + ENV_TRANSFORM_HZ_TO_USECS, +} EnvTransform; + +typedef struct SimpleEnvMap { + const char *name; + const char *option; + EnvTransform transform; +} SimpleEnvMap; + +SimpleEnvMap global_map[] =3D { + /* DAC/out settings */ + { "QEMU_AUDIO_DAC_FIXED_SETTINGS", "out.fixed-settings", + ENV_TRANSFORM_BOOL }, + { "QEMU_AUDIO_DAC_FIXED_FREQ", "out.frequency" }, + { "QEMU_AUDIO_DAC_FIXED_FMT", "out.format", ENV_TRANSFORM_FMT }, + { "QEMU_AUDIO_DAC_FIXED_CHANNELS", "out.channels" }, + { "QEMU_AUDIO_DAC_VOICES", "out.voices" }, + + /* ADC/in settings */ + { "QEMU_AUDIO_ADC_FIXED_SETTINGS", "in.fixed-settings", + ENV_TRANSFORM_BOOL }, + { "QEMU_AUDIO_ADC_FIXED_FREQ", "in.frequency" }, + { "QEMU_AUDIO_ADC_FIXED_FMT", "in.format", ENV_TRANSFORM_FMT }, + { "QEMU_AUDIO_ADC_FIXED_CHANNELS", "in.channels" }, + { "QEMU_AUDIO_ADC_VOICES", "in.voices" }, + + /* general */ + { "QEMU_AUDIO_TIMER_PERIOD", "timer-period", ENV_TRANSFORM_HZ_TO_USECS= }, + { /* End of list */ } +}; + +static unsigned long long toull(const char *str) +{ + unsigned long long ret; + if (parse_uint_full(str, &ret, 10)) { + dolog("Invalid integer value `%s'\n", str); + exit(1); + } + return ret; +} + +/* non reentrant typesafe or anything, but enough in this small c file */ +static const char *tostr(unsigned long long val) +{ + /* max length in decimal possible for an unsigned long long number */ + #define LEN ((CHAR_BIT * sizeof(unsigned long long) - 1) / 3 + 2) + static char ret[LEN]; + snprintf(ret, LEN, "%llu", val); + return ret; +} + +static uint64_t frames_to_usecs(QemuOpts *opts, uint64_t frames, bool in) +{ + const char *opt =3D in ? "in.frequency" : "out.frequency"; + uint64_t freq =3D qemu_opt_get_number(opts, opt, 44100); + return (frames * 1000000 + freq / 2) / freq; +} + +static uint64_t samples_to_usecs(QemuOpts *opts, uint64_t samples, bool in) +{ + const char *opt =3D in ? "in.channels" : "out.channels"; + uint64_t channels =3D qemu_opt_get_number(opts, opt, 2); + return frames_to_usecs(opts, samples / channels, in); +} + +static uint64_t bytes_to_usecs(QemuOpts *opts, uint64_t bytes, bool in) +{ + const char *opt =3D in ? "in.format" : "out.format"; + const char *val =3D qemu_opt_get(opts, opt); + uint64_t bytes_per_sample =3D (val ? toull(val) : 16) / 8; + return samples_to_usecs(opts, bytes * bytes_per_sample, in); +} + +static const char *transform_val(QemuOpts *opts, const char *val, + EnvTransform transform) +{ + switch (transform) { + case ENV_TRANSFORM_NONE: + return val; + + case ENV_TRANSFORM_BOOL: + return toull(val) ? "on" : "off"; + + case ENV_TRANSFORM_FMT: + if (strcasecmp(val, "u8") =3D=3D 0) { + return "u8"; + } else if (strcasecmp(val, "u16") =3D=3D 0) { + return "u16"; + } else if (strcasecmp(val, "u32") =3D=3D 0) { + return "u32"; + } else if (strcasecmp(val, "s8") =3D=3D 0) { + return "s8"; + } else if (strcasecmp(val, "s16") =3D=3D 0) { + return "s16"; + } else if (strcasecmp(val, "s32") =3D=3D 0) { + return "s32"; + } else { + dolog("Invalid audio format `%s'\n", val); + exit(1); + } + + case ENV_TRANSFORM_FRAMES_TO_USECS_IN: + return tostr(frames_to_usecs(opts, toull(val), true)); + case ENV_TRANSFORM_FRAMES_TO_USECS_OUT: + return tostr(frames_to_usecs(opts, toull(val), false)); + + case ENV_TRANSFORM_SAMPLES_TO_USECS_IN: + return tostr(samples_to_usecs(opts, toull(val), true)); + case ENV_TRANSFORM_SAMPLES_TO_USECS_OUT: + return tostr(samples_to_usecs(opts, toull(val), false)); + + case ENV_TRANSFORM_BYTES_TO_USECS_IN: + return tostr(bytes_to_usecs(opts, toull(val), true)); + case ENV_TRANSFORM_BYTES_TO_USECS_OUT: + return tostr(bytes_to_usecs(opts, toull(val), false)); + + case ENV_TRANSFORM_MILLIS_TO_USECS: + return tostr(toull(val) * 1000); + + case ENV_TRANSFORM_HZ_TO_USECS: + return tostr(1000000 / toull(val)); + } + + abort(); /* it's unreachable, gcc */ +} + +static void handle_env_opts(QemuOpts *opts, SimpleEnvMap *map) +{ + while (map->name) { + const char *val =3D getenv(map->name); + + if (val) { + qemu_opt_set(opts, map->option, + transform_val(opts, val, map->transform), + &error_abort); + } + + ++map; + } +} + +static void legacy_opt(const char *drv) +{ + QemuOpts *opts; + opts =3D qemu_opts_create(qemu_find_opts("audiodev"), drv, true, + &error_abort); + qemu_opt_set(opts, "driver", drv, &error_abort); + + handle_env_opts(opts, global_map); +} + +void audio_handle_legacy_opts(void) +{ + const char *drvname =3D getenv("QEMU_AUDIO_DRV"); + + if (drvname) { + audio_driver *driver =3D audio_driver_lookup(drvname); + if (!driver) { + dolog("Unknown audio driver `%s'\n", drvname); + } + legacy_opt(drvname); + } else { + for (int i =3D 0; audio_prio_list[i]; i++) { + audio_driver *driver =3D audio_driver_lookup(audio_prio_list[i= ]); + if (driver && driver->can_be_default) { + legacy_opt(driver->name); + } + } + } +} + +static int legacy_help_each(void *opaque, QemuOpts *opts, Error **errp) +{ + printf("-audiodev "); + qemu_opts_print(opts, ","); + printf("\n"); + return 0; +} + +void audio_legacy_help(void) +{ + printf("Environment variable based configuration deprecated.\n"); + printf("Please use the new -audiodev option.\n"); + + audio_handle_legacy_opts(); + printf("\nEquivalent -audiodev to your current environment variables:\= n"); + qemu_opts_foreach(qemu_find_opts("audiodev"), legacy_help_each, NULL, = NULL); +} diff --git a/audio/audio_template.h b/audio/audio_template.h index 7de227d2d1..c1d7207abd 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -302,8 +302,10 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct = audsettings *as) static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) { HW *hw; + AudioState *s =3D &glob_audio_state; + AudiodevPerDirectionOptions *pdo =3D s->dev->TYPE; =20 - if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greed= y) { + if (pdo->fixed_settings) { hw =3D glue (audio_pcm_hw_add_new_, TYPE) (as); if (hw) { return hw; @@ -331,9 +333,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( SW *sw; HW *hw; struct audsettings hw_as; + AudioState *s =3D &glob_audio_state; + AudiodevPerDirectionOptions *pdo =3D s->dev->TYPE; =20 - if (glue (conf.fixed_, TYPE).enabled) { - hw_as =3D glue (conf.fixed_, TYPE).settings; + if (pdo->fixed_settings) { + hw_as =3D audiodev_to_audsettings(pdo); } else { hw_as =3D *as; @@ -398,6 +402,7 @@ SW *glue (AUD_open_, TYPE) ( ) { AudioState *s =3D &glob_audio_state; + AudiodevPerDirectionOptions *pdo =3D s->dev->TYPE; =20 if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { dolog ("card=3D%p name=3D%p callback_fn=3D%p as=3D%p\n", @@ -422,7 +427,7 @@ SW *glue (AUD_open_, TYPE) ( return sw; } =20 - if (!glue (conf.fixed_, TYPE).enabled && sw) { + if (!pdo->fixed_settings && sw) { glue (AUD_close_, TYPE) (card, sw); sw =3D NULL; } diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 638c60b300..7d4225dbee 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -685,7 +685,7 @@ static CoreaudioConf glob_conf =3D { .nbuffers =3D 4, }; =20 -static void *coreaudio_audio_init (void) +static void *coreaudio_audio_init(Audiodev *dev) { CoreaudioConf *conf =3D g_malloc(sizeof(CoreaudioConf)); *conf =3D glob_conf; diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 3ed73a30d1..02fe777cba 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -783,7 +783,7 @@ static void dsound_audio_fini (void *opaque) g_free(s); } =20 -static void *dsound_audio_init (void) +static void *dsound_audio_init(Audiodev *dev) { int err; HRESULT hr; diff --git a/audio/noaudio.c b/audio/noaudio.c index 1bfebeca7d..2cc274c5e5 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) return 0; } =20 -static void *no_audio_init (void) +static void *no_audio_init (Audiodev *dev) { return &no_audio_init; } diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 355e8fbda5..e0cadbef29 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -842,7 +842,7 @@ static OSSConf glob_conf =3D { .policy =3D 5 }; =20 -static void *oss_audio_init (void) +static void *oss_audio_init(Audiodev *dev) { OSSConf *conf =3D g_malloc(sizeof(OSSConf)); *conf =3D glob_conf; diff --git a/audio/paaudio.c b/audio/paaudio.c index f1f9a741ac..0981f010c9 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -812,7 +812,7 @@ static PAConf glob_conf =3D { .samples =3D 4096, }; =20 -static void *qpa_audio_init (void) +static void *qpa_audio_init(Audiodev *dev) { paaudio *g =3D g_malloc(sizeof(paaudio)); g->conf =3D glob_conf; diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index aa42ea26bf..097841fde1 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -436,7 +436,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } =20 -static void *sdl_audio_init (void) +static void *sdl_audio_init(Audiodev *dev) { SDLAudioState *s =3D &glob_sdl; if (s->driver_created) { diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 3aeb0cb357..affc3df17f 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif =3D { .base.minor_version =3D SPICE_INTERFACE_RECORD_MINOR, }; =20 -static void *spice_audio_init (void) +static void *spice_audio_init(Audiodev *dev) { if (!using_spice) { return NULL; diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 35a614785e..9eff3555b3 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -232,7 +232,7 @@ static WAVConf glob_conf =3D { .wav_path =3D "qemu.wav" }; =20 -static void *wav_audio_init (void) +static void *wav_audio_init(Audiodev *dev) { WAVConf *conf =3D g_malloc(sizeof(WAVConf)); *conf =3D glob_conf; diff --git a/vl.c b/vl.c index 8353d3c718..b5364ffe46 100644 --- a/vl.c +++ b/vl.c @@ -3074,6 +3074,7 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_option_rom_opts); qemu_add_opts(&qemu_machine_opts); qemu_add_opts(&qemu_accel_opts); + qemu_add_opts(&qemu_audiodev_opts); qemu_add_opts(&qemu_mem_opts); qemu_add_opts(&qemu_smp_opts); qemu_add_opts(&qemu_boot_opts); @@ -3307,9 +3308,15 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_BT, optarg); break; case QEMU_OPTION_audio_help: - AUD_help (); + audio_legacy_help(); exit (0); break; + case QEMU_OPTION_audiodev: + if (!qemu_opts_parse_noisily(qemu_find_opts("audiodev"), + optarg, true)) { + exit(1); + } + break; case QEMU_OPTION_soundhw: select_soundhw (optarg); break; @@ -4545,6 +4552,8 @@ int main(int argc, char **argv, char **envp) /* do monitor/qmp handling at preconfig state if requested */ main_loop(); =20 + audio_set_options(); + /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); =20 --=20 2.20.1