diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2019-03-12 16:45:13 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2019-03-12 16:45:13 +0000 |
commit | cfc3fef6b4e493bf1a7ee16790ad584e20dfbbd1 (patch) | |
tree | 7092a7ad69eb6676bb66ded90d94889bfeba28c4 | |
parent | 2cb73afa6a2408b397a5af1427d120b8aa04997a (diff) | |
parent | 05d2f2a64dbcaa50370d344ab12081d776ed0f03 (diff) |
Merge remote-tracking branch 'remotes/kraxel/tags/audio-20190312-pull-request' into staging
audio: introduce -audiodev
# gpg: Signature made Tue 12 Mar 2019 07:12:19 GMT
# gpg: using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg: aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg: aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901 FE7D 4CB6 D8EE D3E8 7138
* remotes/kraxel/tags/audio-20190312-pull-request:
audio: -audiodev command line option: cleanup
wavaudio: port to -audiodev config
spiceaudio: port to -audiodev config
sdlaudio: port to -audiodev config
paaudio: port to -audiodev config
ossaudio: port to -audiodev config
noaudio: port to -audiodev config
dsoundaudio: port to -audiodev config
coreaudio: port to -audiodev config
alsaaudio: port to -audiodev config
audio: -audiodev command line option basic implementation
audio: -audiodev command line option: documentation
audio: use qapi AudioFormat instead of audfmt_e
qapi: qapi for audio backends
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
# Conflicts:
# qemu-deprecated.texi
40 files changed, 1835 insertions, 1287 deletions
diff --git a/audio/Makefile.objs b/audio/Makefile.objs index db4fa7f18f..dca87f6347 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o +common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o common-obj-$(CONFIG_SPICE) += spiceaudio.o common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 635be73bf4..49e6884309 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -33,28 +33,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; }; @@ -66,6 +47,7 @@ typedef struct ALSAVoiceOut { void *pcm_buf; snd_pcm_t *handle; struct pollhlp pollhlp; + Audiodev *dev; } ALSAVoiceOut; typedef struct ALSAVoiceIn { @@ -73,21 +55,18 @@ 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 { int freq; - audfmt_e fmt; + AudioFormat fmt; int endianness; int nchannels; snd_pcm_uframes_t samples; @@ -294,16 +273,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) +static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) { switch (fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: return SND_PCM_FORMAT_S8; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: return SND_PCM_FORMAT_U8; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: if (endianness) { return SND_PCM_FORMAT_S16_BE; } @@ -311,7 +290,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) return SND_PCM_FORMAT_S16_LE; } - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: if (endianness) { return SND_PCM_FORMAT_U16_BE; } @@ -319,7 +298,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) return SND_PCM_FORMAT_U16_LE; } - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: if (endianness) { return SND_PCM_FORMAT_S32_BE; } @@ -327,7 +306,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) return SND_PCM_FORMAT_S32_LE; } - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: if (endianness) { return SND_PCM_FORMAT_U32_BE; } @@ -344,58 +323,58 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) } } -static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, +static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, int *endianness) { switch (alsafmt) { case SND_PCM_FORMAT_S8: *endianness = 0; - *fmt = AUD_FMT_S8; + *fmt = AUDIO_FORMAT_S8; break; case SND_PCM_FORMAT_U8: *endianness = 0; - *fmt = AUD_FMT_U8; + *fmt = AUDIO_FORMAT_U8; break; case SND_PCM_FORMAT_S16_LE: *endianness = 0; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case SND_PCM_FORMAT_U16_LE: *endianness = 0; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case SND_PCM_FORMAT_S16_BE: *endianness = 1; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case SND_PCM_FORMAT_U16_BE: *endianness = 1; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case SND_PCM_FORMAT_S32_LE: *endianness = 0; - *fmt = AUD_FMT_S32; + *fmt = AUDIO_FORMAT_S32; break; case SND_PCM_FORMAT_U32_LE: *endianness = 0; - *fmt = AUD_FMT_U32; + *fmt = AUDIO_FORMAT_U32; break; case SND_PCM_FORMAT_S32_BE: *endianness = 1; - *fmt = AUD_FMT_S32; + *fmt = AUDIO_FORMAT_S32; break; case SND_PCM_FORMAT_U32_BE: *endianness = 1; - *fmt = AUD_FMT_U32; + *fmt = AUDIO_FORMAT_U32; break; default: @@ -408,17 +387,18 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *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, + AudiodevAlsaPerDirectionOptions *apdo) { - dolog ("parameter | requested value | obtained value\n"); - dolog ("format | %10d | %10d\n", req->fmt, obtfmt); - dolog ("channels | %10d | %10d\n", - 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 ("obtained: samples %ld\n", obt->samples); + dolog("parameter | requested value | obtained value\n"); + dolog("format | %10d | %10d\n", req->fmt, obtfmt); + dolog("channels | %10d | %10d\n", + req->nchannels, obt->nchannels); + dolog("frequency | %10d | %10d\n", req->freq, obt->freq); + dolog("============================================\n"); + dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n", + apdo->buffer_length, apdo->period_length); + dolog("obtained: samples %ld\n", obt->samples); } static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) @@ -451,23 +431,23 @@ 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) { + AudiodevAlsaOptions *aopts = &dev->u.alsa; + AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->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); @@ -527,79 +507,42 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - if (req->buffer_size) { - unsigned long obt; + if (apdo->buffer_length) { + int dir = 0; + unsigned int btime = apdo->buffer_length; - if (size_in_usec) { - int dir = 0; - unsigned int btime = req->buffer_size; + err = snd_pcm_hw_params_set_buffer_time_near( + handle, hw_params, &btime, &dir); - 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_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); + alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n", + apdo->buffer_length); 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 (apdo->has_buffer_length && btime != apdo->buffer_length) { + dolog("Requested buffer time %" PRId32 + " was rejected, using %u\n", apdo->buffer_length, btime); + } } - if (req->period_size) { - unsigned long obt; + if (apdo->period_length) { + int dir = 0; + unsigned int ptime = apdo->period_length; - 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 %" PRId32 "\n", + apdo->period_length); 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 (apdo->has_period_length && ptime != apdo->period_length) { + dolog("Requested period time %" PRId32 " was rejected, using %d\n", + apdo->period_length, ptime); + } } err = snd_pcm_hw_params (handle, hw_params); @@ -631,30 +574,12 @@ 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 AUD_FMT_S8: - case AUD_FMT_U8: - break; - - case AUD_FMT_S16: - case AUD_FMT_U16: - bytes_per_sec <<= 1; - break; - - case AUD_FMT_S32: - case AUD_FMT_U32: - bytes_per_sec <<= 2; - break; - } - - 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(qapi_AudiodevAlsaPerDirectionOptions_base(apdo), + &as, aopts->threshold)); } obt->nchannels = nchannels; @@ -667,11 +592,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, apdo); } #ifdef DEBUG - alsa_dump_info (req, obt, obtfmt); + alsa_dump_info(req, obt, obtfmt, pdo); #endif return 0; @@ -797,19 +722,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; } @@ -830,7 +749,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, } alsa->handle = handle; - alsa->pollhlp.conf = conf; + alsa->dev = dev; return 0; } @@ -870,16 +789,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->u.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->try_poll; ldebug ("enabling voice\n"); if (poll_mode && alsa_poll_out (hw)) { @@ -908,19 +823,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; } @@ -941,7 +850,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; } @@ -1083,16 +992,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->u.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->try_poll; ldebug ("enabling voice\n"); if (poll_mode && alsa_poll_in (hw)) { @@ -1115,88 +1020,54 @@ 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_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) +{ + if (!apdo->has_try_poll) { + apdo->try_poll = true; + apdo->has_try_poll = true; + } +} -static void *alsa_audio_init (void) +static void *alsa_audio_init(Audiodev *dev) { - ALSAConf *conf = g_malloc(sizeof(ALSAConf)); - *conf = glob_conf; - return conf; + AudiodevAlsaOptions *aopts; + assert(dev->driver == AUDIODEV_DRIVER_ALSA); + + aopts = &dev->u.alsa; + alsa_init_per_direction(aopts->in); + alsa_init_per_direction(aopts->out); + + /* + * 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->u.alsa.out->has_period_length) { + /* 1024 frames assuming 44100Hz */ + dev->u.alsa.out->period_length = 1024 * 1000000 / 44100; + } + if (!dev->u.alsa.out->has_buffer_length) { + /* 4096 frames assuming 44100Hz */ + dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100; + } + + /* + * OptsVisitor sets unspecified optional fields to zero, but do not depend + * on it... + */ + if (!dev->u.alsa.in->has_period_length) { + dev->u.alsa.in->period_length = 0; + } + if (!dev->u.alsa.in->has_buffer_length) { + dev->u.alsa.in->buffer_length = 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, @@ -1214,7 +1085,6 @@ static struct audio_pcm_ops alsa_pcm_ops = { static 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 909c817103..5fd9a58a80 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -26,6 +26,9 @@ #include "audio.h" #include "monitor/monitor.h" #include "qemu/timer.h" +#include "qapi/error.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-audio.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "sysemu/replay.h" @@ -46,14 +49,16 @@ The 1st one is the one used by default, that is the reason that we generate the list. */ -static const char *audio_prio_list[] = { +const char *audio_prio_list[] = { "spice", CONFIG_AUDIO_DRIVERS "none", "wav", + NULL }; static QLIST_HEAD(, audio_driver) audio_drivers; +static AudiodevListHead audiodevs = QSIMPLEQ_HEAD_INITIALIZER(audiodevs); void audio_driver_register(audio_driver *drv) { @@ -80,61 +85,6 @@ audio_driver *audio_driver_lookup(const char *name) return NULL; } -static void audio_module_load_all(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(audio_prio_list); i++) { - audio_driver_lookup(audio_prio_list[i]); - } -} - -struct fixed_settings { - int enabled; - int nb_voices; - int greedy; - struct audsettings settings; -}; - -static struct { - struct fixed_settings fixed_out; - struct fixed_settings fixed_in; - union { - int hertz; - int64_t ticks; - } period; - int try_poll_in; - int try_poll_out; -} conf = { - .fixed_out = { /* DAC fixed settings */ - .enabled = 1, - .nb_voices = 1, - .greedy = 1, - .settings = { - .freq = 44100, - .nchannels = 2, - .fmt = AUD_FMT_S16, - .endianness = AUDIO_HOST_ENDIANNESS, - } - }, - - .fixed_in = { /* ADC fixed settings */ - .enabled = 1, - .nb_voices = 1, - .greedy = 1, - .settings = { - .freq = 44100, - .nchannels = 2, - .fmt = AUD_FMT_S16, - .endianness = AUDIO_HOST_ENDIANNESS, - } - }, - - .period = { .hertz = 100 }, - .try_poll_in = 1, - .try_poll_out = 1, -}; - static AudioState glob_audio_state; const struct mixeng_volume nominal_volume = { @@ -151,9 +101,6 @@ const struct mixeng_volume nominal_volume = { #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #error No its not #else -static void audio_print_options (const char *prefix, - struct audio_option *opt); - int audio_bug (const char *funcname, int cond) { if (cond) { @@ -161,16 +108,9 @@ int audio_bug (const char *funcname, int cond) AUD_log (NULL, "A bug was just triggered in %s\n", funcname); if (!shown) { - struct audio_driver *d; - shown = 1; AUD_log (NULL, "Save all your work and restart without audio\n"); - AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); AUD_log (NULL, "I am sorry\n"); - d = glob_audio_state.drv; - if (d) { - audio_print_options (d->name, d->options); - } } AUD_log (NULL, "Context:\n"); @@ -232,135 +172,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size) return g_malloc0 (len); } -static char *audio_alloc_prefix (const char *s) -{ - const char qemu_prefix[] = "QEMU_"; - size_t len, i; - char *r, *u; - - if (!s) { - return NULL; - } - - len = strlen (s); - r = g_malloc (len + sizeof (qemu_prefix)); - - u = r + sizeof (qemu_prefix) - 1; - - pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix); - pstrcat (r, len + sizeof (qemu_prefix), s); - - for (i = 0; i < len; ++i) { - u[i] = qemu_toupper(u[i]); - } - - return r; -} - -static const char *audio_audfmt_to_string (audfmt_e fmt) -{ - switch (fmt) { - case AUD_FMT_U8: - return "U8"; - - case AUD_FMT_U16: - return "U16"; - - case AUD_FMT_S8: - return "S8"; - - case AUD_FMT_S16: - return "S16"; - - case AUD_FMT_U32: - return "U32"; - - case AUD_FMT_S32: - return "S32"; - } - - dolog ("Bogus audfmt %d returning S16\n", fmt); - return "S16"; -} - -static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, - int *defaultp) -{ - if (!strcasecmp (s, "u8")) { - *defaultp = 0; - return AUD_FMT_U8; - } - else if (!strcasecmp (s, "u16")) { - *defaultp = 0; - return AUD_FMT_U16; - } - else if (!strcasecmp (s, "u32")) { - *defaultp = 0; - return AUD_FMT_U32; - } - else if (!strcasecmp (s, "s8")) { - *defaultp = 0; - return AUD_FMT_S8; - } - else if (!strcasecmp (s, "s16")) { - *defaultp = 0; - return AUD_FMT_S16; - } - else if (!strcasecmp (s, "s32")) { - *defaultp = 0; - return AUD_FMT_S32; - } - else { - dolog ("Bogus audio format `%s' using %s\n", - s, audio_audfmt_to_string (defval)); - *defaultp = 1; - return defval; - } -} - -static audfmt_e audio_get_conf_fmt (const char *envname, - audfmt_e 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 && !qemu_strtoi(strval, NULL, 10, &val)) { - *defaultp = 0; - 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) { @@ -379,167 +190,27 @@ 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: - { - audfmt_e *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) -{ - gchar *prefix_upper; - - if (audio_bug(__func__, !prefix)) { - dolog ("prefix = NULL\n"); - return; - } - - if (audio_bug(__func__, !opt)) { - dolog ("opt = NULL\n"); - return; - } - - prefix_upper = g_utf8_strup(prefix, -1); - - for (; opt->name; opt++) { - char *optname; - int def; - - if (!opt->valp) { - dolog ("Option value pointer for `%s' is not set\n", - opt->name); - continue; - } - - optname = g_strdup_printf("QEMU_%s_%s", prefix_upper, 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: - { - audfmt_e *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); - } - g_free(prefix_upper); -} - static void audio_print_settings (struct audsettings *as) { dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels); switch (as->fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: AUD_log (NULL, "S8"); break; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: AUD_log (NULL, "U8"); break; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: AUD_log (NULL, "S16"); break; - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: AUD_log (NULL, "U16"); break; - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: AUD_log (NULL, "S32"); break; - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: AUD_log (NULL, "U32"); break; default: @@ -570,12 +241,12 @@ static int audio_validate_settings (struct audsettings *as) invalid |= as->endianness != 0 && as->endianness != 1; switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - case AUD_FMT_S16: - case AUD_FMT_U16: - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: break; default: invalid = 1; @@ -591,25 +262,28 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a int bits = 8, sign = 0; switch (as->fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: sign = 1; /* fall through */ - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: break; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: sign = 1; /* fall through */ - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: bits = 16; break; - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: sign = 1; /* fall through */ - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: bits = 32; break; + + default: + abort(); } return info->freq == as->freq && info->nchannels == as->nchannels @@ -623,24 +297,27 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) int bits = 8, sign = 0, shift = 0; switch (as->fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: sign = 1; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: break; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: sign = 1; - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: bits = 16; shift = 1; break; - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: sign = 1; - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: bits = 32; shift = 2; break; + + default: + abort(); } info->freq = as->freq; @@ -1132,11 +809,11 @@ static void audio_reset_timer (AudioState *s) { if (audio_is_timer_needed ()) { timer_mod_anticipate_ns(s->ts, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks); if (!audio_timer_running) { audio_timer_running = true; audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - trace_audio_timer_start(conf.period.ticks / SCALE_MS); + trace_audio_timer_start(s->period_ticks / SCALE_MS); } } else { timer_del(s->ts); @@ -1150,16 +827,17 @@ static void audio_reset_timer (AudioState *s) static void audio_timer (void *opaque) { int64_t now, diff; + AudioState *s = opaque; now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); diff = now - audio_timer_last; - if (diff > conf.period.ticks * 3 / 2) { + if (diff > s->period_ticks * 3 / 2) { trace_audio_timer_delayed(diff / SCALE_MS); } audio_timer_last = now; - audio_run ("timer"); - audio_reset_timer (opaque); + audio_run("timer"); + audio_reset_timer(s); } /* @@ -1219,7 +897,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); } } @@ -1264,7 +942,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); } } @@ -1585,169 +1263,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) -{ - switch (nb) { - case 0: - printf ("Does not support %s\n", typ); - break; - case 1: - printf ("One %s voice\n", typ); - break; - case INT_MAX: - printf ("Theoretically supports many %s voices\n", typ); - break; - default: - printf ("Theoretically supports up to %d %s voices\n", nb, typ); - break; - } - -} - -void AUD_help (void) -{ - struct audio_driver *d; - - /* make sure we print the help text for modular drivers too */ - audio_module_load_all(); - - audio_process_options ("AUDIO", audio_options); - QLIST_FOREACH(d, &audio_drivers, next) { - if (d->options) { - audio_process_options (d->name, d->options); - } - } - - printf ("Audio options:\n"); - audio_print_options ("AUDIO", audio_options); - printf ("\n"); - - printf ("Available drivers:\n"); - - QLIST_FOREACH(d, &audio_drivers, next) { - - printf ("Name: %s\n", d->name); - printf ("Description: %s\n", d->descr); - - audio_pp_nb_voices ("playback", d->max_voices_out); - audio_pp_nb_voices ("capture", d->max_voices_in); - - if (d->options) { - printf ("Options:\n"); - audio_print_options (d->name, d->options); - } - else { - printf ("No options\n"); - } - printf ("\n"); - } - - printf ( - "Options are settable through environment variables.\n" - "Example:\n" -#ifdef _WIN32 - " set QEMU_AUDIO_DRV=wav\n" - " set QEMU_WAV_PATH=c:\\tune.wav\n" -#else - " export QEMU_AUDIO_DRV=wav\n" - " export QEMU_WAV_PATH=$HOME/tune.wav\n" - "(for csh replace export with setenv in the above)\n" -#endif - " qemu ...\n\n" - ); -} - -static int audio_driver_init(AudioState *s, struct audio_driver *drv, bool msg) +static int audio_driver_init(AudioState *s, struct audio_driver *drv, + bool msg, Audiodev *dev) { - if (drv->options) { - audio_process_options (drv->name, drv->options); - } - s->drv_opaque = drv->init (); + s->drv_opaque = drv->init(dev); if (s->drv_opaque) { audio_init_nb_voices_out (drv); @@ -1773,11 +1292,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); } @@ -1827,6 +1346,11 @@ void audio_cleanup(void) s->drv->fini (s->drv_opaque); s->drv = NULL; } + + if (s->dev) { + qapi_free_Audiodev(s->dev); + s->dev = NULL; + } } static const VMStateDescription vmstate_audio = { @@ -1838,19 +1362,58 @@ static const VMStateDescription vmstate_audio = { } }; -static void audio_init (void) +static void audio_validate_opts(Audiodev *dev, Error **errp); + +static AudiodevListEntry *audiodev_find( + AudiodevListHead *head, const char *drvname) +{ + AudiodevListEntry *e; + QSIMPLEQ_FOREACH(e, head, next) { + if (strcmp(AudiodevDriver_str(e->dev->driver), drvname) == 0) { + return e; + } + } + + return NULL; +} + +static int audio_init(Audiodev *dev) { size_t i; int done = 0; - const char *drvname; + const char *drvname = NULL; VMChangeStateEntry *e; AudioState *s = &glob_audio_state; struct audio_driver *driver; + /* silence gcc warning about uninitialized variable */ + AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); if (s->drv) { - return; + if (dev) { + dolog("Cannot create more than one audio backend, sorry\n"); + qapi_free_Audiodev(dev); + } + return -1; } + if (dev) { + /* -audiodev option */ + drvname = AudiodevDriver_str(dev->driver); + } else { + /* legacy implicit initialization */ + head = audio_handle_legacy_opts(); + /* + * In case of legacy initialization, all Audiodevs in the list will have + * the same configuration (except the driver), so it does't matter which + * one we chose. We need an Audiodev to set up AudioState before we can + * init a driver. Also note that dev at this point is still in the + * list. + */ + dev = QSIMPLEQ_FIRST(&head)->dev; + audio_validate_opts(dev, &error_abort); + } + s->dev = dev; + QLIST_INIT (&s->hw_head_out); QLIST_INIT (&s->hw_head_in); QLIST_INIT (&s->cap_head); @@ -1858,10 +1421,8 @@ static void audio_init (void) s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); - audio_process_options ("AUDIO", audio_options); - - s->nb_hw_voices_out = conf.fixed_out.nb_voices; - s->nb_hw_voices_in = conf.fixed_in.nb_voices; + s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices; + s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices; if (s->nb_hw_voices_out <= 0) { dolog ("Bogus number of playback voices %d, setting to 1\n", @@ -1875,46 +1436,42 @@ static void audio_init (void) s->nb_hw_voices_in = 0; } - { - int def; - drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); - } - if (drvname) { driver = audio_driver_lookup(drvname); if (driver) { - done = !audio_driver_init(s, driver, true); + done = !audio_driver_init(s, driver, true, dev); } else { dolog ("Unknown audio driver `%s'\n", drvname); - dolog ("Run with -audio-help to list available drivers\n"); } - } - - if (!done) { - for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { + } else { + for (i = 0; audio_prio_list[i]; i++) { + AudiodevListEntry *e = audiodev_find(&head, audio_prio_list[i]); driver = audio_driver_lookup(audio_prio_list[i]); - if (driver && driver->can_be_default) { - done = !audio_driver_init(s, driver, false); + + if (e && driver) { + s->dev = dev = e->dev; + audio_validate_opts(dev, &error_abort); + done = !audio_driver_init(s, driver, false, dev); + if (done) { + e->dev = NULL; + break; + } } } } + audio_free_audiodev_list(&head); if (!done) { driver = audio_driver_lookup("none"); - done = !audio_driver_init(s, driver, false); + done = !audio_driver_init(s, driver, false, dev); assert(done); dolog("warning: Using timer based audio emulation\n"); } - if (conf.period.hertz <= 0) { - if (conf.period.hertz < 0) { - dolog ("warning: Timer period is negative - %d " - "treating as zero\n", - conf.period.hertz); - } - conf.period.ticks = 1; + if (dev->timer_period <= 0) { + s->period_ticks = 1; } else { - conf.period.ticks = NANOSECONDS_PER_SECOND / conf.period.hertz; + s->period_ticks = NANOSECONDS_PER_SECOND / dev->timer_period; } e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); @@ -1925,11 +1482,22 @@ static void audio_init (void) QLIST_INIT (&s->card_head); vmstate_register (NULL, 0, &vmstate_audio, s); + return 0; +} + +void audio_free_audiodev_list(AudiodevListHead *head) +{ + AudiodevListEntry *e; + while ((e = QSIMPLEQ_FIRST(head))) { + QSIMPLEQ_REMOVE_HEAD(head, next); + qapi_free_Audiodev(e->dev); + g_free(e); + } } void AUD_register_card (const char *name, QEMUSoundCard *card) { - audio_init (); + audio_init(NULL); card->name = g_strdup (name); memset (&card->entries, 0, sizeof (card->entries)); QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries); @@ -2069,3 +1637,174 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol) } } } + +void audio_create_pdos(Audiodev *dev) +{ + switch (dev->driver) { +#define CASE(DRIVER, driver, pdo_name) \ + case AUDIODEV_DRIVER_##DRIVER: \ + if (!dev->u.driver.has_in) { \ + dev->u.driver.in = g_malloc0( \ + sizeof(Audiodev##pdo_name##PerDirectionOptions)); \ + dev->u.driver.has_in = true; \ + } \ + if (!dev->u.driver.has_out) { \ + dev->u.driver.out = g_malloc0( \ + sizeof(AudiodevAlsaPerDirectionOptions)); \ + dev->u.driver.has_out = true; \ + } \ + break + + CASE(NONE, none, ); + CASE(ALSA, alsa, Alsa); + CASE(COREAUDIO, coreaudio, Coreaudio); + CASE(DSOUND, dsound, ); + CASE(OSS, oss, Oss); + CASE(PA, pa, Pa); + CASE(SDL, sdl, ); + CASE(SPICE, spice, ); + CASE(WAV, wav, ); + + case AUDIODEV_DRIVER__MAX: + abort(); + }; +} + +static void audio_validate_per_direction_opts( + AudiodevPerDirectionOptions *pdo, Error **errp) +{ + if (!pdo->has_fixed_settings) { + pdo->has_fixed_settings = true; + pdo->fixed_settings = true; + } + if (!pdo->fixed_settings && + (pdo->has_frequency || pdo->has_channels || pdo->has_format)) { + error_setg(errp, + "You can't use frequency, channels or format with fixed-settings=off"); + return; + } + + if (!pdo->has_frequency) { + pdo->has_frequency = true; + pdo->frequency = 44100; + } + if (!pdo->has_channels) { + pdo->has_channels = true; + pdo->channels = 2; + } + if (!pdo->has_voices) { + pdo->has_voices = true; + pdo->voices = 1; + } + if (!pdo->has_format) { + pdo->has_format = true; + pdo->format = AUDIO_FORMAT_S16; + } +} + +static void audio_validate_opts(Audiodev *dev, Error **errp) +{ + Error *err = NULL; + + audio_create_pdos(dev); + + audio_validate_per_direction_opts(audio_get_pdo_in(dev), &err); + if (err) { + error_propagate(errp, err); + return; + } + + audio_validate_per_direction_opts(audio_get_pdo_out(dev), &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (!dev->has_timer_period) { + dev->has_timer_period = true; + dev->timer_period = 10000; /* 100Hz -> 10ms */ + } +} + +void audio_parse_option(const char *opt) +{ + AudiodevListEntry *e; + Audiodev *dev = NULL; + + Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal); + visit_type_Audiodev(v, NULL, &dev, &error_fatal); + visit_free(v); + + audio_validate_opts(dev, &error_fatal); + + e = g_malloc0(sizeof(AudiodevListEntry)); + e->dev = dev; + QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next); +} + +void audio_init_audiodevs(void) +{ + AudiodevListEntry *e; + + QSIMPLEQ_FOREACH(e, &audiodevs, next) { + audio_init(e->dev); + } +} + +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo) +{ + return (audsettings) { + .freq = pdo->frequency, + .nchannels = pdo->channels, + .fmt = pdo->format, + .endianness = AUDIO_HOST_ENDIANNESS, + }; +} + +int audioformat_bytes_per_sample(AudioFormat fmt) +{ + switch (fmt) { + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S8: + return 1; + + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S16: + return 2; + + case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_S32: + return 4; + + case AUDIO_FORMAT__MAX: + ; + } + abort(); +} + + +/* frames = freq * usec / 1e6 */ +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs; + return (as->freq * usecs + 500000) / 1000000; +} + +/* samples = channels * frames = channels * freq * usec / 1e6 */ +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + return as->nchannels * audio_buffer_frames(pdo, as, def_usecs); +} + +/* + * bytes = bytes_per_sample * samples = + * bytes_per_sample * channels * freq * usec / 1e6 + */ +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + return audio_buffer_samples(pdo, as, def_usecs) * + audioformat_bytes_per_sample(as->fmt); +} diff --git a/audio/audio.h b/audio/audio.h index f4339a185e..64b0f761bc 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -26,30 +26,31 @@ #define QEMU_AUDIO_H #include "qemu/queue.h" +#include "qapi/qapi-types-audio.h" typedef void (*audio_callback_fn) (void *opaque, int avail); -typedef enum { - AUD_FMT_U8, - AUD_FMT_S8, - AUD_FMT_U16, - AUD_FMT_S16, - AUD_FMT_U32, - AUD_FMT_S32 -} audfmt_e; - #ifdef HOST_WORDS_BIGENDIAN #define AUDIO_HOST_ENDIANNESS 1 #else #define AUDIO_HOST_ENDIANNESS 0 #endif -struct audsettings { +typedef struct audsettings { int freq; int nchannels; - audfmt_e fmt; + 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, @@ -89,7 +90,6 @@ typedef struct QEMUAudioTimeStamp { void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void AUD_help (void); void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card); CaptureVoiceOut *AUD_add_capture ( @@ -171,4 +171,8 @@ void audio_sample_to_uint64(void *samples, int pos, void audio_sample_from_uint64(void *samples, int pos, uint64_t left, uint64_t right); +void audio_parse_option(const char *opt); +void audio_init_audiodevs(void); +void audio_legacy_help(void); + #endif /* QEMU_AUDIO_H */ diff --git a/audio/audio_int.h b/audio/audio_int.h index 6c451b995c..3f14842709 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -33,22 +33,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; @@ -145,8 +129,7 @@ typedef struct audio_driver audio_driver; struct audio_driver { const char *name; const char *descr; - struct audio_option *options; - void *(*init) (void); + void *(*init) (Audiodev *); void (*fini) (void *); struct audio_pcm_ops *pcm_ops; int can_be_default; @@ -193,6 +176,7 @@ struct SWVoiceCap { typedef struct AudioState { struct audio_driver *drv; + Audiodev *dev; void *drv_opaque; QEMUTimer *ts; @@ -203,10 +187,13 @@ typedef struct AudioState { int nb_hw_voices_out; int nb_hw_voices_in; int vm_running; + int64_t period_ticks; } AudioState; extern const struct mixeng_volume nominal_volume; +extern const char *audio_prio_list[]; + void audio_driver_register(audio_driver *drv); audio_driver *audio_driver_lookup(const char *name); @@ -248,4 +235,18 @@ static inline int audio_ring_dist (int dst, int src, int len) #define AUDIO_STRINGIFY_(n) #n #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) +typedef struct AudiodevListEntry { + Audiodev *dev; + QSIMPLEQ_ENTRY(AudiodevListEntry) next; +} AudiodevListEntry; + +typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead; +AudiodevListHead audio_handle_legacy_opts(void); + +void audio_free_audiodev_list(AudiodevListHead *head); + +void audio_create_pdos(Audiodev *dev); +AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev); +AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev); + #endif /* QEMU_AUDIO_INT_H */ diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c new file mode 100644 index 0000000000..6d140119d9 --- /dev/null +++ b/audio/audio_legacy.c @@ -0,0 +1,544 @@ +/* + * QEMU Audio subsystem: legacy configuration handling + * + * Copyright (c) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "qemu/osdep.h" +#include "audio.h" +#include "audio_int.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-audio.h" +#include "qapi/visitor-impl.h" + +#define AUDIO_CAP "audio-legacy" +#include "audio_int.h" + +static uint32_t toui32(const char *str) +{ + unsigned long long ret; + if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) { + dolog("Invalid integer value `%s'\n", str); + exit(1); + } + return ret; +} + +/* helper functions to convert env variables */ +static void get_bool(const char *env, bool *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + *dst = toui32(val) != 0; + *has_dst = true; + } +} + +static void get_int(const char *env, uint32_t *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + *dst = toui32(val); + *has_dst = true; + } +} + +static void get_str(const char *env, char **dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + if (*has_dst) { + g_free(*dst); + } + *dst = g_strdup(val); + *has_dst = true; + } +} + +static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + size_t i; + for (i = 0; AudioFormat_lookup.size; ++i) { + if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) { + *dst = i; + *has_dst = true; + return; + } + } + + dolog("Invalid audio format `%s'\n", val); + exit(1); + } +} + + +static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + *dst = toui32(val) * 1000; + *has_dst = true; + } +} + +static uint32_t frames_to_usecs(uint32_t frames, + AudiodevPerDirectionOptions *pdo) +{ + uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; + return (frames * 1000000 + freq / 2) / freq; +} + + +static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, + AudiodevPerDirectionOptions *pdo) +{ + const char *val = getenv(env); + if (val) { + *dst = frames_to_usecs(toui32(val), pdo); + *has_dst = true; + } +} + +static uint32_t samples_to_usecs(uint32_t samples, + AudiodevPerDirectionOptions *pdo) +{ + uint32_t channels = pdo->has_channels ? pdo->channels : 2; + return frames_to_usecs(samples / channels, pdo); +} + +static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, + AudiodevPerDirectionOptions *pdo) +{ + const char *val = getenv(env); + if (val) { + *dst = samples_to_usecs(toui32(val), pdo); + *has_dst = true; + } +} + +static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo) +{ + AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16; + uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt); + return samples_to_usecs(bytes / bytes_per_sample, pdo); +} + +static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst, + AudiodevPerDirectionOptions *pdo) +{ + const char *val = getenv(env); + if (val) { + *dst = bytes_to_usecs(toui32(val), pdo); + *has_dst = true; + } +} + +/* backend specific functions */ +/* ALSA */ +static void handle_alsa_per_direction( + AudiodevAlsaPerDirectionOptions *apdo, const char *prefix) +{ + char buf[64]; + size_t len = strlen(prefix); + bool size_in_usecs = false; + bool dummy; + + memcpy(buf, prefix, len); + strcpy(buf + len, "TRY_POLL"); + get_bool(buf, &apdo->try_poll, &apdo->has_try_poll); + + strcpy(buf + len, "DEV"); + get_str(buf, &apdo->dev, &apdo->has_dev); + + strcpy(buf + len, "SIZE_IN_USEC"); + get_bool(buf, &size_in_usecs, &dummy); + + strcpy(buf + len, "PERIOD_SIZE"); + get_int(buf, &apdo->period_length, &apdo->has_period_length); + if (apdo->has_period_length && !size_in_usecs) { + apdo->period_length = frames_to_usecs( + apdo->period_length, + qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); + } + + strcpy(buf + len, "BUFFER_SIZE"); + get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length); + if (apdo->has_buffer_length && !size_in_usecs) { + apdo->buffer_length = frames_to_usecs( + apdo->buffer_length, + qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); + } +} + +static void handle_alsa(Audiodev *dev) +{ + AudiodevAlsaOptions *aopt = &dev->u.alsa; + handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_"); + handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_"); + + get_millis_to_usecs("QEMU_ALSA_THRESHOLD", + &aopt->threshold, &aopt->has_threshold); +} + +/* coreaudio */ +static void handle_coreaudio(Audiodev *dev) +{ + get_frames_to_usecs( + "QEMU_COREAUDIO_BUFFER_SIZE", + &dev->u.coreaudio.out->buffer_length, + &dev->u.coreaudio.out->has_buffer_length, + qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out)); + get_int("QEMU_COREAUDIO_BUFFER_COUNT", + &dev->u.coreaudio.out->buffer_count, + &dev->u.coreaudio.out->has_buffer_count); +} + +/* dsound */ +static void handle_dsound(Audiodev *dev) +{ + get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS", + &dev->u.dsound.latency, &dev->u.dsound.has_latency); + get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT", + &dev->u.dsound.out->buffer_length, + &dev->u.dsound.out->has_buffer_length, + dev->u.dsound.out); + get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN", + &dev->u.dsound.in->buffer_length, + &dev->u.dsound.in->has_buffer_length, + dev->u.dsound.in); +} + +/* OSS */ +static void handle_oss_per_direction( + AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env, + const char *dev_env) +{ + get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll); + get_str(dev_env, &opdo->dev, &opdo->has_dev); + + get_bytes_to_usecs("QEMU_OSS_FRAGSIZE", + &opdo->buffer_length, &opdo->has_buffer_length, + qapi_AudiodevOssPerDirectionOptions_base(opdo)); + get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count, + &opdo->has_buffer_count); +} + +static void handle_oss(Audiodev *dev) +{ + AudiodevOssOptions *oopt = &dev->u.oss; + handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL", + "QEMU_OSS_ADC_DEV"); + handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL", + "QEMU_OSS_DAC_DEV"); + + get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap); + get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive); + get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy); +} + +/* pulseaudio */ +static void handle_pa_per_direction( + AudiodevPaPerDirectionOptions *ppdo, const char *env) +{ + get_str(env, &ppdo->name, &ppdo->has_name); +} + +static void handle_pa(Audiodev *dev) +{ + handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE"); + handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK"); + + get_samples_to_usecs( + "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length, + &dev->u.pa.in->has_buffer_length, + qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in)); + get_samples_to_usecs( + "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length, + &dev->u.pa.out->has_buffer_length, + qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out)); + + get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server); +} + +/* SDL */ +static void handle_sdl(Audiodev *dev) +{ + /* SDL is output only */ + get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length, + &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out); +} + +/* wav */ +static void handle_wav(Audiodev *dev) +{ + get_int("QEMU_WAV_FREQUENCY", + &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency); + get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format, + &dev->u.wav.out->has_format); + get_int("QEMU_WAV_DAC_FIXED_CHANNELS", + &dev->u.wav.out->channels, &dev->u.wav.out->has_channels); + get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path); +} + +/* general */ +static void handle_per_direction( + AudiodevPerDirectionOptions *pdo, const char *prefix) +{ + char buf[64]; + size_t len = strlen(prefix); + + memcpy(buf, prefix, len); + strcpy(buf + len, "FIXED_SETTINGS"); + get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings); + + strcpy(buf + len, "FIXED_FREQ"); + get_int(buf, &pdo->frequency, &pdo->has_frequency); + + strcpy(buf + len, "FIXED_FMT"); + get_fmt(buf, &pdo->format, &pdo->has_format); + + strcpy(buf + len, "FIXED_CHANNELS"); + get_int(buf, &pdo->channels, &pdo->has_channels); + + strcpy(buf + len, "VOICES"); + get_int(buf, &pdo->voices, &pdo->has_voices); +} + +static AudiodevListEntry *legacy_opt(const char *drvname) +{ + AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry)); + e->dev = g_malloc0(sizeof(Audiodev)); + e->dev->id = g_strdup(drvname); + e->dev->driver = qapi_enum_parse( + &AudiodevDriver_lookup, drvname, -1, &error_abort); + + audio_create_pdos(e->dev); + + handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_"); + handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_"); + + get_int("QEMU_AUDIO_TIMER_PERIOD", + &e->dev->timer_period, &e->dev->has_timer_period); + + switch (e->dev->driver) { + case AUDIODEV_DRIVER_ALSA: + handle_alsa(e->dev); + break; + + case AUDIODEV_DRIVER_COREAUDIO: + handle_coreaudio(e->dev); + break; + + case AUDIODEV_DRIVER_DSOUND: + handle_dsound(e->dev); + break; + + case AUDIODEV_DRIVER_OSS: + handle_oss(e->dev); + break; + + case AUDIODEV_DRIVER_PA: + handle_pa(e->dev); + break; + + case AUDIODEV_DRIVER_SDL: + handle_sdl(e->dev); + break; + + case AUDIODEV_DRIVER_WAV: + handle_wav(e->dev); + break; + + default: + break; + } + + return e; +} + +AudiodevListHead audio_handle_legacy_opts(void) +{ + const char *drvname = getenv("QEMU_AUDIO_DRV"); + AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); + + if (drvname) { + AudiodevListEntry *e; + audio_driver *driver = audio_driver_lookup(drvname); + if (!driver) { + dolog("Unknown audio driver `%s'\n", drvname); + exit(1); + } + e = legacy_opt(drvname); + QSIMPLEQ_INSERT_TAIL(&head, e, next); + } else { + for (int i = 0; audio_prio_list[i]; i++) { + audio_driver *driver = audio_driver_lookup(audio_prio_list[i]); + if (driver && driver->can_be_default) { + AudiodevListEntry *e = legacy_opt(driver->name); + QSIMPLEQ_INSERT_TAIL(&head, e, next); + } + } + if (QSIMPLEQ_EMPTY(&head)) { + dolog("Internal error: no default audio driver available\n"); + exit(1); + } + } + + return head; +} + +/* visitor to print -audiodev option */ +typedef struct { + Visitor visitor; + + bool comma; + GList *path; +} LegacyPrintVisitor; + +static void lv_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + lv->path = g_list_append(lv->path, g_strdup(name)); +} + +static void lv_end_struct(Visitor *v, void **obj) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + lv->path = g_list_delete_link(lv->path, g_list_last(lv->path)); +} + +static void lv_print_key(Visitor *v, const char *name) +{ + GList *e; + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + if (lv->comma) { + putchar(','); + } else { + lv->comma = true; + } + + for (e = lv->path; e; e = e->next) { + if (e->data) { + printf("%s.", (const char *) e->data); + } + } + + printf("%s=", name); +} + +static void lv_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) +{ + lv_print_key(v, name); + printf("%" PRIi64, *obj); +} + +static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) +{ + lv_print_key(v, name); + printf("%" PRIu64, *obj); +} + +static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) +{ + lv_print_key(v, name); + printf("%s", *obj ? "on" : "off"); +} + +static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp) +{ + const char *str = *obj; + lv_print_key(v, name); + + while (*str) { + if (*str == ',') { + putchar(','); + } + putchar(*str++); + } +} + +static void lv_complete(Visitor *v, void *opaque) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + assert(lv->path == NULL); +} + +static void lv_free(Visitor *v) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + + g_list_free_full(lv->path, g_free); + g_free(lv); +} + +static Visitor *legacy_visitor_new(void) +{ + LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor)); + + lv->visitor.start_struct = lv_start_struct; + lv->visitor.end_struct = lv_end_struct; + /* lists not supported */ + lv->visitor.type_int64 = lv_type_int64; + lv->visitor.type_uint64 = lv_type_uint64; + lv->visitor.type_bool = lv_type_bool; + lv->visitor.type_str = lv_type_str; + + lv->visitor.type = VISITOR_OUTPUT; + lv->visitor.complete = lv_complete; + lv->visitor.free = lv_free; + + return &lv->visitor; +} + +void audio_legacy_help(void) +{ + AudiodevListHead head; + AudiodevListEntry *e; + + printf("Environment variable based configuration deprecated.\n"); + printf("Please use the new -audiodev option.\n"); + + head = audio_handle_legacy_opts(); + printf("\nEquivalent -audiodev to your current environment variables:\n"); + if (!getenv("QEMU_AUDIO_DRV")) { + printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all " + "possibilities)\n"); + } + + QSIMPLEQ_FOREACH(e, &head, next) { + Visitor *v; + Audiodev *dev = e->dev; + printf("-audiodev "); + + v = legacy_visitor_new(); + visit_type_Audiodev(v, NULL, &dev, &error_abort); + visit_free(v); + + printf("\n"); + } + audio_free_audiodev_list(&head); +} diff --git a/audio/audio_template.h b/audio/audio_template.h index 7de227d2d1..1232bb54db 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -299,11 +299,42 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) return NULL; } +AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) +{ + switch (dev->driver) { + case AUDIODEV_DRIVER_NONE: + return dev->u.none.TYPE; + case AUDIODEV_DRIVER_ALSA: + return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); + case AUDIODEV_DRIVER_COREAUDIO: + return qapi_AudiodevCoreaudioPerDirectionOptions_base( + dev->u.coreaudio.TYPE); + case AUDIODEV_DRIVER_DSOUND: + return dev->u.dsound.TYPE; + case AUDIODEV_DRIVER_OSS: + return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); + case AUDIODEV_DRIVER_PA: + return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); + case AUDIODEV_DRIVER_SDL: + return dev->u.sdl.TYPE; + case AUDIODEV_DRIVER_SPICE: + return dev->u.spice.TYPE; + case AUDIODEV_DRIVER_WAV: + return dev->u.wav.TYPE; + + case AUDIODEV_DRIVER__MAX: + break; + } + abort(); +} + static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) { HW *hw; + AudioState *s = &glob_audio_state; + AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); - if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { + if (pdo->fixed_settings) { hw = glue (audio_pcm_hw_add_new_, TYPE) (as); if (hw) { return hw; @@ -331,9 +362,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( SW *sw; HW *hw; struct audsettings hw_as; + AudioState *s = &glob_audio_state; + AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); - if (glue (conf.fixed_, TYPE).enabled) { - hw_as = glue (conf.fixed_, TYPE).settings; + if (pdo->fixed_settings) { + hw_as = audiodev_to_audsettings(pdo); } else { hw_as = *as; @@ -398,6 +431,7 @@ SW *glue (AUD_open_, TYPE) ( ) { AudioState *s = &glob_audio_state; + AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { dolog ("card=%p name=%p callback_fn=%p as=%p\n", @@ -422,7 +456,7 @@ SW *glue (AUD_open_, TYPE) ( return sw; } - if (!glue (conf.fixed_, TYPE).enabled && sw) { + if (!pdo->fixed_settings && sw) { glue (AUD_close_, TYPE) (card, sw); sw = NULL; } diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c index 6900008d0c..b938fd667b 100644 --- a/audio/audio_win_int.c +++ b/audio/audio_win_int.c @@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, wfx->cbSize = 0; switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: wfx->wBitsPerSample = 8; break; - case AUD_FMT_S16: - case AUD_FMT_U16: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: wfx->wBitsPerSample = 16; wfx->nAvgBytesPerSec <<= 1; wfx->nBlockAlign <<= 1; break; - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: wfx->wBitsPerSample = 32; wfx->nAvgBytesPerSec <<= 2; wfx->nBlockAlign <<= 2; @@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, switch (wfx->wBitsPerSample) { case 8: - as->fmt = AUD_FMT_U8; + as->fmt = AUDIO_FORMAT_U8; break; case 16: - as->fmt = AUD_FMT_S16; + as->fmt = AUDIO_FORMAT_S16; break; case 32: - as->fmt = AUD_FMT_S32; + as->fmt = AUDIO_FORMAT_S32; break; default: diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 638c60b300..1ee43b7d5f 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -36,11 +36,6 @@ #define MAC_OS_X_VERSION_10_6 1060 #endif -typedef struct { - int buffer_frames; - int nbuffers; -} CoreaudioConf; - typedef struct coreaudioVoiceOut { HWVoiceOut hw; pthread_mutex_t mutex; @@ -507,7 +502,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; + AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; + int frames; /* create mutex */ err = pthread_mutex_init(&core->mutex, NULL); @@ -538,16 +535,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, return -1; } - if (frameRange.mMinimum > conf->buffer_frames) { + frames = audio_buffer_frames( + qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), 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 */ @@ -568,7 +566,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 = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * + core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ status = coreaudio_get_streamformat(core->outputDeviceID, @@ -680,40 +679,15 @@ 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; - - 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, @@ -725,7 +699,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = { static 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 b439f33f58..8ece870c9e 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->u.dsound.in; #else const char *typ = "DAC"; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; DSBUFFERDESC bd; DSBCAPS bc; + AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.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 3ed73a30d1..a7d04b5033 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -32,6 +32,7 @@ #define AUDIO_CAP "dsound" #include "audio_int.h" +#include "qemu/host-utils.h" #include <windows.h> #include <mmsystem.h> @@ -43,16 +44,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 { @@ -248,9 +243,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 @@ -478,7 +473,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->u.dsound; if (!dsb) { dolog ("Attempt to run empty with playback buffer\n"); @@ -501,14 +496,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; } @@ -747,12 +742,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; @@ -783,13 +772,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->driver == AUDIODEV_DRIVER_DSOUND); + s->dev = dev; + dso = &dev->u.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"); @@ -854,28 +852,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, @@ -893,7 +869,6 @@ static struct audio_pcm_ops dsound_pcm_ops = { static 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 1bfebeca7d..ccc611fc84 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) return 0; } -static void *no_audio_init (void) +static void *no_audio_init(Audiodev *dev) { return &no_audio_init; } @@ -163,7 +163,6 @@ static struct audio_pcm_ops no_pcm_ops = { static 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 6c69622b4c..fc28981a39 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -37,16 +37,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; @@ -56,7 +46,7 @@ typedef struct OSSVoiceOut { int fragsize; int mmapped; int pending; - OSSConf *conf; + Audiodev *dev; } OSSVoiceOut; typedef struct OSSVoiceIn { @@ -65,12 +55,12 @@ typedef struct OSSVoiceIn { int fd; int nfrags; int fragsize; - OSSConf *conf; + Audiodev *dev; } OSSVoiceIn; struct oss_params { int freq; - audfmt_e fmt; + int fmt; int nchannels; int nfrags; int fragsize; @@ -148,16 +138,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static int aud_to_ossfmt (audfmt_e fmt, int endianness) +static int aud_to_ossfmt (AudioFormat fmt, int endianness) { switch (fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: return AFMT_S8; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: return AFMT_U8; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: if (endianness) { return AFMT_S16_BE; } @@ -165,7 +155,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) return AFMT_S16_LE; } - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: if (endianness) { return AFMT_U16_BE; } @@ -182,37 +172,37 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) } } -static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) +static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness) { switch (ossfmt) { case AFMT_S8: *endianness = 0; - *fmt = AUD_FMT_S8; + *fmt = AUDIO_FORMAT_S8; break; case AFMT_U8: *endianness = 0; - *fmt = AUD_FMT_U8; + *fmt = AUDIO_FORMAT_U8; break; case AFMT_S16_LE: *endianness = 0; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AFMT_U16_LE: *endianness = 0; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case AFMT_S16_BE: *endianness = 1; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AFMT_U16_BE: *endianness = 1; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; default: @@ -262,19 +252,25 @@ 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->u.oss; + AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->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) { @@ -285,6 +281,9 @@ static int oss_open (int in, struct oss_params *req, freq = req->freq; nchannels = req->nchannels; fmt = req->fmt; + req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4; + req->fragsize = audio_buffer_bytes( + qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220); if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); @@ -308,18 +307,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; @@ -500,19 +499,18 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, int endianness; int err; int fd; - audfmt_e effective_fmt; + AudioFormat effective_fmt; struct audsettings obt_as; - OSSConf *conf = drv_opaque; + Audiodev *dev = drv_opaque; + AudiodevOssOptions *oopts = &dev->u.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; } @@ -539,7 +537,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, @@ -597,7 +595,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, } oss->fd = fd; - oss->conf = conf; + oss->dev = dev; return 0; } @@ -605,16 +603,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) { int trig; OSSVoiceOut *oss = (OSSVoiceOut *) hw; + AudiodevOssPerDirectionOptions *opdo = oss->dev->u.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->try_poll; ldebug ("enabling voice\n"); if (poll_mode) { @@ -667,18 +661,16 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) int endianness; int err; int fd; - audfmt_e effective_fmt; + AudioFormat effective_fmt; struct audsettings obt_as; - OSSConf *conf = drv_opaque; + 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; } @@ -712,7 +704,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; } @@ -803,16 +795,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->u.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->try_poll; if (poll_mode) { oss_poll_in (hw); @@ -832,82 +820,36 @@ 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_init_per_direction(AudiodevOssPerDirectionOptions *opdo) +{ + if (!opdo->has_try_poll) { + opdo->try_poll = true; + opdo->has_try_poll = true; + } +} -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->driver == AUDIODEV_DRIVER_OSS); + + oopts = &dev->u.oss; + oss_init_per_direction(oopts->in); + oss_init_per_direction(oopts->out); - if (access(conf->devpath_in, R_OK | W_OK) < 0 || - access(conf->devpath_out, R_OK | W_OK) < 0) { - g_free(conf); + if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp", + R_OK | W_OK) < 0 || + access(oopts->out->has_dev ? oopts->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 won't 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, @@ -925,7 +867,6 @@ static struct audio_pcm_ops oss_pcm_ops = { static 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 6153b908da..5d410ed73f 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -2,6 +2,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" +#include "qapi/opts-visitor.h" #include <pulse/pulseaudio.h> @@ -10,14 +11,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; @@ -32,6 +26,7 @@ typedef struct { void *pcm_buf; struct audio_pt pt; paaudio *g; + int samples; } PAVoiceOut; typedef struct { @@ -46,6 +41,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); @@ -227,7 +223,7 @@ static void *qpa_thread_out (void *arg) } } - decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5); + decr = to_mix = audio_MIN(pa->live, pa->samples >> 5); rpos = pa->rpos; if (audio_pt_unlock(&pa->pt, __func__)) { @@ -319,7 +315,7 @@ static void *qpa_thread_in (void *arg) } } - incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5); + incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5); wpos = pa->wpos; if (audio_pt_unlock(&pa->pt, __func__)) { @@ -385,21 +381,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len) return audio_pcm_sw_read (sw, buf, len); } -static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) +static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) { int format; switch (afmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: format = PA_SAMPLE_U8; break; - case AUD_FMT_S16: - case AUD_FMT_U16: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; break; - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; break; default: @@ -410,26 +406,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) return format; } -static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) +static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) { switch (fmt) { case PA_SAMPLE_U8: - return AUD_FMT_U8; + return AUDIO_FORMAT_U8; case PA_SAMPLE_S16BE: *endianness = 1; - return AUD_FMT_S16; + return AUDIO_FORMAT_S16; case PA_SAMPLE_S16LE: *endianness = 0; - return AUD_FMT_S16; + return AUDIO_FORMAT_S16; case PA_SAMPLE_S32BE: *endianness = 1; - return AUD_FMT_S32; + return AUDIO_FORMAT_S32; case PA_SAMPLE_S32LE: *endianness = 0; - return AUD_FMT_S32; + return AUDIO_FORMAT_S32; default: dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); - return AUD_FMT_U8; + return AUDIO_FORMAT_U8; } } @@ -546,6 +542,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->u.pa; + AudiodevPaPerDirectionOptions *ppdo = popts->out; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; @@ -566,7 +564,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 */ @@ -578,7 +576,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( + qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->rpos = hw->rpos; if (!pa->pcm_buf) { @@ -612,6 +611,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->u.pa; + AudiodevPaPerDirectionOptions *ppdo = popts->in; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; @@ -623,7 +624,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 */ @@ -635,7 +636,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( + qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->wpos = hw->wpos; if (!pa->pcm_buf) { @@ -808,13 +810,13 @@ 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) { - if (glob_conf.server == NULL) { + paaudio *g; + AudiodevPaOptions *popts = &dev->u.pa; + const char *server; + + if (!popts->has_server) { char pidfile[64]; char *runtime; struct stat st; @@ -829,8 +831,12 @@ static void *qpa_audio_init (void) } } - paaudio *g = g_malloc(sizeof(paaudio)); - g->conf = glob_conf; + assert(dev->driver == AUDIODEV_DRIVER_PA); + + g = g_malloc(sizeof(paaudio)); + server = popts->has_server ? popts->server : NULL; + + g->dev = dev; g->mainloop = NULL; g->context = NULL; @@ -840,14 +846,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; @@ -910,34 +916,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, @@ -955,7 +933,6 @@ static struct audio_pcm_ops qpa_pcm_ops = { static 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 f7ee70b153..ff9248ba68 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -44,16 +44,11 @@ typedef struct SDLVoiceOut { int decr; } SDLVoiceOut; -static struct { - int nb_samples; -} conf = { - .nb_samples = 1024 -}; - static struct SDLAudioState { int exit; int initialized; bool driver_created; + Audiodev *dev; } glob_sdl; typedef struct SDLAudioState SDLAudioState; @@ -68,19 +63,19 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); } -static int aud_to_sdlfmt (audfmt_e fmt) +static int aud_to_sdlfmt (AudioFormat fmt) { switch (fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: return AUDIO_S8; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: return AUDIO_U8; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: return AUDIO_S16LSB; - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: return AUDIO_U16LSB; default: @@ -92,37 +87,37 @@ static int aud_to_sdlfmt (audfmt_e fmt) } } -static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) +static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) { switch (sdlfmt) { case AUDIO_S8: *endianness = 0; - *fmt = AUD_FMT_S8; + *fmt = AUDIO_FORMAT_S8; break; case AUDIO_U8: *endianness = 0; - *fmt = AUD_FMT_U8; + *fmt = AUDIO_FORMAT_U8; break; case AUDIO_S16LSB: *endianness = 0; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AUDIO_U16LSB: *endianness = 0; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case AUDIO_S16MSB: *endianness = 1; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AUDIO_U16MSB: *endianness = 1; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; default: @@ -265,13 +260,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, SDL_AudioSpec req, obt; int endianness; int err; - audfmt_e effective_fmt; + AudioFormat effective_fmt; struct audsettings obt_as; req.freq = as->freq; req.format = aud_to_sdlfmt (as->fmt); req.channels = as->nchannels; - req.samples = conf.nb_samples; + req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); req.callback = sdl_callback; req.userdata = sdl; @@ -315,7 +310,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) { @@ -329,6 +324,7 @@ static void *sdl_audio_init (void) } s->driver_created = true; + s->dev = dev; return s; } @@ -338,18 +334,9 @@ static void sdl_audio_fini (void *opaque) sdl_close (s); 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, @@ -361,7 +348,6 @@ static struct audio_pcm_ops sdl_pcm_ops = { static 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 6ad0eafbc6..4f7873af5a 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = { .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, }; -static void *spice_audio_init (void) +static void *spice_audio_init(Audiodev *dev) { if (!using_spice) { return NULL; @@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as, settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; #endif settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; - settings.fmt = AUD_FMT_S16; + settings.fmt = AUDIO_FORMAT_S16; settings.endianness = AUDIO_HOST_ENDIANNESS; audio_pcm_init_info (&hw->info, &settings); @@ -258,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) settings.freq = SPICE_INTERFACE_RECORD_FREQ; #endif settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; - settings.fmt = AUD_FMT_S16; + settings.fmt = AUDIO_FORMAT_S16; settings.endianness = AUDIO_HOST_ENDIANNESS; audio_pcm_init_info (&hw->info, &settings); @@ -373,10 +373,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, @@ -394,7 +390,6 @@ static struct audio_pcm_ops audio_callbacks = { static 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 40adfa30c3..8d30f57296 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "qemu/timer.h" +#include "qapi/opts-visitor.h" #include "audio.h" #define AUDIO_CAP "wav" @@ -37,11 +38,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; @@ -112,25 +108,30 @@ 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->u.wav; + struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); + const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; stereo = wav_as.nchannels == 2; switch (wav_as.fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: bits16 = 0; break; - case AUD_FMT_S16: - case AUD_FMT_U16: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: bits16 = 1; break; - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: dolog ("WAVE files can not handle 32bit formats\n"); return -1; + + default: + abort(); } hdr[34] = bits16 ? 0x10 : 0x08; @@ -151,10 +152,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; @@ -222,54 +223,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } -static WAVConf glob_conf = { - .settings.freq = 44100, - .settings.nchannels = 2, - .settings.fmt = AUD_FMT_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->driver == 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, @@ -281,7 +245,6 @@ static struct audio_pcm_ops wav_pcm_ops = { static 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/audio/wavcapture.c b/audio/wavcapture.c index cd24570aa7..74320dfecc 100644 --- a/audio/wavcapture.c +++ b/audio/wavcapture.c @@ -136,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, as.freq = freq; as.nchannels = 1 << stereo; - as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; + as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; as.endianness = 0; ops.notify = wav_notify; diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 94dffb2f57..446223906e 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -273,7 +273,7 @@ static void omap_eac_format_update(struct omap_eac_s *s) * does I2S specify it? */ /* All register writes are 16 bits so we we store 16-bit samples * in the buffers regardless of AGCFR[B8_16] value. */ - fmt.fmt = AUD_FMT_U16; + fmt.fmt = AUDIO_FORMAT_U16; s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice, "eac.codec.in", s, omap_eac_in_cb, &fmt); diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index d799533aa9..2265622d44 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -365,7 +365,7 @@ static void open_voice (AC97LinkState *s, int index, int freq) as.freq = freq; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; if (freq > 0) { diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 97b876c7e0..0957780a3d 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -269,7 +269,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) as.freq = s->freq; as.nchannels = SHIFT; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = AUDIO_HOST_ENDIANNESS; AUD_register_card ("adlib", &s->card); diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 9089dcb47e..62da75eefe 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -288,7 +288,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) { case 0: - as.fmt = AUD_FMT_U8; + as.fmt = AUDIO_FORMAT_U8; s->shift = as.nchannels == 2; break; @@ -298,7 +298,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) case 3: s->tab = ALawDecompressTable; x_law: - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = AUDIO_HOST_ENDIANNESS; s->shift = as.nchannels == 2; break; @@ -307,7 +307,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) as.endianness = 1; /* fall through */ case 2: - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; s->shift = as.nchannels; break; diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 97789a0771..a5314d66fd 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -414,14 +414,14 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) i, new_freq, 1 << (new_fmt & 1), - (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8, + (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8, d->shift); if (new_freq) { struct audsettings as; as.freq = new_freq; as.nchannels = 1 << (new_fmt & 1); - as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8; + as.fmt = (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; as.endianness = 0; if (i == ADC_CHANNEL) { diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 8e0b27e0f2..b3e2a7fdd5 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -251,7 +251,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) as.freq = s->freq; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = GUS_ENDIANNESS; s->voice = AUD_open_out ( diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 617a1c1016..c25bfa38b1 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -99,9 +99,9 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) } switch (format & AC_FMT_BITS_MASK) { - case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break; - case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break; - case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break; + case AC_FMT_BITS_8: as->fmt = AUDIO_FORMAT_S8; break; + case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break; + case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break; } as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; @@ -134,12 +134,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) /* -------------------------------------------------------------------------- */ static const char *fmt2name[] = { - [ AUD_FMT_U8 ] = "PCM-U8", - [ AUD_FMT_S8 ] = "PCM-S8", - [ AUD_FMT_U16 ] = "PCM-U16", - [ AUD_FMT_S16 ] = "PCM-S16", - [ AUD_FMT_U32 ] = "PCM-U32", - [ AUD_FMT_S32 ] = "PCM-S32", + [ AUDIO_FORMAT_U8 ] = "PCM-U8", + [ AUDIO_FORMAT_S8 ] = "PCM-S8", + [ AUDIO_FORMAT_U16 ] = "PCM-U16", + [ AUDIO_FORMAT_S16 ] = "PCM-S16", + [ AUDIO_FORMAT_U32 ] = "PCM-U32", + [ AUDIO_FORMAT_S32 ] = "PCM-S32", }; typedef struct HDAAudioState HDAAudioState; diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c index a46f2301af..af8b22b541 100644 --- a/hw/audio/lm4549.c +++ b/hw/audio/lm4549.c @@ -185,7 +185,7 @@ void lm4549_write(lm4549_state *s, struct audsettings as; as.freq = value; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; s->voice = AUD_open_out( @@ -255,7 +255,7 @@ static int lm4549_post_load(void *opaque, int version_id) struct audsettings as; as.freq = freq; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; s->voice = AUD_open_out( @@ -292,7 +292,7 @@ void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) /* Open a default voice */ as.freq = 48000; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; s->voice = AUD_open_out( diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c index bc8db71ae0..90cce1e6ed 100644 --- a/hw/audio/milkymist-ac97.c +++ b/hw/audio/milkymist-ac97.c @@ -308,7 +308,7 @@ static void milkymist_ac97_realize(DeviceState *dev, Error **errp) as.freq = 48000; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 1; s->voice_in = AUD_open_in(&s->card, s->voice_in, diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index b80a62ce90..fdbb4b6e99 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -162,7 +162,7 @@ static void pcspk_initfn(Object *obj) static void pcspk_realizefn(DeviceState *dev, Error **errp) { - struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0}; + struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0}; ISADevice *isadev = ISA_DEVICE(dev); PCSpkState *s = PC_SPEAKER(dev); diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index c5b9bf79e8..65ea0cd938 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -66,7 +66,7 @@ typedef struct SB16State { int fmt_stereo; int fmt_signed; int fmt_bits; - audfmt_e fmt; + AudioFormat fmt; int dma_auto; int block_size; int fifo; @@ -224,7 +224,7 @@ static void continue_dma8 (SB16State *s) static void dma_cmd8 (SB16State *s, int mask, int dma_len) { - s->fmt = AUD_FMT_U8; + s->fmt = AUDIO_FORMAT_U8; s->use_hdma = 0; s->fmt_bits = 8; s->fmt_signed = 0; @@ -319,18 +319,18 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) if (16 == s->fmt_bits) { if (s->fmt_signed) { - s->fmt = AUD_FMT_S16; + s->fmt = AUDIO_FORMAT_S16; } else { - s->fmt = AUD_FMT_U16; + s->fmt = AUDIO_FORMAT_U16; } } else { if (s->fmt_signed) { - s->fmt = AUD_FMT_S8; + s->fmt = AUDIO_FORMAT_S8; } else { - s->fmt = AUD_FMT_U8; + s->fmt = AUDIO_FORMAT_U8; } } @@ -852,7 +852,7 @@ static void legacy_reset (SB16State *s) as.freq = s->freq; as.nchannels = 1; - as.fmt = AUD_FMT_U8; + as.fmt = AUDIO_FORMAT_U8; as.endianness = 0; s->voice = AUD_open_out ( diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 169b006ade..ca0ad73caf 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -201,7 +201,7 @@ static void wm8750_set_format(WM8750State *s) in_fmt.endianness = 0; in_fmt.nchannels = 2; in_fmt.freq = s->adc_hz; - in_fmt.fmt = AUD_FMT_S16; + in_fmt.fmt = AUDIO_FORMAT_S16; s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0], CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt); @@ -214,7 +214,7 @@ static void wm8750_set_format(WM8750State *s) out_fmt.endianness = 0; out_fmt.nchannels = 2; out_fmt.freq = s->dac_hz; - out_fmt.fmt = AUD_FMT_S16; + out_fmt.fmt = AUDIO_FORMAT_S16; s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt); @@ -681,7 +681,7 @@ uint32_t wm8750_adc_dat(void *opaque) if (s->idx_in >= sizeof(s->data_in)) { wm8750_in_load(s); if (s->idx_in >= sizeof(s->data_in)) { - return 0x80008000; /* silence in AUD_FMT_S16 sample format */ + return 0x80008000; /* silence in AUDIO_FORMAT_S16 sample format */ } } diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index cc0f9bc9cc..11b09bd18c 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1260,7 +1260,7 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) as.freq = 44100; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; AUD_register_card("xlnx_dp.audio", &s->aud_card); diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index 2eb3cb9518..41731619bb 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -318,7 +318,7 @@ static void tsc2102_audio_output_update(TSC210xState *s) fmt.endianness = 0; fmt.nchannels = 2; fmt.freq = s->codec.tx_rate; - fmt.fmt = AUD_FMT_S16; + fmt.fmt = AUDIO_FORMAT_S16; s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 28ac7c5165..c46d5eeb79 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -650,7 +650,7 @@ static void usb_audio_realize(USBDevice *dev, Error **errp) s->out.vol[1] = 240; /* 0 dB */ s->out.as.freq = USBAUDIO_SAMPLE_RATE; s->out.as.nchannels = 2; - s->out.as.fmt = AUD_FMT_S16; + s->out.as.fmt = AUDIO_FORMAT_S16; s->out.as.endianness = 0; streambuf_init(&s->out.buf, s->buffer); diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 77acca0209..729e5185c5 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -5,9 +5,9 @@ util-obj-y += opts-visitor.o qapi-clone-visitor.o util-obj-y += qmp-event.o util-obj-y += qapi-util.o -QAPI_COMMON_MODULES = authz block-core block char common crypto introspect -QAPI_COMMON_MODULES += job migration misc net rdma rocker run-state -QAPI_COMMON_MODULES += sockets tpm trace transaction ui +QAPI_COMMON_MODULES = audio authz block-core block char common crypto +QAPI_COMMON_MODULES += introspect job migration misc net rdma rocker +QAPI_COMMON_MODULES += run-state sockets tpm trace transaction ui QAPI_TARGET_MODULES = target QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES) diff --git a/qapi/audio.json b/qapi/audio.json new file mode 100644 index 0000000000..97aee37288 --- /dev/null +++ b/qapi/audio.json @@ -0,0 +1,304 @@ +# -*- mode: python -*- +# +# Copyright (C) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# @AudiodevPerDirectionOptions: +# +# General audio backend options that are used for both playback and +# recording. +# +# @fixed-settings: use fixed settings for host input/output. When off, +# frequency, channels and format must not be +# specified (default true) +# +# @frequency: frequency to use when using fixed settings +# (default 44100) +# +# @channels: number of channels when using fixed settings (default 2) +# +# @voices: number of voices to use (default 1) +# +# @format: sample format to use when using fixed settings +# (default s16) +# +# @buffer-length: the buffer length in microseconds +# +# Since: 4.0 +## +{ 'struct': 'AudiodevPerDirectionOptions', + 'data': { + '*fixed-settings': 'bool', + '*frequency': 'uint32', + '*channels': 'uint32', + '*voices': 'uint32', + '*format': 'AudioFormat', + '*buffer-length': 'uint32' } } + +## +# @AudiodevGenericOptions: +# +# Generic driver-specific options. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 4.0 +## +{ 'struct': 'AudiodevGenericOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions' } } + +## +# @AudiodevAlsaPerDirectionOptions: +# +# Options of the ALSA backend that are used for both playback and +# recording. +# +# @dev: the name of the ALSA device to use (default 'default') +# +# @period-length: the period length in microseconds +# +# @try-poll: attempt to use poll mode, falling back to non-polling +# access on failure (default true) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevAlsaPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*dev': 'str', + '*period-length': 'uint32', + '*try-poll': 'bool' } } + +## +# @AudiodevAlsaOptions: +# +# Options of the ALSA audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @threshold: set the threshold (in microseconds) when playback starts +# +# Since: 4.0 +## +{ 'struct': 'AudiodevAlsaOptions', + 'data': { + '*in': 'AudiodevAlsaPerDirectionOptions', + '*out': 'AudiodevAlsaPerDirectionOptions', + '*threshold': 'uint32' } } + +## +# @AudiodevCoreaudioPerDirectionOptions: +# +# Options of the Core Audio backend that are used for both playback and +# recording. +# +# @buffer-count: number of buffers +# +# Since: 4.0 +## +{ 'struct': 'AudiodevCoreaudioPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*buffer-count': 'uint32' } } + +## +# @AudiodevCoreaudioOptions: +# +# Options of the coreaudio audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 4.0 +## +{ 'struct': 'AudiodevCoreaudioOptions', + 'data': { + '*in': 'AudiodevCoreaudioPerDirectionOptions', + '*out': 'AudiodevCoreaudioPerDirectionOptions' } } + +## +# @AudiodevDsoundOptions: +# +# Options of the DirectSound audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @latency: add extra latency to playback in microseconds +# (default 10000) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevDsoundOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*latency': 'uint32' } } + +## +# @AudiodevOssPerDirectionOptions: +# +# Options of the OSS backend that are used for both playback and +# recording. +# +# @dev: file name of the OSS device (default '/dev/dsp') +# +# @buffer-count: number of buffers +# +# @try-poll: attempt to use poll mode, falling back to non-polling +# access on failure (default true) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevOssPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*dev': 'str', + '*buffer-count': 'uint32', + '*try-poll': 'bool' } } + +## +# @AudiodevOssOptions: +# +# Options of the OSS audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @try-mmap: try using memory-mapped access, falling back to +# non-memory-mapped access on failure (default true) +# +# @exclusive: open device in exclusive mode (vmix won't work) +# (default false) +# +# @dsp-policy: set the timing policy of the device (between 0 and 10, +# where smaller number means smaller latency but higher +# CPU usage) or -1 to use fragment mode (option ignored +# on some platforms) (default 5) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevOssOptions', + 'data': { + '*in': 'AudiodevOssPerDirectionOptions', + '*out': 'AudiodevOssPerDirectionOptions', + '*try-mmap': 'bool', + '*exclusive': 'bool', + '*dsp-policy': 'uint32' } } + +## +# @AudiodevPaPerDirectionOptions: +# +# Options of the Pulseaudio backend that are used for both playback and +# recording. +# +# @name: name of the sink/source to use +# +# Since: 4.0 +## +{ 'struct': 'AudiodevPaPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*name': 'str' } } + +## +# @AudiodevPaOptions: +# +# Options of the PulseAudio audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @server: PulseAudio server address (default: let PulseAudio choose) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevPaOptions', + 'data': { + '*in': 'AudiodevPaPerDirectionOptions', + '*out': 'AudiodevPaPerDirectionOptions', + '*server': 'str' } } + +## +# @AudiodevWavOptions: +# +# Options of the wav audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @path: name of the wav file to record (default 'qemu.wav') +# +# Since: 4.0 +## +{ 'struct': 'AudiodevWavOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*path': 'str' } } + + +## +# @AudioFormat: +# +# An enumeration of possible audio formats. +# +# Since: 4.0 +## +{ 'enum': 'AudioFormat', + 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] } + +## +# @AudiodevDriver: +# +# An enumeration of possible audio backend drivers. +# +# Since: 4.0 +## +{ 'enum': 'AudiodevDriver', + 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', + 'spice', 'wav' ] } + +## +# @Audiodev: +# +# Options of an audio backend. +# +# @id: identifier of the backend +# +# @driver: the backend driver to use +# +# @timer-period: timer period (in microseconds, 0: use lowest possible) +# +# Since: 4.0 +## +{ 'union': 'Audiodev', + 'base': { + 'id': 'str', + 'driver': 'AudiodevDriver', + '*timer-period': 'uint32' }, + 'discriminator': 'driver', + 'data': { + 'none': 'AudiodevGenericOptions', + 'alsa': 'AudiodevAlsaOptions', + 'coreaudio': 'AudiodevCoreaudioOptions', + 'dsound': 'AudiodevDsoundOptions', + 'oss': 'AudiodevOssOptions', + 'pa': 'AudiodevPaOptions', + 'sdl': 'AudiodevGenericOptions', + 'spice': 'AudiodevGenericOptions', + 'wav': 'AudiodevWavOptions' } } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index a34899c626..4bd1223637 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -99,3 +99,4 @@ { 'include': 'introspect.json' } { 'include': 'misc.json' } { 'include': 'target.json' } +{ 'include': 'audio.json' } diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index 1e15f57e9c..1cf10fc78b 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -65,6 +65,13 @@ topologies described with -smp include all possible cpus, i.e. The @code{acl} option to the @code{-vnc} argument has been replaced by the @code{tls-authz} and @code{sasl-authz} options. +@subsection QEMU_AUDIO_ environment variables and -audio-help (since 4.0) + +The ``-audiodev'' argument is now the preferred way to specify audio +backend settings instead of environment variables. To ease migration to +the new format, the ``-audiodev-help'' option can be used to convert +the current values of the environment variables to ``-audiodev'' options. + @section QEMU Machine Protocol (QMP) commands @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) diff --git a/qemu-options.hx b/qemu-options.hx index 7118d90352..8693f5fa3c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -416,14 +416,244 @@ 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 currently specified audio settings\n", QEMU_ARCH_ALL) STEXI @item -audio-help @findex -audio-help -Will show the audio subsystem help: list of drivers, tunable -parameters. +Will show the -audiodev equivalent of the currently specified +(deprecated) environment variables. +ETEXI + +DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, + "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n" + " specifies the audio backend to use\n" + " id= identifier of the backend\n" + " timer-period= timer period in microseconds\n" + " in|out.fixed-settings= use fixed settings for host audio\n" + " in|out.frequency= frequency to use with fixed settings\n" + " in|out.channels= number of channels to use with fixed settings\n" + " in|out.format= sample format to use with fixed settings\n" + " valid values: s8, s16, s32, u8, u16, u32\n" + " in|out.voices= number of voices to use\n" + " in|out.buffer-len= length of buffer in microseconds\n" + "-audiodev none,id=id,[,prop[=value][,...]]\n" + " dummy driver that discards all output\n" +#ifdef CONFIG_AUDIO_ALSA + "-audiodev alsa,id=id[,prop[=value][,...]]\n" + " in|out.dev= name of the audio device to use\n" + " in|out.period-len= length of period in microseconds\n" + " in|out.try-poll= attempt to use poll mode\n" + " threshold= threshold (in microseconds) when playback starts\n" +#endif +#ifdef CONFIG_AUDIO_COREAUDIO + "-audiodev coreaudio,id=id[,prop[=value][,...]]\n" + " in|out.buffer-count= number of buffers\n" +#endif +#ifdef CONFIG_AUDIO_DSOUND + "-audiodev dsound,id=id[,prop[=value][,...]]\n" + " latency= add extra latency to playback in microseconds\n" +#endif +#ifdef CONFIG_AUDIO_OSS + "-audiodev oss,id=id[,prop[=value][,...]]\n" + " in|out.dev= path of the audio device to use\n" + " in|out.buffer-count= number of buffers\n" + " in|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_AUDIO_PA + "-audiodev pa,id=id[,prop[=value][,...]]\n" + " server= PulseAudio server address\n" + " in|out.name= source/sink device name\n" +#endif +#ifdef CONFIG_AUDIO_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,id=example,in.frequency=44110,out.frequency=8000 +-audiodev alsa,id=example,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. + +@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 in|out.dev=@var{device} +Specify the ALSA @var{device} to use for input and/or output. Default +is @code{default}. + +@item in|out.period-len=@var{usecs} +Sets the period length in microseconds. + +@item in|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. + +Core Audio specific options are: + +@table @option + +@item in|out.buffer-count=@var{count} +Sets the @var{count} of the buffers. + +@end table + +@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. + +DirectSound 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 in|out.dev=@var{device} +Specify the file name of the OSS @var{device} to use. Default is +@code{/dev/dsp}. + +@item in|out.buffer-count=@var{count} +Sets the @var{count} of the buffers. + +@item in|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 in|out.name=@var{sink} +Use the specified source/sink for recording/playback. + +@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, @@ -1019,16 +1019,16 @@ static void vnc_update_throttle_offset(VncState *vs) int bps; switch (vs->as.fmt) { default: - case AUD_FMT_U8: - case AUD_FMT_S8: + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S8: bps = 1; break; - case AUD_FMT_U16: - case AUD_FMT_S16: + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S16: bps = 2; break; - case AUD_FMT_U32: - case AUD_FMT_S32: + case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_S32: bps = 4; break; } @@ -2375,12 +2375,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 4) return 10; switch (read_u8(data, 4)) { - case 0: vs->as.fmt = AUD_FMT_U8; break; - case 1: vs->as.fmt = AUD_FMT_S8; break; - case 2: vs->as.fmt = AUD_FMT_U16; break; - case 3: vs->as.fmt = AUD_FMT_S16; break; - case 4: vs->as.fmt = AUD_FMT_U32; break; - case 5: vs->as.fmt = AUD_FMT_S32; break; + case 0: vs->as.fmt = AUDIO_FORMAT_U8; break; + case 1: vs->as.fmt = AUDIO_FORMAT_S8; break; + case 2: vs->as.fmt = AUDIO_FORMAT_U16; break; + case 3: vs->as.fmt = AUDIO_FORMAT_S16; break; + case 4: vs->as.fmt = AUDIO_FORMAT_U32; break; + case 5: vs->as.fmt = AUDIO_FORMAT_S32; break; default: VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4)); vnc_client_error(vs); @@ -3111,7 +3111,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, vs->as.freq = 44100; vs->as.nchannels = 2; - vs->as.fmt = AUD_FMT_S16; + vs->as.fmt = AUDIO_FORMAT_S16; vs->as.endianness = 0; qemu_mutex_init(&vs->output_mutex); @@ -3285,9 +3285,12 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_BT, optarg); break; case QEMU_OPTION_audio_help: - AUD_help (); + audio_legacy_help(); exit (0); break; + case QEMU_OPTION_audiodev: + audio_parse_option(optarg); + break; case QEMU_OPTION_soundhw: select_soundhw (optarg); break; @@ -4454,6 +4457,8 @@ int main(int argc, char **argv, char **envp) /* do monitor/qmp handling at preconfig state if requested */ main_loop(); + audio_init_audiodevs(); + /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); |