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