audio: -audiodev command line option basic implementation
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ővágó, Zoltán <DirtY.iCE.hu@gmail.com> Message-id: e99a7cbdac0d13512743880660b2032024703e4c.1552083282.git.DirtY.iCE.hu@gmail.com Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
f0b3d81152
commit
71830221fb
@ -1,4 +1,4 @@
|
||||
common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
|
||||
common-obj-$(CONFIG_SPICE) += spiceaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
||||
|
@ -1125,7 +1125,7 @@ static ALSAConf glob_conf = {
|
||||
.pcm_name_in = "default",
|
||||
};
|
||||
|
||||
static void *alsa_audio_init (void)
|
||||
static void *alsa_audio_init(Audiodev *dev)
|
||||
{
|
||||
ALSAConf *conf = g_malloc(sizeof(ALSAConf));
|
||||
*conf = glob_conf;
|
||||
|
630
audio/audio.c
630
audio/audio.c
@ -26,6 +26,9 @@
|
||||
#include "audio.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-audio.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/replay.h"
|
||||
@ -46,14 +49,16 @@
|
||||
The 1st one is the one used by default, that is the reason
|
||||
that we generate the list.
|
||||
*/
|
||||
static const char *audio_prio_list[] = {
|
||||
const char *audio_prio_list[] = {
|
||||
"spice",
|
||||
CONFIG_AUDIO_DRIVERS
|
||||
"none",
|
||||
"wav",
|
||||
NULL
|
||||
};
|
||||
|
||||
static QLIST_HEAD(, audio_driver) audio_drivers;
|
||||
static AudiodevListHead audiodevs = QSIMPLEQ_HEAD_INITIALIZER(audiodevs);
|
||||
|
||||
void audio_driver_register(audio_driver *drv)
|
||||
{
|
||||
@ -80,61 +85,6 @@ audio_driver *audio_driver_lookup(const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void audio_module_load_all(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 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 = {
|
||||
.fixed_out = { /* DAC fixed settings */
|
||||
.enabled = 1,
|
||||
.nb_voices = 1,
|
||||
.greedy = 1,
|
||||
.settings = {
|
||||
.freq = 44100,
|
||||
.nchannels = 2,
|
||||
.fmt = AUDIO_FORMAT_S16,
|
||||
.endianness = AUDIO_HOST_ENDIANNESS,
|
||||
}
|
||||
},
|
||||
|
||||
.fixed_in = { /* ADC fixed settings */
|
||||
.enabled = 1,
|
||||
.nb_voices = 1,
|
||||
.greedy = 1,
|
||||
.settings = {
|
||||
.freq = 44100,
|
||||
.nchannels = 2,
|
||||
.fmt = AUDIO_FORMAT_S16,
|
||||
.endianness = AUDIO_HOST_ENDIANNESS,
|
||||
}
|
||||
},
|
||||
|
||||
.period = { .hertz = 100 },
|
||||
.try_poll_in = 1,
|
||||
.try_poll_out = 1,
|
||||
};
|
||||
|
||||
static AudioState glob_audio_state;
|
||||
|
||||
const struct mixeng_volume nominal_volume = {
|
||||
@ -151,9 +101,6 @@ const struct mixeng_volume nominal_volume = {
|
||||
#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
|
||||
#error No its not
|
||||
#else
|
||||
static void audio_print_options (const char *prefix,
|
||||
struct audio_option *opt);
|
||||
|
||||
int audio_bug (const char *funcname, int cond)
|
||||
{
|
||||
if (cond) {
|
||||
@ -161,16 +108,9 @@ int audio_bug (const char *funcname, int cond)
|
||||
|
||||
AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
|
||||
if (!shown) {
|
||||
struct audio_driver *d;
|
||||
|
||||
shown = 1;
|
||||
AUD_log (NULL, "Save all your work and restart without audio\n");
|
||||
AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
|
||||
AUD_log (NULL, "I am sorry\n");
|
||||
d = glob_audio_state.drv;
|
||||
if (d) {
|
||||
audio_print_options (d->name, d->options);
|
||||
}
|
||||
}
|
||||
AUD_log (NULL, "Context:\n");
|
||||
|
||||
@ -232,31 +172,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size)
|
||||
return g_malloc0 (len);
|
||||
}
|
||||
|
||||
static char *audio_alloc_prefix (const char *s)
|
||||
{
|
||||
const char qemu_prefix[] = "QEMU_";
|
||||
size_t len, i;
|
||||
char *r, *u;
|
||||
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen (s);
|
||||
r = g_malloc (len + sizeof (qemu_prefix));
|
||||
|
||||
u = r + sizeof (qemu_prefix) - 1;
|
||||
|
||||
pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix);
|
||||
pstrcat (r, len + sizeof (qemu_prefix), s);
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
u[i] = qemu_toupper(u[i]);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char *audio_audfmt_to_string (AudioFormat fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
@ -382,78 +297,6 @@ void AUD_log (const char *cap, const char *fmt, ...)
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
static void audio_print_options (const char *prefix,
|
||||
struct audio_option *opt)
|
||||
{
|
||||
char *uprefix;
|
||||
|
||||
if (!prefix) {
|
||||
dolog ("No prefix specified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!opt) {
|
||||
dolog ("No options\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uprefix = audio_alloc_prefix (prefix);
|
||||
|
||||
for (; opt->name; opt++) {
|
||||
const char *state = "default";
|
||||
printf (" %s_%s: ", uprefix, opt->name);
|
||||
|
||||
if (opt->overriddenp && *opt->overriddenp) {
|
||||
state = "current";
|
||||
}
|
||||
|
||||
switch (opt->tag) {
|
||||
case AUD_OPT_BOOL:
|
||||
{
|
||||
int *intp = opt->valp;
|
||||
printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUD_OPT_INT:
|
||||
{
|
||||
int *intp = opt->valp;
|
||||
printf ("integer, %s = %d\n", state, *intp);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUD_OPT_FMT:
|
||||
{
|
||||
AudioFormat *fmtp = opt->valp;
|
||||
printf (
|
||||
"format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
|
||||
state,
|
||||
audio_audfmt_to_string (*fmtp)
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case AUD_OPT_STR:
|
||||
{
|
||||
const char **strp = opt->valp;
|
||||
printf ("string, %s = %s\n",
|
||||
state,
|
||||
*strp ? *strp : "(not set)");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("???\n");
|
||||
dolog ("Bad value tag for option %s_%s %d\n",
|
||||
uprefix, opt->name, opt->tag);
|
||||
break;
|
||||
}
|
||||
printf (" %s\n", opt->descr);
|
||||
}
|
||||
|
||||
g_free (uprefix);
|
||||
}
|
||||
|
||||
static void audio_process_options (const char *prefix,
|
||||
struct audio_option *opt)
|
||||
{
|
||||
@ -1141,11 +984,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 = true;
|
||||
audio_timer_last = 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);
|
||||
@ -1159,16 +1002,17 @@ static void audio_reset_timer (AudioState *s)
|
||||
static void audio_timer (void *opaque)
|
||||
{
|
||||
int64_t now, diff;
|
||||
AudioState *s = opaque;
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
diff = 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 = now;
|
||||
|
||||
audio_run ("timer");
|
||||
audio_reset_timer (opaque);
|
||||
audio_run("timer");
|
||||
audio_reset_timer(s);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1228,7 +1072,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
|
||||
hw->pcm_ops->ctl_out(hw, VOICE_ENABLE, true);
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
}
|
||||
@ -1273,7 +1117,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
|
||||
if (!hw->enabled) {
|
||||
hw->enabled = 1;
|
||||
if (s->vm_running) {
|
||||
hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
|
||||
hw->pcm_ops->ctl_in(hw, VOICE_ENABLE, true);
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
}
|
||||
@ -1594,169 +1438,13 @@ void audio_run (const char *msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct audio_option audio_options[] = {
|
||||
/* DAC */
|
||||
{
|
||||
.name = "DAC_FIXED_SETTINGS",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.fixed_out.enabled,
|
||||
.descr = "Use fixed settings for host DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_FREQ",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_out.settings.freq,
|
||||
.descr = "Frequency for fixed host DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_FMT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &conf.fixed_out.settings.fmt,
|
||||
.descr = "Format for fixed host DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_FIXED_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_out.settings.nchannels,
|
||||
.descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "DAC_VOICES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_out.nb_voices,
|
||||
.descr = "Number of voices for DAC"
|
||||
},
|
||||
{
|
||||
.name = "DAC_TRY_POLL",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.try_poll_out,
|
||||
.descr = "Attempt using poll mode for DAC"
|
||||
},
|
||||
/* ADC */
|
||||
{
|
||||
.name = "ADC_FIXED_SETTINGS",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.fixed_in.enabled,
|
||||
.descr = "Use fixed settings for host ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_FIXED_FREQ",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_in.settings.freq,
|
||||
.descr = "Frequency for fixed host ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_FIXED_FMT",
|
||||
.tag = AUD_OPT_FMT,
|
||||
.valp = &conf.fixed_in.settings.fmt,
|
||||
.descr = "Format for fixed host ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_FIXED_CHANNELS",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_in.settings.nchannels,
|
||||
.descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
|
||||
},
|
||||
{
|
||||
.name = "ADC_VOICES",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.fixed_in.nb_voices,
|
||||
.descr = "Number of voices for ADC"
|
||||
},
|
||||
{
|
||||
.name = "ADC_TRY_POLL",
|
||||
.tag = AUD_OPT_BOOL,
|
||||
.valp = &conf.try_poll_in,
|
||||
.descr = "Attempt using poll mode for ADC"
|
||||
},
|
||||
/* Misc */
|
||||
{
|
||||
.name = "TIMER_PERIOD",
|
||||
.tag = AUD_OPT_INT,
|
||||
.valp = &conf.period.hertz,
|
||||
.descr = "Timer period in HZ (0 - use lowest possible)"
|
||||
},
|
||||
{ /* End of list */ }
|
||||
};
|
||||
|
||||
static void audio_pp_nb_voices (const char *typ, int nb)
|
||||
{
|
||||
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=wav\n"
|
||||
" set QEMU_WAV_PATH=c:\\tune.wav\n"
|
||||
#else
|
||||
" export QEMU_AUDIO_DRV=wav\n"
|
||||
" export QEMU_WAV_PATH=$HOME/tune.wav\n"
|
||||
"(for csh replace export with setenv in the above)\n"
|
||||
#endif
|
||||
" qemu ...\n\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg)
|
||||
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
|
||||
bool msg, Audiodev *dev)
|
||||
{
|
||||
if (drv->options) {
|
||||
audio_process_options (drv->name, drv->options);
|
||||
}
|
||||
s->drv_opaque = drv->init ();
|
||||
s->drv_opaque = drv->init(dev);
|
||||
|
||||
if (s->drv_opaque) {
|
||||
audio_init_nb_voices_out (drv);
|
||||
@ -1782,11 +1470,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
|
||||
|
||||
s->vm_running = running;
|
||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
|
||||
hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
|
||||
hwo->pcm_ops->ctl_out(hwo, op, true);
|
||||
}
|
||||
|
||||
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
|
||||
hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
|
||||
hwi->pcm_ops->ctl_in(hwi, op, true);
|
||||
}
|
||||
audio_reset_timer (s);
|
||||
}
|
||||
@ -1836,6 +1524,11 @@ void audio_cleanup(void)
|
||||
s->drv->fini (s->drv_opaque);
|
||||
s->drv = NULL;
|
||||
}
|
||||
|
||||
if (s->dev) {
|
||||
qapi_free_Audiodev(s->dev);
|
||||
s->dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_audio = {
|
||||
@ -1847,19 +1540,58 @@ static const VMStateDescription vmstate_audio = {
|
||||
}
|
||||
};
|
||||
|
||||
static void audio_init (void)
|
||||
static void audio_validate_opts(Audiodev *dev, Error **errp);
|
||||
|
||||
static AudiodevListEntry *audiodev_find(
|
||||
AudiodevListHead *head, const char *drvname)
|
||||
{
|
||||
AudiodevListEntry *e;
|
||||
QSIMPLEQ_FOREACH(e, head, next) {
|
||||
if (strcmp(AudiodevDriver_str(e->dev->driver), drvname) == 0) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int audio_init(Audiodev *dev)
|
||||
{
|
||||
size_t i;
|
||||
int done = 0;
|
||||
const char *drvname;
|
||||
const char *drvname = NULL;
|
||||
VMChangeStateEntry *e;
|
||||
AudioState *s = &glob_audio_state;
|
||||
struct audio_driver *driver;
|
||||
/* silence gcc warning about uninitialized variable */
|
||||
AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
|
||||
|
||||
if (s->drv) {
|
||||
return;
|
||||
if (dev) {
|
||||
dolog("Cannot create more than one audio backend, sorry\n");
|
||||
qapi_free_Audiodev(dev);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
/* -audiodev option */
|
||||
drvname = AudiodevDriver_str(dev->driver);
|
||||
} else {
|
||||
/* legacy implicit initialization */
|
||||
head = audio_handle_legacy_opts();
|
||||
/*
|
||||
* In case of legacy initialization, all Audiodevs in the list will have
|
||||
* the same configuration (except the driver), so it does't matter which
|
||||
* one we chose. We need an Audiodev to set up AudioState before we can
|
||||
* init a driver. Also note that dev at this point is still in the
|
||||
* list.
|
||||
*/
|
||||
dev = QSIMPLEQ_FIRST(&head)->dev;
|
||||
audio_validate_opts(dev, &error_abort);
|
||||
}
|
||||
s->dev = dev;
|
||||
|
||||
QLIST_INIT (&s->hw_head_out);
|
||||
QLIST_INIT (&s->hw_head_in);
|
||||
QLIST_INIT (&s->cap_head);
|
||||
@ -1867,10 +1599,8 @@ static void audio_init (void)
|
||||
|
||||
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
|
||||
|
||||
audio_process_options ("AUDIO", audio_options);
|
||||
|
||||
s->nb_hw_voices_out = conf.fixed_out.nb_voices;
|
||||
s->nb_hw_voices_in = conf.fixed_in.nb_voices;
|
||||
s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices;
|
||||
s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices;
|
||||
|
||||
if (s->nb_hw_voices_out <= 0) {
|
||||
dolog ("Bogus number of playback voices %d, setting to 1\n",
|
||||
@ -1884,46 +1614,42 @@ static void audio_init (void)
|
||||
s->nb_hw_voices_in = 0;
|
||||
}
|
||||
|
||||
{
|
||||
int def;
|
||||
drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
|
||||
}
|
||||
|
||||
if (drvname) {
|
||||
driver = audio_driver_lookup(drvname);
|
||||
if (driver) {
|
||||
done = !audio_driver_init(s, driver, true);
|
||||
done = !audio_driver_init(s, driver, true, dev);
|
||||
} else {
|
||||
dolog ("Unknown audio driver `%s'\n", drvname);
|
||||
dolog ("Run with -audio-help to list available drivers\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) {
|
||||
} else {
|
||||
for (i = 0; audio_prio_list[i]; i++) {
|
||||
AudiodevListEntry *e = audiodev_find(&head, audio_prio_list[i]);
|
||||
driver = audio_driver_lookup(audio_prio_list[i]);
|
||||
if (driver && driver->can_be_default) {
|
||||
done = !audio_driver_init(s, driver, false);
|
||||
|
||||
if (e && driver) {
|
||||
s->dev = dev = e->dev;
|
||||
audio_validate_opts(dev, &error_abort);
|
||||
done = !audio_driver_init(s, driver, false, dev);
|
||||
if (done) {
|
||||
e->dev = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
audio_free_audiodev_list(&head);
|
||||
|
||||
if (!done) {
|
||||
driver = audio_driver_lookup("none");
|
||||
done = !audio_driver_init(s, driver, false);
|
||||
done = !audio_driver_init(s, driver, false, dev);
|
||||
assert(done);
|
||||
dolog("warning: Using timer based audio emulation\n");
|
||||
}
|
||||
|
||||
if (conf.period.hertz <= 0) {
|
||||
if (conf.period.hertz < 0) {
|
||||
dolog ("warning: Timer period is negative - %d "
|
||||
"treating as zero\n",
|
||||
conf.period.hertz);
|
||||
}
|
||||
conf.period.ticks = 1;
|
||||
if (dev->timer_period <= 0) {
|
||||
s->period_ticks = 1;
|
||||
} else {
|
||||
conf.period.ticks = NANOSECONDS_PER_SECOND / conf.period.hertz;
|
||||
s->period_ticks = NANOSECONDS_PER_SECOND / dev->timer_period;
|
||||
}
|
||||
|
||||
e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
|
||||
@ -1934,11 +1660,22 @@ static void audio_init (void)
|
||||
|
||||
QLIST_INIT (&s->card_head);
|
||||
vmstate_register (NULL, 0, &vmstate_audio, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void audio_free_audiodev_list(AudiodevListHead *head)
|
||||
{
|
||||
AudiodevListEntry *e;
|
||||
while ((e = QSIMPLEQ_FIRST(head))) {
|
||||
QSIMPLEQ_REMOVE_HEAD(head, next);
|
||||
qapi_free_Audiodev(e->dev);
|
||||
g_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
void AUD_register_card (const char *name, QEMUSoundCard *card)
|
||||
{
|
||||
audio_init ();
|
||||
audio_init(NULL);
|
||||
card->name = g_strdup (name);
|
||||
memset (&card->entries, 0, sizeof (card->entries));
|
||||
QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries);
|
||||
@ -2078,3 +1815,174 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_create_pdos(Audiodev *dev)
|
||||
{
|
||||
switch (dev->driver) {
|
||||
#define CASE(DRIVER, driver, pdo_name) \
|
||||
case AUDIODEV_DRIVER_##DRIVER: \
|
||||
if (!dev->u.driver.has_in) { \
|
||||
dev->u.driver.in = g_malloc0( \
|
||||
sizeof(Audiodev##pdo_name##PerDirectionOptions)); \
|
||||
dev->u.driver.has_in = true; \
|
||||
} \
|
||||
if (!dev->u.driver.has_out) { \
|
||||
dev->u.driver.out = g_malloc0( \
|
||||
sizeof(AudiodevAlsaPerDirectionOptions)); \
|
||||
dev->u.driver.has_out = true; \
|
||||
} \
|
||||
break
|
||||
|
||||
CASE(NONE, none, );
|
||||
CASE(ALSA, alsa, Alsa);
|
||||
CASE(COREAUDIO, coreaudio, Coreaudio);
|
||||
CASE(DSOUND, dsound, );
|
||||
CASE(OSS, oss, Oss);
|
||||
CASE(PA, pa, Pa);
|
||||
CASE(SDL, sdl, );
|
||||
CASE(SPICE, spice, );
|
||||
CASE(WAV, wav, );
|
||||
|
||||
case AUDIODEV_DRIVER__MAX:
|
||||
abort();
|
||||
};
|
||||
}
|
||||
|
||||
static void audio_validate_per_direction_opts(
|
||||
AudiodevPerDirectionOptions *pdo, Error **errp)
|
||||
{
|
||||
if (!pdo->has_fixed_settings) {
|
||||
pdo->has_fixed_settings = true;
|
||||
pdo->fixed_settings = true;
|
||||
}
|
||||
if (!pdo->fixed_settings &&
|
||||
(pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
|
||||
error_setg(errp,
|
||||
"You can't use frequency, channels or format with fixed-settings=off");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pdo->has_frequency) {
|
||||
pdo->has_frequency = true;
|
||||
pdo->frequency = 44100;
|
||||
}
|
||||
if (!pdo->has_channels) {
|
||||
pdo->has_channels = true;
|
||||
pdo->channels = 2;
|
||||
}
|
||||
if (!pdo->has_voices) {
|
||||
pdo->has_voices = true;
|
||||
pdo->voices = 1;
|
||||
}
|
||||
if (!pdo->has_format) {
|
||||
pdo->has_format = true;
|
||||
pdo->format = AUDIO_FORMAT_S16;
|
||||
}
|
||||
}
|
||||
|
||||
static void audio_validate_opts(Audiodev *dev, Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
|
||||
audio_create_pdos(dev);
|
||||
|
||||
audio_validate_per_direction_opts(audio_get_pdo_in(dev), &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
audio_validate_per_direction_opts(audio_get_pdo_out(dev), &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dev->has_timer_period) {
|
||||
dev->has_timer_period = true;
|
||||
dev->timer_period = 10000; /* 100Hz -> 10ms */
|
||||
}
|
||||
}
|
||||
|
||||
void audio_parse_option(const char *opt)
|
||||
{
|
||||
AudiodevListEntry *e;
|
||||
Audiodev *dev = NULL;
|
||||
|
||||
Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal);
|
||||
visit_type_Audiodev(v, NULL, &dev, &error_fatal);
|
||||
visit_free(v);
|
||||
|
||||
audio_validate_opts(dev, &error_fatal);
|
||||
|
||||
e = g_malloc0(sizeof(AudiodevListEntry));
|
||||
e->dev = dev;
|
||||
QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
|
||||
}
|
||||
|
||||
void audio_init_audiodevs(void)
|
||||
{
|
||||
AudiodevListEntry *e;
|
||||
|
||||
QSIMPLEQ_FOREACH(e, &audiodevs, next) {
|
||||
audio_init(e->dev);
|
||||
}
|
||||
}
|
||||
|
||||
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
|
||||
{
|
||||
return (audsettings) {
|
||||
.freq = pdo->frequency,
|
||||
.nchannels = pdo->channels,
|
||||
.fmt = pdo->format,
|
||||
.endianness = AUDIO_HOST_ENDIANNESS,
|
||||
};
|
||||
}
|
||||
|
||||
int audioformat_bytes_per_sample(AudioFormat fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case AUDIO_FORMAT_U8:
|
||||
case AUDIO_FORMAT_S8:
|
||||
return 1;
|
||||
|
||||
case AUDIO_FORMAT_U16:
|
||||
case AUDIO_FORMAT_S16:
|
||||
return 2;
|
||||
|
||||
case AUDIO_FORMAT_U32:
|
||||
case AUDIO_FORMAT_S32:
|
||||
return 4;
|
||||
|
||||
case AUDIO_FORMAT__MAX:
|
||||
;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
/* frames = freq * usec / 1e6 */
|
||||
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs)
|
||||
{
|
||||
uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs;
|
||||
return (as->freq * usecs + 500000) / 1000000;
|
||||
}
|
||||
|
||||
/* samples = channels * frames = channels * freq * usec / 1e6 */
|
||||
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs)
|
||||
{
|
||||
return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);
|
||||
}
|
||||
|
||||
/*
|
||||
* bytes = bytes_per_sample * samples =
|
||||
* bytes_per_sample * channels * freq * usec / 1e6
|
||||
*/
|
||||
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs)
|
||||
{
|
||||
return audio_buffer_samples(pdo, as, def_usecs) *
|
||||
audioformat_bytes_per_sample(as->fmt);
|
||||
}
|
||||
|
@ -36,12 +36,21 @@ typedef void (*audio_callback_fn) (void *opaque, int avail);
|
||||
#define AUDIO_HOST_ENDIANNESS 0
|
||||
#endif
|
||||
|
||||
struct audsettings {
|
||||
typedef struct audsettings {
|
||||
int freq;
|
||||
int nchannels;
|
||||
AudioFormat fmt;
|
||||
int endianness;
|
||||
};
|
||||
} audsettings;
|
||||
|
||||
audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo);
|
||||
int audioformat_bytes_per_sample(AudioFormat fmt);
|
||||
int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs);
|
||||
int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs);
|
||||
int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
|
||||
audsettings *as, int def_usecs);
|
||||
|
||||
typedef enum {
|
||||
AUD_CNOTIFY_ENABLE,
|
||||
@ -81,7 +90,6 @@ typedef struct QEMUAudioTimeStamp {
|
||||
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
|
||||
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
|
||||
|
||||
void AUD_help (void);
|
||||
void AUD_register_card (const char *name, QEMUSoundCard *card);
|
||||
void AUD_remove_card (QEMUSoundCard *card);
|
||||
CaptureVoiceOut *AUD_add_capture (
|
||||
@ -163,4 +171,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);
|
||||
|
||||
void audio_parse_option(const char *opt);
|
||||
void audio_init_audiodevs(void);
|
||||
void audio_legacy_help(void);
|
||||
|
||||
#endif /* QEMU_AUDIO_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 {
|
||||
|
||||
typedef struct AudioState {
|
||||
struct audio_driver *drv;
|
||||
Audiodev *dev;
|
||||
void *drv_opaque;
|
||||
|
||||
QEMUTimer *ts;
|
||||
@ -203,10 +204,13 @@ typedef struct AudioState {
|
||||
int nb_hw_voices_out;
|
||||
int nb_hw_voices_in;
|
||||
int vm_running;
|
||||
int64_t period_ticks;
|
||||
} AudioState;
|
||||
|
||||
extern const struct mixeng_volume nominal_volume;
|
||||
|
||||
extern const char *audio_prio_list[];
|
||||
|
||||
void audio_driver_register(audio_driver *drv);
|
||||
audio_driver *audio_driver_lookup(const char *name);
|
||||
|
||||
@ -248,4 +252,18 @@ static inline int audio_ring_dist (int dst, int src, int len)
|
||||
#define AUDIO_STRINGIFY_(n) #n
|
||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
|
||||
|
||||
typedef struct AudiodevListEntry {
|
||||
Audiodev *dev;
|
||||
QSIMPLEQ_ENTRY(AudiodevListEntry) next;
|
||||
} AudiodevListEntry;
|
||||
|
||||
typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead;
|
||||
AudiodevListHead audio_handle_legacy_opts(void);
|
||||
|
||||
void audio_free_audiodev_list(AudiodevListHead *head);
|
||||
|
||||
void audio_create_pdos(Audiodev *dev);
|
||||
AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev);
|
||||
AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev);
|
||||
|
||||
#endif /* QEMU_AUDIO_INT_H */
|
||||
|
293
audio/audio_legacy.c
Normal file
293
audio/audio_legacy.c
Normal file
@ -0,0 +1,293 @@
|
||||
/*
|
||||
* QEMU Audio subsystem: legacy configuration handling
|
||||
*
|
||||
* Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "audio.h"
|
||||
#include "audio_int.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-visit-audio.h"
|
||||
#include "qapi/visitor-impl.h"
|
||||
|
||||
#define AUDIO_CAP "audio-legacy"
|
||||
#include "audio_int.h"
|
||||
|
||||
static uint32_t toui32(const char *str)
|
||||
{
|
||||
unsigned long long ret;
|
||||
if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) {
|
||||
dolog("Invalid integer value `%s'\n", str);
|
||||
exit(1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* helper functions to convert env variables */
|
||||
static void get_bool(const char *env, bool *dst, bool *has_dst)
|
||||
{
|
||||
const char *val = getenv(env);
|
||||
if (val) {
|
||||
*dst = toui32(val) != 0;
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_int(const char *env, uint32_t *dst, bool *has_dst)
|
||||
{
|
||||
const char *val = getenv(env);
|
||||
if (val) {
|
||||
*dst = toui32(val);
|
||||
*has_dst = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst)
|
||||
{
|
||||
const char *val = getenv(env);
|
||||
if (val) {
|
||||
size_t i;
|
||||
for (i = 0; AudioFormat_lookup.size; ++i) {
|
||||
if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) {
|
||||
*dst = i;
|
||||
*has_dst = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dolog("Invalid audio format `%s'\n", val);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* backend specific functions */
|
||||
/* todo */
|
||||
|
||||
/* general */
|
||||
static void handle_per_direction(
|
||||
AudiodevPerDirectionOptions *pdo, const char *prefix)
|
||||
{
|
||||
char buf[64];
|
||||
size_t len = strlen(prefix);
|
||||
|
||||
memcpy(buf, prefix, len);
|
||||
strcpy(buf + len, "FIXED_SETTINGS");
|
||||
get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings);
|
||||
|
||||
strcpy(buf + len, "FIXED_FREQ");
|
||||
get_int(buf, &pdo->frequency, &pdo->has_frequency);
|
||||
|
||||
strcpy(buf + len, "FIXED_FMT");
|
||||
get_fmt(buf, &pdo->format, &pdo->has_format);
|
||||
|
||||
strcpy(buf + len, "FIXED_CHANNELS");
|
||||
get_int(buf, &pdo->channels, &pdo->has_channels);
|
||||
|
||||
strcpy(buf + len, "VOICES");
|
||||
get_int(buf, &pdo->voices, &pdo->has_voices);
|
||||
}
|
||||
|
||||
static AudiodevListEntry *legacy_opt(const char *drvname)
|
||||
{
|
||||
AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry));
|
||||
e->dev = g_malloc0(sizeof(Audiodev));
|
||||
e->dev->id = g_strdup(drvname);
|
||||
e->dev->driver = qapi_enum_parse(
|
||||
&AudiodevDriver_lookup, drvname, -1, &error_abort);
|
||||
|
||||
audio_create_pdos(e->dev);
|
||||
|
||||
handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_");
|
||||
handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_");
|
||||
|
||||
get_int("QEMU_AUDIO_TIMER_PERIOD",
|
||||
&e->dev->timer_period, &e->dev->has_timer_period);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
AudiodevListHead audio_handle_legacy_opts(void)
|
||||
{
|
||||
const char *drvname = getenv("QEMU_AUDIO_DRV");
|
||||
AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
|
||||
|
||||
if (drvname) {
|
||||
AudiodevListEntry *e;
|
||||
audio_driver *driver = audio_driver_lookup(drvname);
|
||||
if (!driver) {
|
||||
dolog("Unknown audio driver `%s'\n", drvname);
|
||||
exit(1);
|
||||
}
|
||||
e = legacy_opt(drvname);
|
||||
QSIMPLEQ_INSERT_TAIL(&head, e, next);
|
||||
} else {
|
||||
for (int i = 0; audio_prio_list[i]; i++) {
|
||||
audio_driver *driver = audio_driver_lookup(audio_prio_list[i]);
|
||||
if (driver && driver->can_be_default) {
|
||||
AudiodevListEntry *e = legacy_opt(driver->name);
|
||||
QSIMPLEQ_INSERT_TAIL(&head, e, next);
|
||||
}
|
||||
}
|
||||
if (QSIMPLEQ_EMPTY(&head)) {
|
||||
dolog("Internal error: no default audio driver available\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
/* visitor to print -audiodev option */
|
||||
typedef struct {
|
||||
Visitor visitor;
|
||||
|
||||
bool comma;
|
||||
GList *path;
|
||||
} LegacyPrintVisitor;
|
||||
|
||||
static void lv_start_struct(Visitor *v, const char *name, void **obj,
|
||||
size_t size, Error **errp)
|
||||
{
|
||||
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||
lv->path = g_list_append(lv->path, g_strdup(name));
|
||||
}
|
||||
|
||||
static void lv_end_struct(Visitor *v, void **obj)
|
||||
{
|
||||
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||
lv->path = g_list_delete_link(lv->path, g_list_last(lv->path));
|
||||
}
|
||||
|
||||
static void lv_print_key(Visitor *v, const char *name)
|
||||
{
|
||||
GList *e;
|
||||
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||
if (lv->comma) {
|
||||
putchar(',');
|
||||
} else {
|
||||
lv->comma = true;
|
||||
}
|
||||
|
||||
for (e = lv->path; e; e = e->next) {
|
||||
if (e->data) {
|
||||
printf("%s.", (const char *) e->data);
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s=", name);
|
||||
}
|
||||
|
||||
static void lv_type_int64(Visitor *v, const char *name, int64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
lv_print_key(v, name);
|
||||
printf("%" PRIi64, *obj);
|
||||
}
|
||||
|
||||
static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
lv_print_key(v, name);
|
||||
printf("%" PRIu64, *obj);
|
||||
}
|
||||
|
||||
static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
|
||||
{
|
||||
lv_print_key(v, name);
|
||||
printf("%s", *obj ? "on" : "off");
|
||||
}
|
||||
|
||||
static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp)
|
||||
{
|
||||
const char *str = *obj;
|
||||
lv_print_key(v, name);
|
||||
|
||||
while (*str) {
|
||||
if (*str == ',') {
|
||||
putchar(',');
|
||||
}
|
||||
putchar(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
static void lv_complete(Visitor *v, void *opaque)
|
||||
{
|
||||
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||
assert(lv->path == NULL);
|
||||
}
|
||||
|
||||
static void lv_free(Visitor *v)
|
||||
{
|
||||
LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v;
|
||||
|
||||
g_list_free_full(lv->path, g_free);
|
||||
g_free(lv);
|
||||
}
|
||||
|
||||
static Visitor *legacy_visitor_new(void)
|
||||
{
|
||||
LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor));
|
||||
|
||||
lv->visitor.start_struct = lv_start_struct;
|
||||
lv->visitor.end_struct = lv_end_struct;
|
||||
/* lists not supported */
|
||||
lv->visitor.type_int64 = lv_type_int64;
|
||||
lv->visitor.type_uint64 = lv_type_uint64;
|
||||
lv->visitor.type_bool = lv_type_bool;
|
||||
lv->visitor.type_str = lv_type_str;
|
||||
|
||||
lv->visitor.type = VISITOR_OUTPUT;
|
||||
lv->visitor.complete = lv_complete;
|
||||
lv->visitor.free = lv_free;
|
||||
|
||||
return &lv->visitor;
|
||||
}
|
||||
|
||||
void audio_legacy_help(void)
|
||||
{
|
||||
AudiodevListHead head;
|
||||
AudiodevListEntry *e;
|
||||
|
||||
printf("Environment variable based configuration deprecated.\n");
|
||||
printf("Please use the new -audiodev option.\n");
|
||||
|
||||
head = audio_handle_legacy_opts();
|
||||
printf("\nEquivalent -audiodev to your current environment variables:\n");
|
||||
if (!getenv("QEMU_AUDIO_DRV")) {
|
||||
printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all "
|
||||
"possibilities)\n");
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(e, &head, next) {
|
||||
Visitor *v;
|
||||
Audiodev *dev = e->dev;
|
||||
printf("-audiodev ");
|
||||
|
||||
v = legacy_visitor_new();
|
||||
visit_type_Audiodev(v, NULL, &dev, &error_abort);
|
||||
visit_free(v);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
audio_free_audiodev_list(&head);
|
||||
}
|
@ -299,11 +299,42 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
|
||||
{
|
||||
switch (dev->driver) {
|
||||
case AUDIODEV_DRIVER_NONE:
|
||||
return dev->u.none.TYPE;
|
||||
case AUDIODEV_DRIVER_ALSA:
|
||||
return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
|
||||
case AUDIODEV_DRIVER_COREAUDIO:
|
||||
return qapi_AudiodevCoreaudioPerDirectionOptions_base(
|
||||
dev->u.coreaudio.TYPE);
|
||||
case AUDIODEV_DRIVER_DSOUND:
|
||||
return dev->u.dsound.TYPE;
|
||||
case AUDIODEV_DRIVER_OSS:
|
||||
return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
|
||||
case AUDIODEV_DRIVER_PA:
|
||||
return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
|
||||
case AUDIODEV_DRIVER_SDL:
|
||||
return dev->u.sdl.TYPE;
|
||||
case AUDIODEV_DRIVER_SPICE:
|
||||
return dev->u.spice.TYPE;
|
||||
case AUDIODEV_DRIVER_WAV:
|
||||
return dev->u.wav.TYPE;
|
||||
|
||||
case AUDIODEV_DRIVER__MAX:
|
||||
break;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
|
||||
{
|
||||
HW *hw;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
|
||||
if (pdo->fixed_settings) {
|
||||
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
||||
if (hw) {
|
||||
return hw;
|
||||
@ -331,9 +362,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||
SW *sw;
|
||||
HW *hw;
|
||||
struct audsettings hw_as;
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (glue (conf.fixed_, TYPE).enabled) {
|
||||
hw_as = glue (conf.fixed_, TYPE).settings;
|
||||
if (pdo->fixed_settings) {
|
||||
hw_as = audiodev_to_audsettings(pdo);
|
||||
}
|
||||
else {
|
||||
hw_as = *as;
|
||||
@ -398,6 +431,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||
)
|
||||
{
|
||||
AudioState *s = &glob_audio_state;
|
||||
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||
|
||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||
@ -422,7 +456,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||
return sw;
|
||||
}
|
||||
|
||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
||||
if (!pdo->fixed_settings && sw) {
|
||||
glue (AUD_close_, TYPE) (card, sw);
|
||||
sw = NULL;
|
||||
}
|
||||
|
@ -685,7 +685,7 @@ static CoreaudioConf glob_conf = {
|
||||
.nbuffers = 4,
|
||||
};
|
||||
|
||||
static void *coreaudio_audio_init (void)
|
||||
static void *coreaudio_audio_init(Audiodev *dev)
|
||||
{
|
||||
CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
|
||||
*conf = glob_conf;
|
||||
|
@ -783,7 +783,7 @@ static void dsound_audio_fini (void *opaque)
|
||||
g_free(s);
|
||||
}
|
||||
|
||||
static void *dsound_audio_init (void)
|
||||
static void *dsound_audio_init(Audiodev *dev)
|
||||
{
|
||||
int err;
|
||||
HRESULT hr;
|
||||
|
@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *no_audio_init (void)
|
||||
static void *no_audio_init(Audiodev *dev)
|
||||
{
|
||||
return &no_audio_init;
|
||||
}
|
||||
|
@ -842,7 +842,7 @@ static OSSConf glob_conf = {
|
||||
.policy = 5
|
||||
};
|
||||
|
||||
static void *oss_audio_init (void)
|
||||
static void *oss_audio_init(Audiodev *dev)
|
||||
{
|
||||
OSSConf *conf = g_malloc(sizeof(OSSConf));
|
||||
*conf = glob_conf;
|
||||
|
@ -812,7 +812,7 @@ static PAConf glob_conf = {
|
||||
.samples = 4096,
|
||||
};
|
||||
|
||||
static void *qpa_audio_init (void)
|
||||
static void *qpa_audio_init(Audiodev *dev)
|
||||
{
|
||||
if (glob_conf.server == NULL) {
|
||||
char pidfile[64];
|
||||
|
@ -315,7 +315,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *sdl_audio_init (void)
|
||||
static void *sdl_audio_init(Audiodev *dev)
|
||||
{
|
||||
SDLAudioState *s = &glob_sdl;
|
||||
if (s->driver_created) {
|
||||
|
@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
|
||||
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
||||
};
|
||||
|
||||
static void *spice_audio_init (void)
|
||||
static void *spice_audio_init(Audiodev *dev)
|
||||
{
|
||||
if (!using_spice) {
|
||||
return NULL;
|
||||
|
@ -232,7 +232,7 @@ static WAVConf glob_conf = {
|
||||
.wav_path = "qemu.wav"
|
||||
};
|
||||
|
||||
static void *wav_audio_init (void)
|
||||
static void *wav_audio_init(Audiodev *dev)
|
||||
{
|
||||
WAVConf *conf = g_malloc(sizeof(WAVConf));
|
||||
*conf = glob_conf;
|
||||
|
7
vl.c
7
vl.c
@ -3253,9 +3253,12 @@ 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:
|
||||
audio_parse_option(optarg);
|
||||
break;
|
||||
case QEMU_OPTION_soundhw:
|
||||
select_soundhw (optarg);
|
||||
break;
|
||||
@ -4447,6 +4450,8 @@ int main(int argc, char **argv, char **envp)
|
||||
/* do monitor/qmp handling at preconfig state if requested */
|
||||
main_loop();
|
||||
|
||||
audio_init_audiodevs();
|
||||
|
||||
/* from here on runstate is RUN_STATE_PRELAUNCH */
|
||||
machine_run_board_init(current_machine);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user