summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Maydell2019-03-12 17:45:13 +0100
committerPeter Maydell2019-03-12 17:45:13 +0100
commitcfc3fef6b4e493bf1a7ee16790ad584e20dfbbd1 (patch)
tree7092a7ad69eb6676bb66ded90d94889bfeba28c4
parentMerge remote-tracking branch 'remotes/ehabkost/tags/machine-next-pull-request... (diff)
parentaudio: -audiodev command line option: cleanup (diff)
downloadqemu-cfc3fef6b4e493bf1a7ee16790ad584e20dfbbd1.tar.gz
qemu-cfc3fef6b4e493bf1a7ee16790ad584e20dfbbd1.tar.xz
qemu-cfc3fef6b4e493bf1a7ee16790ad584e20dfbbd1.zip
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
-rw-r--r--audio/Makefile.objs2
-rw-r--r--audio/alsaaudio.c370
-rw-r--r--audio/audio.c859
-rw-r--r--audio/audio.h30
-rw-r--r--audio/audio_int.h37
-rw-r--r--audio/audio_legacy.c544
-rw-r--r--audio/audio_template.h42
-rw-r--r--audio/audio_win_int.c18
-rw-r--r--audio/coreaudio.c51
-rw-r--r--audio/dsound_template.h6
-rw-r--r--audio/dsoundaudio.c61
-rw-r--r--audio/noaudio.c3
-rw-r--r--audio/ossaudio.c191
-rw-r--r--audio/paaudio.c111
-rw-r--r--audio/sdlaudio.c50
-rw-r--r--audio/spiceaudio.c11
-rw-r--r--audio/wavaudio.c75
-rw-r--r--audio/wavcapture.c2
-rw-r--r--hw/arm/omap2.c2
-rw-r--r--hw/audio/ac97.c2
-rw-r--r--hw/audio/adlib.c2
-rw-r--r--hw/audio/cs4231a.c6
-rw-r--r--hw/audio/es1370.c4
-rw-r--r--hw/audio/gus.c2
-rw-r--r--hw/audio/hda-codec.c18
-rw-r--r--hw/audio/lm4549.c6
-rw-r--r--hw/audio/milkymist-ac97.c2
-rw-r--r--hw/audio/pcspk.c2
-rw-r--r--hw/audio/sb16.c14
-rw-r--r--hw/audio/wm8750.c6
-rw-r--r--hw/display/xlnx_dp.c2
-rw-r--r--hw/input/tsc210x.c2
-rw-r--r--hw/usb/dev-audio.c2
-rw-r--r--qapi/Makefile.objs6
-rw-r--r--qapi/audio.json304
-rw-r--r--qapi/qapi-schema.json1
-rw-r--r--qemu-deprecated.texi7
-rw-r--r--qemu-options.hx236
-rw-r--r--ui/vnc.c26
-rw-r--r--vl.c7
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,
diff --git a/ui/vnc.c b/ui/vnc.c
index 2d9e8f43b0..1871422e1d 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -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);
diff --git a/vl.c b/vl.c
index 5616208ea0..027b853d92 100644
--- a/vl.c
+++ b/vl.c
@@ -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);