mirror of https://github.com/xemu-project/xemu.git
audio: introduce -audiodev
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJch1vTAAoJEEy22O7T6HE4KL8QAI7VfMK8ZKTx/9Su46RL1A0G xNYhapdkhK2DEMd7JDY+eg8I8VQEwWroEjphCxLdx3p84Gr44gwgj3T24iRlXugq hJEgtBR548bBBdGd6KRihS8ZOro1T2gXol/TRq9z/TEF2LU6AVy52riZ/iydEd2U /ibkISR/vLdOnQ1Mj4YpchCZHx1yvMjSOhF/Cw2kXUm8C0jEuj3ws/BfdZQ8DxDG ayzS7JwSGOedsoFj+yfWX/YjCiocaXSLux9kyACzhHSJcA/5hw5srbXyhe4JrgdY BEqaUa23KiulgFt5fXnprktq+BQba/a4Tbx+YZFaNvX4HXqVccnCNN+VMkV4CvBN Y6UwijfaePVaQFP4kP+vpvqdPoJHNGikPlX8j3Q7ofL5+/c7Qk9yChNyB8lpfOIe KBNNsIIJO2GVU0IVNuqIGldWZYQw2y8ojpNSntg5lyFIrwZ+ipDFuxD9weZibVgc pa4VfPNKW4lOWrDX0PLv5eTNWOLamY3T1wY6pQCOtHgzlChRa28pMWkB7VkzTTQW gzipigqpVzmd3l1m2uI7LOovZa326IrGRC1/Yb4a41Gz58p9a1U2sYTijhPlAaeq VDMafPTAIJYpg0MW5Uxh1eB05WzNafPfEznjMJ/Z84J4P/oGFxlOGcL99sAbrxpl cQWWZlBGINQhxZ1PtvJc =7YpW -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
cfc3fef6b4
|
@ -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_SPICE) += spiceaudio.o
|
||||||
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
|
||||||
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
|
||||||
|
|
|
@ -33,28 +33,9 @@
|
||||||
#define AUDIO_CAP "alsa"
|
#define AUDIO_CAP "alsa"
|
||||||
#include "audio_int.h"
|
#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 {
|
struct pollhlp {
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct pollfd *pfds;
|
struct pollfd *pfds;
|
||||||
ALSAConf *conf;
|
|
||||||
int count;
|
int count;
|
||||||
int mask;
|
int mask;
|
||||||
};
|
};
|
||||||
|
@ -66,6 +47,7 @@ typedef struct ALSAVoiceOut {
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct pollhlp pollhlp;
|
struct pollhlp pollhlp;
|
||||||
|
Audiodev *dev;
|
||||||
} ALSAVoiceOut;
|
} ALSAVoiceOut;
|
||||||
|
|
||||||
typedef struct ALSAVoiceIn {
|
typedef struct ALSAVoiceIn {
|
||||||
|
@ -73,21 +55,18 @@ typedef struct ALSAVoiceIn {
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
struct pollhlp pollhlp;
|
struct pollhlp pollhlp;
|
||||||
|
Audiodev *dev;
|
||||||
} ALSAVoiceIn;
|
} ALSAVoiceIn;
|
||||||
|
|
||||||
struct alsa_params_req {
|
struct alsa_params_req {
|
||||||
int freq;
|
int freq;
|
||||||
snd_pcm_format_t fmt;
|
snd_pcm_format_t fmt;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
int size_in_usec;
|
|
||||||
int override_mask;
|
|
||||||
unsigned int buffer_size;
|
|
||||||
unsigned int period_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct alsa_params_obt {
|
struct alsa_params_obt {
|
||||||
int freq;
|
int freq;
|
||||||
audfmt_e fmt;
|
AudioFormat fmt;
|
||||||
int endianness;
|
int endianness;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
snd_pcm_uframes_t samples;
|
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);
|
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) {
|
switch (fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
return SND_PCM_FORMAT_S8;
|
return SND_PCM_FORMAT_S8;
|
||||||
|
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
return SND_PCM_FORMAT_U8;
|
return SND_PCM_FORMAT_U8;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_S16_BE;
|
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;
|
return SND_PCM_FORMAT_S16_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_U16_BE;
|
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;
|
return SND_PCM_FORMAT_U16_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_S32_BE;
|
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;
|
return SND_PCM_FORMAT_S32_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return SND_PCM_FORMAT_U32_BE;
|
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)
|
int *endianness)
|
||||||
{
|
{
|
||||||
switch (alsafmt) {
|
switch (alsafmt) {
|
||||||
case SND_PCM_FORMAT_S8:
|
case SND_PCM_FORMAT_S8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S8;
|
*fmt = AUDIO_FORMAT_S8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U8:
|
case SND_PCM_FORMAT_U8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U8;
|
*fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S16_LE:
|
case SND_PCM_FORMAT_S16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U16_LE:
|
case SND_PCM_FORMAT_U16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S16_BE:
|
case SND_PCM_FORMAT_S16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U16_BE:
|
case SND_PCM_FORMAT_U16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S32_LE:
|
case SND_PCM_FORMAT_S32_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S32;
|
*fmt = AUDIO_FORMAT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U32_LE:
|
case SND_PCM_FORMAT_U32_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U32;
|
*fmt = AUDIO_FORMAT_U32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_S32_BE:
|
case SND_PCM_FORMAT_S32_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S32;
|
*fmt = AUDIO_FORMAT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SND_PCM_FORMAT_U32_BE:
|
case SND_PCM_FORMAT_U32_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U32;
|
*fmt = AUDIO_FORMAT_U32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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,
|
static void alsa_dump_info (struct alsa_params_req *req,
|
||||||
struct alsa_params_obt *obt,
|
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("parameter | requested value | obtained value\n");
|
||||||
dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
|
dolog("format | %10d | %10d\n", req->fmt, obtfmt);
|
||||||
dolog ("channels | %10d | %10d\n",
|
dolog("channels | %10d | %10d\n",
|
||||||
req->nchannels, obt->nchannels);
|
req->nchannels, obt->nchannels);
|
||||||
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
|
dolog("frequency | %10d | %10d\n", req->freq, obt->freq);
|
||||||
dolog ("============================================\n");
|
dolog("============================================\n");
|
||||||
dolog ("requested: buffer size %d period size %d\n",
|
dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n",
|
||||||
req->buffer_size, req->period_size);
|
apdo->buffer_length, apdo->period_length);
|
||||||
dolog ("obtained: samples %ld\n", obt->samples);
|
dolog("obtained: samples %ld\n", obt->samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
|
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,
|
static int alsa_open(bool in, struct alsa_params_req *req,
|
||||||
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
struct alsa_params_obt *obt, snd_pcm_t **handlep,
|
||||||
ALSAConf *conf)
|
Audiodev *dev)
|
||||||
{
|
{
|
||||||
|
AudiodevAlsaOptions *aopts = &dev->u.alsa;
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
snd_pcm_hw_params_t *hw_params;
|
snd_pcm_hw_params_t *hw_params;
|
||||||
int err;
|
int err;
|
||||||
int size_in_usec;
|
|
||||||
unsigned int freq, nchannels;
|
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;
|
snd_pcm_uframes_t obt_buffer_size;
|
||||||
const char *typ = in ? "ADC" : "DAC";
|
const char *typ = in ? "ADC" : "DAC";
|
||||||
snd_pcm_format_t obtfmt;
|
snd_pcm_format_t obtfmt;
|
||||||
|
|
||||||
freq = req->freq;
|
freq = req->freq;
|
||||||
nchannels = req->nchannels;
|
nchannels = req->nchannels;
|
||||||
size_in_usec = req->size_in_usec;
|
|
||||||
|
|
||||||
snd_pcm_hw_params_alloca (&hw_params);
|
snd_pcm_hw_params_alloca (&hw_params);
|
||||||
|
|
||||||
|
@ -527,79 +507,42 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->buffer_size) {
|
if (apdo->buffer_length) {
|
||||||
unsigned long obt;
|
int dir = 0;
|
||||||
|
unsigned int btime = apdo->buffer_length;
|
||||||
|
|
||||||
if (size_in_usec) {
|
err = snd_pcm_hw_params_set_buffer_time_near(
|
||||||
int dir = 0;
|
handle, hw_params, &btime, &dir);
|
||||||
unsigned int btime = req->buffer_size;
|
|
||||||
|
|
||||||
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) {
|
if (err < 0) {
|
||||||
alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
|
alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n",
|
||||||
size_in_usec ? "time" : "size", req->buffer_size);
|
apdo->buffer_length);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((req->override_mask & 2) && (obt - req->buffer_size))
|
if (apdo->has_buffer_length && btime != apdo->buffer_length) {
|
||||||
dolog ("Requested buffer %s %u was rejected, using %lu\n",
|
dolog("Requested buffer time %" PRId32
|
||||||
size_in_usec ? "time" : "size", req->buffer_size, obt);
|
" was rejected, using %u\n", apdo->buffer_length, btime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req->period_size) {
|
if (apdo->period_length) {
|
||||||
unsigned long obt;
|
int dir = 0;
|
||||||
|
unsigned int ptime = apdo->period_length;
|
||||||
|
|
||||||
if (size_in_usec) {
|
err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime,
|
||||||
int dir = 0;
|
&dir);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
|
alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n",
|
||||||
size_in_usec ? "time" : "size", req->period_size);
|
apdo->period_length);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((req->override_mask & 1) && (obt - req->period_size)))
|
if (apdo->has_period_length && ptime != apdo->period_length) {
|
||||||
dolog ("Requested period %s %u was rejected, using %lu\n",
|
dolog("Requested period time %" PRId32 " was rejected, using %d\n",
|
||||||
size_in_usec ? "time" : "size", req->period_size, obt);
|
apdo->period_length, ptime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = snd_pcm_hw_params (handle, hw_params);
|
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;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in && conf->threshold) {
|
if (!in && aopts->has_threshold && aopts->threshold) {
|
||||||
snd_pcm_uframes_t threshold;
|
struct audsettings as = { .freq = freq };
|
||||||
int bytes_per_sec;
|
alsa_set_threshold(
|
||||||
|
handle,
|
||||||
bytes_per_sec = freq << (nchannels == 2);
|
audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo),
|
||||||
|
&as, aopts->threshold));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
obt->nchannels = nchannels;
|
obt->nchannels = nchannels;
|
||||||
|
@ -667,11 +592,11 @@ static int alsa_open (int in, struct alsa_params_req *req,
|
||||||
obt->nchannels != req->nchannels ||
|
obt->nchannels != req->nchannels ||
|
||||||
obt->freq != req->freq) {
|
obt->freq != req->freq) {
|
||||||
dolog ("Audio parameters for %s\n", typ);
|
dolog ("Audio parameters for %s\n", typ);
|
||||||
alsa_dump_info (req, obt, obtfmt);
|
alsa_dump_info(req, obt, obtfmt, apdo);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
alsa_dump_info (req, obt, obtfmt);
|
alsa_dump_info(req, obt, obtfmt, pdo);
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -797,19 +722,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
struct alsa_params_obt obt;
|
struct alsa_params_obt obt;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
ALSAConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,7 +749,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
alsa->handle = handle;
|
alsa->handle = handle;
|
||||||
alsa->pollhlp.conf = conf;
|
alsa->dev = dev;
|
||||||
return 0;
|
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, ...)
|
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = apdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
ldebug ("enabling voice\n");
|
ldebug ("enabling voice\n");
|
||||||
if (poll_mode && alsa_poll_out (hw)) {
|
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;
|
struct alsa_params_obt obt;
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
ALSAConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,7 +850,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
alsa->handle = handle;
|
alsa->handle = handle;
|
||||||
alsa->pollhlp.conf = conf;
|
alsa->dev = dev;
|
||||||
return 0;
|
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, ...)
|
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
|
||||||
|
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = apdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
ldebug ("enabling voice\n");
|
ldebug ("enabling voice\n");
|
||||||
if (poll_mode && alsa_poll_in (hw)) {
|
if (poll_mode && alsa_poll_in (hw)) {
|
||||||
|
@ -1115,88 +1020,54 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ALSAConf glob_conf = {
|
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
|
||||||
.buffer_size_out = 4096,
|
|
||||||
.period_size_out = 1024,
|
|
||||||
.pcm_name_out = "default",
|
|
||||||
.pcm_name_in = "default",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *alsa_audio_init (void)
|
|
||||||
{
|
{
|
||||||
ALSAConf *conf = g_malloc(sizeof(ALSAConf));
|
if (!apdo->has_try_poll) {
|
||||||
*conf = glob_conf;
|
apdo->try_poll = true;
|
||||||
return conf;
|
apdo->has_try_poll = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *alsa_audio_init(Audiodev *dev)
|
||||||
|
{
|
||||||
|
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)
|
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 = {
|
static struct audio_pcm_ops alsa_pcm_ops = {
|
||||||
.init_out = alsa_init_out,
|
.init_out = alsa_init_out,
|
||||||
.fini_out = alsa_fini_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 = {
|
static struct audio_driver alsa_audio_driver = {
|
||||||
.name = "alsa",
|
.name = "alsa",
|
||||||
.descr = "ALSA http://www.alsa-project.org",
|
.descr = "ALSA http://www.alsa-project.org",
|
||||||
.options = alsa_options,
|
|
||||||
.init = alsa_audio_init,
|
.init = alsa_audio_init,
|
||||||
.fini = alsa_audio_fini,
|
.fini = alsa_audio_fini,
|
||||||
.pcm_ops = &alsa_pcm_ops,
|
.pcm_ops = &alsa_pcm_ops,
|
||||||
|
|
859
audio/audio.c
859
audio/audio.c
File diff suppressed because it is too large
Load Diff
|
@ -26,30 +26,31 @@
|
||||||
#define QEMU_AUDIO_H
|
#define QEMU_AUDIO_H
|
||||||
|
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
#include "qapi/qapi-types-audio.h"
|
||||||
|
|
||||||
typedef void (*audio_callback_fn) (void *opaque, int avail);
|
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
|
#ifdef HOST_WORDS_BIGENDIAN
|
||||||
#define AUDIO_HOST_ENDIANNESS 1
|
#define AUDIO_HOST_ENDIANNESS 1
|
||||||
#else
|
#else
|
||||||
#define AUDIO_HOST_ENDIANNESS 0
|
#define AUDIO_HOST_ENDIANNESS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct audsettings {
|
typedef struct audsettings {
|
||||||
int freq;
|
int freq;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
audfmt_e fmt;
|
AudioFormat fmt;
|
||||||
int endianness;
|
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 {
|
typedef enum {
|
||||||
AUD_CNOTIFY_ENABLE,
|
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_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_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_register_card (const char *name, QEMUSoundCard *card);
|
||||||
void AUD_remove_card (QEMUSoundCard *card);
|
void AUD_remove_card (QEMUSoundCard *card);
|
||||||
CaptureVoiceOut *AUD_add_capture (
|
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,
|
void audio_sample_from_uint64(void *samples, int pos,
|
||||||
uint64_t left, uint64_t right);
|
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 */
|
#endif /* QEMU_AUDIO_H */
|
||||||
|
|
|
@ -33,22 +33,6 @@
|
||||||
|
|
||||||
struct audio_pcm_ops;
|
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 {
|
struct audio_callback {
|
||||||
void *opaque;
|
void *opaque;
|
||||||
audio_callback_fn fn;
|
audio_callback_fn fn;
|
||||||
|
@ -145,8 +129,7 @@ typedef struct audio_driver audio_driver;
|
||||||
struct audio_driver {
|
struct audio_driver {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *descr;
|
const char *descr;
|
||||||
struct audio_option *options;
|
void *(*init) (Audiodev *);
|
||||||
void *(*init) (void);
|
|
||||||
void (*fini) (void *);
|
void (*fini) (void *);
|
||||||
struct audio_pcm_ops *pcm_ops;
|
struct audio_pcm_ops *pcm_ops;
|
||||||
int can_be_default;
|
int can_be_default;
|
||||||
|
@ -193,6 +176,7 @@ struct SWVoiceCap {
|
||||||
|
|
||||||
typedef struct AudioState {
|
typedef struct AudioState {
|
||||||
struct audio_driver *drv;
|
struct audio_driver *drv;
|
||||||
|
Audiodev *dev;
|
||||||
void *drv_opaque;
|
void *drv_opaque;
|
||||||
|
|
||||||
QEMUTimer *ts;
|
QEMUTimer *ts;
|
||||||
|
@ -203,10 +187,13 @@ typedef struct AudioState {
|
||||||
int nb_hw_voices_out;
|
int nb_hw_voices_out;
|
||||||
int nb_hw_voices_in;
|
int nb_hw_voices_in;
|
||||||
int vm_running;
|
int vm_running;
|
||||||
|
int64_t period_ticks;
|
||||||
} AudioState;
|
} AudioState;
|
||||||
|
|
||||||
extern const struct mixeng_volume nominal_volume;
|
extern const struct mixeng_volume nominal_volume;
|
||||||
|
|
||||||
|
extern const char *audio_prio_list[];
|
||||||
|
|
||||||
void audio_driver_register(audio_driver *drv);
|
void audio_driver_register(audio_driver *drv);
|
||||||
audio_driver *audio_driver_lookup(const char *name);
|
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) #n
|
||||||
#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(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 */
|
#endif /* QEMU_AUDIO_INT_H */
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -299,11 +299,42 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
|
||||||
return NULL;
|
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)
|
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
|
||||||
{
|
{
|
||||||
HW *hw;
|
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);
|
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
|
||||||
if (hw) {
|
if (hw) {
|
||||||
return hw;
|
return hw;
|
||||||
|
@ -331,9 +362,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
|
||||||
SW *sw;
|
SW *sw;
|
||||||
HW *hw;
|
HW *hw;
|
||||||
struct audsettings hw_as;
|
struct audsettings hw_as;
|
||||||
|
AudioState *s = &glob_audio_state;
|
||||||
|
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
||||||
if (glue (conf.fixed_, TYPE).enabled) {
|
if (pdo->fixed_settings) {
|
||||||
hw_as = glue (conf.fixed_, TYPE).settings;
|
hw_as = audiodev_to_audsettings(pdo);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
hw_as = *as;
|
hw_as = *as;
|
||||||
|
@ -398,6 +431,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
AudioState *s = &glob_audio_state;
|
AudioState *s = &glob_audio_state;
|
||||||
|
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
|
||||||
|
|
||||||
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
|
||||||
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
|
||||||
|
@ -422,7 +456,7 @@ SW *glue (AUD_open_, TYPE) (
|
||||||
return sw;
|
return sw;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!glue (conf.fixed_, TYPE).enabled && sw) {
|
if (!pdo->fixed_settings && sw) {
|
||||||
glue (AUD_close_, TYPE) (card, sw);
|
glue (AUD_close_, TYPE) (card, sw);
|
||||||
sw = NULL;
|
sw = NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
|
||||||
wfx->cbSize = 0;
|
wfx->cbSize = 0;
|
||||||
|
|
||||||
switch (as->fmt) {
|
switch (as->fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
wfx->wBitsPerSample = 8;
|
wfx->wBitsPerSample = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
wfx->wBitsPerSample = 16;
|
wfx->wBitsPerSample = 16;
|
||||||
wfx->nAvgBytesPerSec <<= 1;
|
wfx->nAvgBytesPerSec <<= 1;
|
||||||
wfx->nBlockAlign <<= 1;
|
wfx->nBlockAlign <<= 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
wfx->wBitsPerSample = 32;
|
wfx->wBitsPerSample = 32;
|
||||||
wfx->nAvgBytesPerSec <<= 2;
|
wfx->nAvgBytesPerSec <<= 2;
|
||||||
wfx->nBlockAlign <<= 2;
|
wfx->nBlockAlign <<= 2;
|
||||||
|
@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
|
||||||
|
|
||||||
switch (wfx->wBitsPerSample) {
|
switch (wfx->wBitsPerSample) {
|
||||||
case 8:
|
case 8:
|
||||||
as->fmt = AUD_FMT_U8;
|
as->fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
as->fmt = AUD_FMT_S16;
|
as->fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 32:
|
case 32:
|
||||||
as->fmt = AUD_FMT_S32;
|
as->fmt = AUDIO_FORMAT_S32;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -36,11 +36,6 @@
|
||||||
#define MAC_OS_X_VERSION_10_6 1060
|
#define MAC_OS_X_VERSION_10_6 1060
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int buffer_frames;
|
|
||||||
int nbuffers;
|
|
||||||
} CoreaudioConf;
|
|
||||||
|
|
||||||
typedef struct coreaudioVoiceOut {
|
typedef struct coreaudioVoiceOut {
|
||||||
HWVoiceOut hw;
|
HWVoiceOut hw;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
|
@ -507,7 +502,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
int err;
|
int err;
|
||||||
const char *typ = "playback";
|
const char *typ = "playback";
|
||||||
AudioValueRange frameRange;
|
AudioValueRange frameRange;
|
||||||
CoreaudioConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
|
||||||
|
int frames;
|
||||||
|
|
||||||
/* create mutex */
|
/* create mutex */
|
||||||
err = pthread_mutex_init(&core->mutex, NULL);
|
err = pthread_mutex_init(&core->mutex, NULL);
|
||||||
|
@ -538,16 +535,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
return -1;
|
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;
|
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
|
||||||
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
|
||||||
}
|
} else if (frameRange.mMaximum < frames) {
|
||||||
else if (frameRange.mMaximum < conf->buffer_frames) {
|
|
||||||
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
|
||||||
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
|
core->audioDevicePropertyBufferFrameSize = frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set Buffer Frame Size */
|
/* 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");
|
"Could not get device buffer frame size\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
|
hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
|
||||||
|
core->audioDevicePropertyBufferFrameSize;
|
||||||
|
|
||||||
/* get StreamFormat */
|
/* get StreamFormat */
|
||||||
status = coreaudio_get_streamformat(core->outputDeviceID,
|
status = coreaudio_get_streamformat(core->outputDeviceID,
|
||||||
|
@ -680,40 +679,15 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CoreaudioConf glob_conf = {
|
static void *coreaudio_audio_init(Audiodev *dev)
|
||||||
.buffer_frames = 512,
|
|
||||||
.nbuffers = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *coreaudio_audio_init (void)
|
|
||||||
{
|
{
|
||||||
CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
|
return dev;
|
||||||
*conf = glob_conf;
|
|
||||||
|
|
||||||
return conf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coreaudio_audio_fini (void *opaque)
|
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 = {
|
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||||
.init_out = coreaudio_init_out,
|
.init_out = coreaudio_init_out,
|
||||||
.fini_out = coreaudio_fini_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 = {
|
static struct audio_driver coreaudio_audio_driver = {
|
||||||
.name = "coreaudio",
|
.name = "coreaudio",
|
||||||
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
.descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
|
||||||
.options = coreaudio_options,
|
|
||||||
.init = coreaudio_audio_init,
|
.init = coreaudio_audio_init,
|
||||||
.fini = coreaudio_audio_fini,
|
.fini = coreaudio_audio_fini,
|
||||||
.pcm_ops = &coreaudio_pcm_ops,
|
.pcm_ops = &coreaudio_pcm_ops,
|
||||||
|
|
|
@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
dsound *s = drv_opaque;
|
dsound *s = drv_opaque;
|
||||||
WAVEFORMATEX wfx;
|
WAVEFORMATEX wfx;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
DSoundConf *conf = &s->conf;
|
|
||||||
#ifdef DSBTYPE_IN
|
#ifdef DSBTYPE_IN
|
||||||
const char *typ = "ADC";
|
const char *typ = "ADC";
|
||||||
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
|
||||||
DSCBUFFERDESC bd;
|
DSCBUFFERDESC bd;
|
||||||
DSCBCAPS bc;
|
DSCBCAPS bc;
|
||||||
|
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in;
|
||||||
#else
|
#else
|
||||||
const char *typ = "DAC";
|
const char *typ = "DAC";
|
||||||
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
|
||||||
DSBUFFERDESC bd;
|
DSBUFFERDESC bd;
|
||||||
DSBCAPS bc;
|
DSBCAPS bc;
|
||||||
|
AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!s->FIELD2) {
|
if (!s->FIELD2) {
|
||||||
|
@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
memset (&bd, 0, sizeof (bd));
|
memset (&bd, 0, sizeof (bd));
|
||||||
bd.dwSize = sizeof (bd);
|
bd.dwSize = sizeof (bd);
|
||||||
bd.lpwfxFormat = &wfx;
|
bd.lpwfxFormat = &wfx;
|
||||||
|
bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880);
|
||||||
#ifdef DSBTYPE_IN
|
#ifdef DSBTYPE_IN
|
||||||
bd.dwBufferBytes = conf->bufsize_in;
|
|
||||||
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
hr = IDirectSoundCapture_CreateCaptureBuffer (
|
||||||
s->dsound_capture,
|
s->dsound_capture,
|
||||||
&bd,
|
&bd,
|
||||||
|
@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
);
|
);
|
||||||
#else
|
#else
|
||||||
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
|
||||||
bd.dwBufferBytes = conf->bufsize_out;
|
|
||||||
hr = IDirectSound_CreateSoundBuffer (
|
hr = IDirectSound_CreateSoundBuffer (
|
||||||
s->dsound,
|
s->dsound,
|
||||||
&bd,
|
&bd,
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#define AUDIO_CAP "dsound"
|
#define AUDIO_CAP "dsound"
|
||||||
#include "audio_int.h"
|
#include "audio_int.h"
|
||||||
|
#include "qemu/host-utils.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mmsystem.h>
|
#include <mmsystem.h>
|
||||||
|
@ -42,17 +43,11 @@
|
||||||
|
|
||||||
/* #define DEBUG_DSOUND */
|
/* #define DEBUG_DSOUND */
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
int bufsize_in;
|
|
||||||
int bufsize_out;
|
|
||||||
int latency_millis;
|
|
||||||
} DSoundConf;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
LPDIRECTSOUND dsound;
|
LPDIRECTSOUND dsound;
|
||||||
LPDIRECTSOUNDCAPTURE dsound_capture;
|
LPDIRECTSOUNDCAPTURE dsound_capture;
|
||||||
struct audsettings settings;
|
struct audsettings settings;
|
||||||
DSoundConf conf;
|
Audiodev *dev;
|
||||||
} dsound;
|
} dsound;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -248,9 +243,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
|
||||||
dsound_log_hresult (hr);
|
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
|
#ifdef DEBUG_DSOUND
|
||||||
|
@ -478,7 +473,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
|
||||||
LPVOID p1, p2;
|
LPVOID p1, p2;
|
||||||
int bufsize;
|
int bufsize;
|
||||||
dsound *s = ds->s;
|
dsound *s = ds->s;
|
||||||
DSoundConf *conf = &s->conf;
|
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
|
||||||
|
|
||||||
if (!dsb) {
|
if (!dsb) {
|
||||||
dolog ("Attempt to run empty with playback buffer\n");
|
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;
|
len = live << hwshift;
|
||||||
|
|
||||||
if (ds->first_time) {
|
if (ds->first_time) {
|
||||||
if (conf->latency_millis) {
|
if (dso->latency) {
|
||||||
DWORD cur_blat;
|
DWORD cur_blat;
|
||||||
|
|
||||||
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
|
||||||
ds->first_time = 0;
|
ds->first_time = 0;
|
||||||
old_pos = wpos;
|
old_pos = wpos;
|
||||||
old_pos +=
|
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 %= bufsize;
|
||||||
old_pos &= ~hw->info.align;
|
old_pos &= ~hw->info.align;
|
||||||
}
|
}
|
||||||
|
@ -747,12 +742,6 @@ static int dsound_run_in (HWVoiceIn *hw)
|
||||||
return decr;
|
return decr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DSoundConf glob_conf = {
|
|
||||||
.bufsize_in = 16384,
|
|
||||||
.bufsize_out = 16384,
|
|
||||||
.latency_millis = 10
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dsound_audio_fini (void *opaque)
|
static void dsound_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -783,13 +772,22 @@ static void dsound_audio_fini (void *opaque)
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *dsound_audio_init (void)
|
static void *dsound_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
dsound *s = g_malloc0(sizeof(dsound));
|
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);
|
hr = CoInitialize (NULL);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not initialize COM\n");
|
dsound_logerr (hr, "Could not initialize COM\n");
|
||||||
|
@ -854,28 +852,6 @@ static void *dsound_audio_init (void)
|
||||||
return s;
|
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 = {
|
static struct audio_pcm_ops dsound_pcm_ops = {
|
||||||
.init_out = dsound_init_out,
|
.init_out = dsound_init_out,
|
||||||
.fini_out = dsound_fini_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 = {
|
static struct audio_driver dsound_audio_driver = {
|
||||||
.name = "dsound",
|
.name = "dsound",
|
||||||
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
|
.descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
|
||||||
.options = dsound_options,
|
|
||||||
.init = dsound_audio_init,
|
.init = dsound_audio_init,
|
||||||
.fini = dsound_audio_fini,
|
.fini = dsound_audio_fini,
|
||||||
.pcm_ops = &dsound_pcm_ops,
|
.pcm_ops = &dsound_pcm_ops,
|
||||||
|
|
|
@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *no_audio_init (void)
|
static void *no_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
return &no_audio_init;
|
return &no_audio_init;
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,6 @@ static struct audio_pcm_ops no_pcm_ops = {
|
||||||
static struct audio_driver no_audio_driver = {
|
static struct audio_driver no_audio_driver = {
|
||||||
.name = "none",
|
.name = "none",
|
||||||
.descr = "Timer based audio emulation",
|
.descr = "Timer based audio emulation",
|
||||||
.options = NULL,
|
|
||||||
.init = no_audio_init,
|
.init = no_audio_init,
|
||||||
.fini = no_audio_fini,
|
.fini = no_audio_fini,
|
||||||
.pcm_ops = &no_pcm_ops,
|
.pcm_ops = &no_pcm_ops,
|
||||||
|
|
193
audio/ossaudio.c
193
audio/ossaudio.c
|
@ -37,16 +37,6 @@
|
||||||
#define USE_DSP_POLICY
|
#define USE_DSP_POLICY
|
||||||
#endif
|
#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 {
|
typedef struct OSSVoiceOut {
|
||||||
HWVoiceOut hw;
|
HWVoiceOut hw;
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
|
@ -56,7 +46,7 @@ typedef struct OSSVoiceOut {
|
||||||
int fragsize;
|
int fragsize;
|
||||||
int mmapped;
|
int mmapped;
|
||||||
int pending;
|
int pending;
|
||||||
OSSConf *conf;
|
Audiodev *dev;
|
||||||
} OSSVoiceOut;
|
} OSSVoiceOut;
|
||||||
|
|
||||||
typedef struct OSSVoiceIn {
|
typedef struct OSSVoiceIn {
|
||||||
|
@ -65,12 +55,12 @@ typedef struct OSSVoiceIn {
|
||||||
int fd;
|
int fd;
|
||||||
int nfrags;
|
int nfrags;
|
||||||
int fragsize;
|
int fragsize;
|
||||||
OSSConf *conf;
|
Audiodev *dev;
|
||||||
} OSSVoiceIn;
|
} OSSVoiceIn;
|
||||||
|
|
||||||
struct oss_params {
|
struct oss_params {
|
||||||
int freq;
|
int freq;
|
||||||
audfmt_e fmt;
|
int fmt;
|
||||||
int nchannels;
|
int nchannels;
|
||||||
int nfrags;
|
int nfrags;
|
||||||
int fragsize;
|
int fragsize;
|
||||||
|
@ -148,16 +138,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
|
||||||
return audio_pcm_sw_write (sw, buf, 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) {
|
switch (fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
return AFMT_S8;
|
return AFMT_S8;
|
||||||
|
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
return AFMT_U8;
|
return AFMT_U8;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return AFMT_S16_BE;
|
return AFMT_S16_BE;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +155,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
|
||||||
return AFMT_S16_LE;
|
return AFMT_S16_LE;
|
||||||
}
|
}
|
||||||
|
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
if (endianness) {
|
if (endianness) {
|
||||||
return AFMT_U16_BE;
|
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) {
|
switch (ossfmt) {
|
||||||
case AFMT_S8:
|
case AFMT_S8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S8;
|
*fmt = AUDIO_FORMAT_S8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_U8:
|
case AFMT_U8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U8;
|
*fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_S16_LE:
|
case AFMT_S16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_U16_LE:
|
case AFMT_U16_LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_S16_BE:
|
case AFMT_S16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AFMT_U16_BE:
|
case AFMT_U16_BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -262,19 +252,25 @@ static int oss_get_version (int fd, int *version, const char *typ)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int oss_open (int in, struct oss_params *req,
|
static int oss_open(int in, struct oss_params *req, audsettings *as,
|
||||||
struct oss_params *obt, int *pfd, OSSConf* conf)
|
struct oss_params *obt, int *pfd, Audiodev *dev)
|
||||||
{
|
{
|
||||||
|
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||||
|
AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out;
|
||||||
int fd;
|
int fd;
|
||||||
int oflags = conf->exclusive ? O_EXCL : 0;
|
int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0;
|
||||||
audio_buf_info abinfo;
|
audio_buf_info abinfo;
|
||||||
int fmt, freq, nchannels;
|
int fmt, freq, nchannels;
|
||||||
int setfragment = 1;
|
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";
|
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 */
|
/* 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);
|
fd = open (dspname, oflags | O_NONBLOCK);
|
||||||
if (-1 == fd) {
|
if (-1 == fd) {
|
||||||
|
@ -285,6 +281,9 @@ static int oss_open (int in, struct oss_params *req,
|
||||||
freq = req->freq;
|
freq = req->freq;
|
||||||
nchannels = req->nchannels;
|
nchannels = req->nchannels;
|
||||||
fmt = req->fmt;
|
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)) {
|
if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
|
||||||
oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->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
|
#ifdef USE_DSP_POLICY
|
||||||
if (conf->policy >= 0) {
|
if (policy >= 0) {
|
||||||
int version;
|
int version;
|
||||||
|
|
||||||
if (!oss_get_version (fd, &version, typ)) {
|
if (!oss_get_version (fd, &version, typ)) {
|
||||||
trace_oss_version(version);
|
trace_oss_version(version);
|
||||||
|
|
||||||
if (version >= 0x040000) {
|
if (version >= 0x040000) {
|
||||||
int policy = conf->policy;
|
int policy2 = policy;
|
||||||
if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
|
if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) {
|
||||||
oss_logerr2 (errno, typ,
|
oss_logerr2 (errno, typ,
|
||||||
"Failed to set timing policy to %d\n",
|
"Failed to set timing policy to %d\n",
|
||||||
conf->policy);
|
policy);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
setfragment = 0;
|
setfragment = 0;
|
||||||
|
@ -500,19 +499,18 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
int endianness;
|
int endianness;
|
||||||
int err;
|
int err;
|
||||||
int fd;
|
int fd;
|
||||||
audfmt_e effective_fmt;
|
AudioFormat effective_fmt;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
OSSConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
AudiodevOssOptions *oopts = &dev->u.oss;
|
||||||
|
|
||||||
oss->fd = -1;
|
oss->fd = -1;
|
||||||
|
|
||||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
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;
|
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;
|
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
|
||||||
|
|
||||||
oss->mmapped = 0;
|
oss->mmapped = 0;
|
||||||
if (conf->try_mmap) {
|
if (oopts->has_try_mmap && oopts->try_mmap) {
|
||||||
oss->pcm_buf = mmap (
|
oss->pcm_buf = mmap (
|
||||||
NULL,
|
NULL,
|
||||||
hw->samples << hw->info.shift,
|
hw->samples << hw->info.shift,
|
||||||
|
@ -597,7 +595,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
}
|
}
|
||||||
|
|
||||||
oss->fd = fd;
|
oss->fd = fd;
|
||||||
oss->conf = conf;
|
oss->dev = dev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,16 +603,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
int trig;
|
int trig;
|
||||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||||
|
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = opdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
ldebug ("enabling voice\n");
|
ldebug ("enabling voice\n");
|
||||||
if (poll_mode) {
|
if (poll_mode) {
|
||||||
|
@ -667,18 +661,16 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
int endianness;
|
int endianness;
|
||||||
int err;
|
int err;
|
||||||
int fd;
|
int fd;
|
||||||
audfmt_e effective_fmt;
|
AudioFormat effective_fmt;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
OSSConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
|
|
||||||
oss->fd = -1;
|
oss->fd = -1;
|
||||||
|
|
||||||
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.nchannels = as->nchannels;
|
req.nchannels = as->nchannels;
|
||||||
req.fragsize = conf->fragsize;
|
if (oss_open(1, &req, as, &obt, &fd, dev)) {
|
||||||
req.nfrags = conf->nfrags;
|
|
||||||
if (oss_open (1, &req, &obt, &fd, conf)) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,7 +704,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
oss->fd = fd;
|
oss->fd = fd;
|
||||||
oss->conf = conf;
|
oss->dev = dev;
|
||||||
return 0;
|
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, ...)
|
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
{
|
{
|
||||||
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
|
||||||
|
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case VOICE_ENABLE:
|
case VOICE_ENABLE:
|
||||||
{
|
{
|
||||||
va_list ap;
|
bool poll_mode = opdo->try_poll;
|
||||||
int poll_mode;
|
|
||||||
|
|
||||||
va_start (ap, cmd);
|
|
||||||
poll_mode = va_arg (ap, int);
|
|
||||||
va_end (ap);
|
|
||||||
|
|
||||||
if (poll_mode) {
|
if (poll_mode) {
|
||||||
oss_poll_in (hw);
|
oss_poll_in (hw);
|
||||||
|
@ -832,82 +820,36 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static OSSConf glob_conf = {
|
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
|
||||||
.try_mmap = 0,
|
|
||||||
.nfrags = 4,
|
|
||||||
.fragsize = 4096,
|
|
||||||
.devpath_out = "/dev/dsp",
|
|
||||||
.devpath_in = "/dev/dsp",
|
|
||||||
.exclusive = 0,
|
|
||||||
.policy = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *oss_audio_init (void)
|
|
||||||
{
|
{
|
||||||
OSSConf *conf = g_malloc(sizeof(OSSConf));
|
if (!opdo->has_try_poll) {
|
||||||
*conf = glob_conf;
|
opdo->try_poll = true;
|
||||||
|
opdo->has_try_poll = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (access(conf->devpath_in, R_OK | W_OK) < 0 ||
|
static void *oss_audio_init(Audiodev *dev)
|
||||||
access(conf->devpath_out, R_OK | W_OK) < 0) {
|
{
|
||||||
g_free(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(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 NULL;
|
||||||
}
|
}
|
||||||
return conf;
|
return dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void oss_audio_fini (void *opaque)
|
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 = {
|
static struct audio_pcm_ops oss_pcm_ops = {
|
||||||
.init_out = oss_init_out,
|
.init_out = oss_init_out,
|
||||||
.fini_out = oss_fini_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 = {
|
static struct audio_driver oss_audio_driver = {
|
||||||
.name = "oss",
|
.name = "oss",
|
||||||
.descr = "OSS http://www.opensound.com",
|
.descr = "OSS http://www.opensound.com",
|
||||||
.options = oss_options,
|
|
||||||
.init = oss_audio_init,
|
.init = oss_audio_init,
|
||||||
.fini = oss_audio_fini,
|
.fini = oss_audio_fini,
|
||||||
.pcm_ops = &oss_pcm_ops,
|
.pcm_ops = &oss_pcm_ops,
|
||||||
|
|
111
audio/paaudio.c
111
audio/paaudio.c
|
@ -2,6 +2,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
#include "qapi/opts-visitor.h"
|
||||||
|
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
|
@ -10,14 +11,7 @@
|
||||||
#include "audio_pt_int.h"
|
#include "audio_pt_int.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int samples;
|
Audiodev *dev;
|
||||||
char *server;
|
|
||||||
char *sink;
|
|
||||||
char *source;
|
|
||||||
} PAConf;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PAConf conf;
|
|
||||||
pa_threaded_mainloop *mainloop;
|
pa_threaded_mainloop *mainloop;
|
||||||
pa_context *context;
|
pa_context *context;
|
||||||
} paaudio;
|
} paaudio;
|
||||||
|
@ -32,6 +26,7 @@ typedef struct {
|
||||||
void *pcm_buf;
|
void *pcm_buf;
|
||||||
struct audio_pt pt;
|
struct audio_pt pt;
|
||||||
paaudio *g;
|
paaudio *g;
|
||||||
|
int samples;
|
||||||
} PAVoiceOut;
|
} PAVoiceOut;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -46,6 +41,7 @@ typedef struct {
|
||||||
const void *read_data;
|
const void *read_data;
|
||||||
size_t read_index, read_length;
|
size_t read_index, read_length;
|
||||||
paaudio *g;
|
paaudio *g;
|
||||||
|
int samples;
|
||||||
} PAVoiceIn;
|
} PAVoiceIn;
|
||||||
|
|
||||||
static void qpa_audio_fini(void *opaque);
|
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;
|
rpos = pa->rpos;
|
||||||
|
|
||||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
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;
|
wpos = pa->wpos;
|
||||||
|
|
||||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
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);
|
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;
|
int format;
|
||||||
|
|
||||||
switch (afmt) {
|
switch (afmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
format = PA_SAMPLE_U8;
|
format = PA_SAMPLE_U8;
|
||||||
break;
|
break;
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
|
||||||
break;
|
break;
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -410,26 +406,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
|
||||||
return format;
|
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) {
|
switch (fmt) {
|
||||||
case PA_SAMPLE_U8:
|
case PA_SAMPLE_U8:
|
||||||
return AUD_FMT_U8;
|
return AUDIO_FORMAT_U8;
|
||||||
case PA_SAMPLE_S16BE:
|
case PA_SAMPLE_S16BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
return AUD_FMT_S16;
|
return AUDIO_FORMAT_S16;
|
||||||
case PA_SAMPLE_S16LE:
|
case PA_SAMPLE_S16LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
return AUD_FMT_S16;
|
return AUDIO_FORMAT_S16;
|
||||||
case PA_SAMPLE_S32BE:
|
case PA_SAMPLE_S32BE:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
return AUD_FMT_S32;
|
return AUDIO_FORMAT_S32;
|
||||||
case PA_SAMPLE_S32LE:
|
case PA_SAMPLE_S32LE:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
return AUD_FMT_S32;
|
return AUDIO_FORMAT_S32;
|
||||||
default:
|
default:
|
||||||
dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
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;
|
struct audsettings obt_as = *as;
|
||||||
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
PAVoiceOut *pa = (PAVoiceOut *) hw;
|
||||||
paaudio *g = pa->g = drv_opaque;
|
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.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||||
ss.channels = as->nchannels;
|
ss.channels = as->nchannels;
|
||||||
|
@ -566,7 +564,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
g,
|
g,
|
||||||
"qemu",
|
"qemu",
|
||||||
PA_STREAM_PLAYBACK,
|
PA_STREAM_PLAYBACK,
|
||||||
g->conf.sink,
|
ppdo->has_name ? ppdo->name : NULL,
|
||||||
&ss,
|
&ss,
|
||||||
NULL, /* channel map */
|
NULL, /* channel map */
|
||||||
&ba, /* buffering attributes */
|
&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);
|
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->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||||
pa->rpos = hw->rpos;
|
pa->rpos = hw->rpos;
|
||||||
if (!pa->pcm_buf) {
|
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;
|
struct audsettings obt_as = *as;
|
||||||
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
PAVoiceIn *pa = (PAVoiceIn *) hw;
|
||||||
paaudio *g = pa->g = drv_opaque;
|
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.format = audfmt_to_pa (as->fmt, as->endianness);
|
||||||
ss.channels = as->nchannels;
|
ss.channels = as->nchannels;
|
||||||
|
@ -623,7 +624,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
|
||||||
g,
|
g,
|
||||||
"qemu",
|
"qemu",
|
||||||
PA_STREAM_RECORD,
|
PA_STREAM_RECORD,
|
||||||
g->conf.source,
|
ppdo->has_name ? ppdo->name : NULL,
|
||||||
&ss,
|
&ss,
|
||||||
NULL, /* channel map */
|
NULL, /* channel map */
|
||||||
NULL, /* buffering attributes */
|
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);
|
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->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
|
||||||
pa->wpos = hw->wpos;
|
pa->wpos = hw->wpos;
|
||||||
if (!pa->pcm_buf) {
|
if (!pa->pcm_buf) {
|
||||||
|
@ -808,13 +810,13 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* common */
|
/* common */
|
||||||
static PAConf glob_conf = {
|
static void *qpa_audio_init(Audiodev *dev)
|
||||||
.samples = 4096,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *qpa_audio_init (void)
|
|
||||||
{
|
{
|
||||||
if (glob_conf.server == NULL) {
|
paaudio *g;
|
||||||
|
AudiodevPaOptions *popts = &dev->u.pa;
|
||||||
|
const char *server;
|
||||||
|
|
||||||
|
if (!popts->has_server) {
|
||||||
char pidfile[64];
|
char pidfile[64];
|
||||||
char *runtime;
|
char *runtime;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -829,8 +831,12 @@ static void *qpa_audio_init (void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paaudio *g = g_malloc(sizeof(paaudio));
|
assert(dev->driver == AUDIODEV_DRIVER_PA);
|
||||||
g->conf = glob_conf;
|
|
||||||
|
g = g_malloc(sizeof(paaudio));
|
||||||
|
server = popts->has_server ? popts->server : NULL;
|
||||||
|
|
||||||
|
g->dev = dev;
|
||||||
g->mainloop = NULL;
|
g->mainloop = NULL;
|
||||||
g->context = 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->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
|
||||||
g->conf.server);
|
server);
|
||||||
if (!g->context) {
|
if (!g->context) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_context_set_state_callback (g->context, context_state_cb, g);
|
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),
|
qpa_logerr (pa_context_errno (g->context),
|
||||||
"pa_context_connect() failed\n");
|
"pa_context_connect() failed\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
@ -910,34 +916,6 @@ static void qpa_audio_fini (void *opaque)
|
||||||
g_free(g);
|
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 = {
|
static struct audio_pcm_ops qpa_pcm_ops = {
|
||||||
.init_out = qpa_init_out,
|
.init_out = qpa_init_out,
|
||||||
.fini_out = qpa_fini_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 = {
|
static struct audio_driver pa_audio_driver = {
|
||||||
.name = "pa",
|
.name = "pa",
|
||||||
.descr = "http://www.pulseaudio.org/",
|
.descr = "http://www.pulseaudio.org/",
|
||||||
.options = qpa_options,
|
|
||||||
.init = qpa_audio_init,
|
.init = qpa_audio_init,
|
||||||
.fini = qpa_audio_fini,
|
.fini = qpa_audio_fini,
|
||||||
.pcm_ops = &qpa_pcm_ops,
|
.pcm_ops = &qpa_pcm_ops,
|
||||||
|
|
|
@ -44,16 +44,11 @@ typedef struct SDLVoiceOut {
|
||||||
int decr;
|
int decr;
|
||||||
} SDLVoiceOut;
|
} SDLVoiceOut;
|
||||||
|
|
||||||
static struct {
|
|
||||||
int nb_samples;
|
|
||||||
} conf = {
|
|
||||||
.nb_samples = 1024
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct SDLAudioState {
|
static struct SDLAudioState {
|
||||||
int exit;
|
int exit;
|
||||||
int initialized;
|
int initialized;
|
||||||
bool driver_created;
|
bool driver_created;
|
||||||
|
Audiodev *dev;
|
||||||
} glob_sdl;
|
} glob_sdl;
|
||||||
typedef struct SDLAudioState SDLAudioState;
|
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 ());
|
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) {
|
switch (fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
return AUDIO_S8;
|
return AUDIO_S8;
|
||||||
|
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
return AUDIO_U8;
|
return AUDIO_U8;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
return AUDIO_S16LSB;
|
return AUDIO_S16LSB;
|
||||||
|
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
return AUDIO_U16LSB;
|
return AUDIO_U16LSB;
|
||||||
|
|
||||||
default:
|
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) {
|
switch (sdlfmt) {
|
||||||
case AUDIO_S8:
|
case AUDIO_S8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S8;
|
*fmt = AUDIO_FORMAT_S8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_U8:
|
case AUDIO_U8:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U8;
|
*fmt = AUDIO_FORMAT_U8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_S16LSB:
|
case AUDIO_S16LSB:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_U16LSB:
|
case AUDIO_U16LSB:
|
||||||
*endianness = 0;
|
*endianness = 0;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_S16MSB:
|
case AUDIO_S16MSB:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_S16;
|
*fmt = AUDIO_FORMAT_S16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUDIO_U16MSB:
|
case AUDIO_U16MSB:
|
||||||
*endianness = 1;
|
*endianness = 1;
|
||||||
*fmt = AUD_FMT_U16;
|
*fmt = AUDIO_FORMAT_U16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -265,13 +260,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
|
||||||
SDL_AudioSpec req, obt;
|
SDL_AudioSpec req, obt;
|
||||||
int endianness;
|
int endianness;
|
||||||
int err;
|
int err;
|
||||||
audfmt_e effective_fmt;
|
AudioFormat effective_fmt;
|
||||||
struct audsettings obt_as;
|
struct audsettings obt_as;
|
||||||
|
|
||||||
req.freq = as->freq;
|
req.freq = as->freq;
|
||||||
req.format = aud_to_sdlfmt (as->fmt);
|
req.format = aud_to_sdlfmt (as->fmt);
|
||||||
req.channels = as->nchannels;
|
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.callback = sdl_callback;
|
||||||
req.userdata = sdl;
|
req.userdata = sdl;
|
||||||
|
|
||||||
|
@ -315,7 +310,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *sdl_audio_init (void)
|
static void *sdl_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
SDLAudioState *s = &glob_sdl;
|
SDLAudioState *s = &glob_sdl;
|
||||||
if (s->driver_created) {
|
if (s->driver_created) {
|
||||||
|
@ -329,6 +324,7 @@ static void *sdl_audio_init (void)
|
||||||
}
|
}
|
||||||
|
|
||||||
s->driver_created = true;
|
s->driver_created = true;
|
||||||
|
s->dev = dev;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,18 +334,9 @@ static void sdl_audio_fini (void *opaque)
|
||||||
sdl_close (s);
|
sdl_close (s);
|
||||||
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
||||||
s->driver_created = false;
|
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 = {
|
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||||
.init_out = sdl_init_out,
|
.init_out = sdl_init_out,
|
||||||
.fini_out = sdl_fini_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 = {
|
static struct audio_driver sdl_audio_driver = {
|
||||||
.name = "sdl",
|
.name = "sdl",
|
||||||
.descr = "SDL http://www.libsdl.org",
|
.descr = "SDL http://www.libsdl.org",
|
||||||
.options = sdl_options,
|
|
||||||
.init = sdl_audio_init,
|
.init = sdl_audio_init,
|
||||||
.fini = sdl_audio_fini,
|
.fini = sdl_audio_fini,
|
||||||
.pcm_ops = &sdl_pcm_ops,
|
.pcm_ops = &sdl_pcm_ops,
|
||||||
|
|
|
@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = {
|
||||||
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *spice_audio_init (void)
|
static void *spice_audio_init(Audiodev *dev)
|
||||||
{
|
{
|
||||||
if (!using_spice) {
|
if (!using_spice) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
|
||||||
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
|
||||||
#endif
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
|
||||||
settings.fmt = AUD_FMT_S16;
|
settings.fmt = AUDIO_FORMAT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &settings);
|
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;
|
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
|
||||||
#endif
|
#endif
|
||||||
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
|
||||||
settings.fmt = AUD_FMT_S16;
|
settings.fmt = AUDIO_FORMAT_S16;
|
||||||
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
settings.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
|
|
||||||
audio_pcm_init_info (&hw->info, &settings);
|
audio_pcm_init_info (&hw->info, &settings);
|
||||||
|
@ -373,10 +373,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct audio_option audio_options[] = {
|
|
||||||
{ /* end of list */ },
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct audio_pcm_ops audio_callbacks = {
|
static struct audio_pcm_ops audio_callbacks = {
|
||||||
.init_out = line_out_init,
|
.init_out = line_out_init,
|
||||||
.fini_out = line_out_fini,
|
.fini_out = line_out_fini,
|
||||||
|
@ -394,7 +390,6 @@ static struct audio_pcm_ops audio_callbacks = {
|
||||||
static struct audio_driver spice_audio_driver = {
|
static struct audio_driver spice_audio_driver = {
|
||||||
.name = "spice",
|
.name = "spice",
|
||||||
.descr = "spice audio driver",
|
.descr = "spice audio driver",
|
||||||
.options = audio_options,
|
|
||||||
.init = spice_audio_init,
|
.init = spice_audio_init,
|
||||||
.fini = spice_audio_fini,
|
.fini = spice_audio_fini,
|
||||||
.pcm_ops = &audio_callbacks,
|
.pcm_ops = &audio_callbacks,
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/host-utils.h"
|
#include "qemu/host-utils.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
#include "qapi/opts-visitor.h"
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
#define AUDIO_CAP "wav"
|
#define AUDIO_CAP "wav"
|
||||||
|
@ -37,11 +38,6 @@ typedef struct WAVVoiceOut {
|
||||||
int total_samples;
|
int total_samples;
|
||||||
} WAVVoiceOut;
|
} WAVVoiceOut;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct audsettings settings;
|
|
||||||
const char *wav_path;
|
|
||||||
} WAVConf;
|
|
||||||
|
|
||||||
static int wav_run_out (HWVoiceOut *hw, int live)
|
static int wav_run_out (HWVoiceOut *hw, int live)
|
||||||
{
|
{
|
||||||
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
|
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,
|
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||||
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
WAVConf *conf = drv_opaque;
|
Audiodev *dev = drv_opaque;
|
||||||
struct audsettings wav_as = conf->settings;
|
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;
|
stereo = wav_as.nchannels == 2;
|
||||||
switch (wav_as.fmt) {
|
switch (wav_as.fmt) {
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
bits16 = 0;
|
bits16 = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
bits16 = 1;
|
bits16 = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
dolog ("WAVE files can not handle 32bit formats\n");
|
dolog ("WAVE files can not handle 32bit formats\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
hdr[34] = bits16 ? 0x10 : 0x08;
|
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 + 28, hw->info.freq << (bits16 + stereo), 4);
|
||||||
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
|
||||||
|
|
||||||
wav->f = fopen (conf->wav_path, "wb");
|
wav->f = fopen(wav_path, "wb");
|
||||||
if (!wav->f) {
|
if (!wav->f) {
|
||||||
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
dolog ("Failed to open wave file `%s'\nReason: %s\n",
|
||||||
conf->wav_path, strerror (errno));
|
wav_path, strerror(errno));
|
||||||
g_free (wav->pcm_buf);
|
g_free (wav->pcm_buf);
|
||||||
wav->pcm_buf = NULL;
|
wav->pcm_buf = NULL;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -222,54 +223,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WAVConf glob_conf = {
|
static void *wav_audio_init(Audiodev *dev)
|
||||||
.settings.freq = 44100,
|
|
||||||
.settings.nchannels = 2,
|
|
||||||
.settings.fmt = AUD_FMT_S16,
|
|
||||||
.wav_path = "qemu.wav"
|
|
||||||
};
|
|
||||||
|
|
||||||
static void *wav_audio_init (void)
|
|
||||||
{
|
{
|
||||||
WAVConf *conf = g_malloc(sizeof(WAVConf));
|
assert(dev->driver == AUDIODEV_DRIVER_WAV);
|
||||||
*conf = glob_conf;
|
return dev;
|
||||||
return conf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wav_audio_fini (void *opaque)
|
static void wav_audio_fini (void *opaque)
|
||||||
{
|
{
|
||||||
ldebug ("wav_fini");
|
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 = {
|
static struct audio_pcm_ops wav_pcm_ops = {
|
||||||
.init_out = wav_init_out,
|
.init_out = wav_init_out,
|
||||||
.fini_out = wav_fini_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 = {
|
static struct audio_driver wav_audio_driver = {
|
||||||
.name = "wav",
|
.name = "wav",
|
||||||
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
|
.descr = "WAV renderer http://wikipedia.org/wiki/WAV",
|
||||||
.options = wav_options,
|
|
||||||
.init = wav_audio_init,
|
.init = wav_audio_init,
|
||||||
.fini = wav_audio_fini,
|
.fini = wav_audio_fini,
|
||||||
.pcm_ops = &wav_pcm_ops,
|
.pcm_ops = &wav_pcm_ops,
|
||||||
|
|
|
@ -136,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
|
||||||
|
|
||||||
as.freq = freq;
|
as.freq = freq;
|
||||||
as.nchannels = 1 << stereo;
|
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;
|
as.endianness = 0;
|
||||||
|
|
||||||
ops.notify = wav_notify;
|
ops.notify = wav_notify;
|
||||||
|
|
|
@ -273,7 +273,7 @@ static void omap_eac_format_update(struct omap_eac_s *s)
|
||||||
* does I2S specify it? */
|
* does I2S specify it? */
|
||||||
/* All register writes are 16 bits so we we store 16-bit samples
|
/* All register writes are 16 bits so we we store 16-bit samples
|
||||||
* in the buffers regardless of AGCFR[B8_16] value. */
|
* 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,
|
s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice,
|
||||||
"eac.codec.in", s, omap_eac_in_cb, &fmt);
|
"eac.codec.in", s, omap_eac_in_cb, &fmt);
|
||||||
|
|
|
@ -365,7 +365,7 @@ static void open_voice (AC97LinkState *s, int index, int freq)
|
||||||
|
|
||||||
as.freq = freq;
|
as.freq = freq;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
if (freq > 0) {
|
if (freq > 0) {
|
||||||
|
|
|
@ -269,7 +269,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
as.freq = s->freq;
|
as.freq = s->freq;
|
||||||
as.nchannels = SHIFT;
|
as.nchannels = SHIFT;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = AUDIO_HOST_ENDIANNESS;
|
as.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
|
|
||||||
AUD_register_card ("adlib", &s->card);
|
AUD_register_card ("adlib", &s->card);
|
||||||
|
|
|
@ -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)) {
|
switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) {
|
||||||
case 0:
|
case 0:
|
||||||
as.fmt = AUD_FMT_U8;
|
as.fmt = AUDIO_FORMAT_U8;
|
||||||
s->shift = as.nchannels == 2;
|
s->shift = as.nchannels == 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -298,7 +298,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
|
||||||
case 3:
|
case 3:
|
||||||
s->tab = ALawDecompressTable;
|
s->tab = ALawDecompressTable;
|
||||||
x_law:
|
x_law:
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = AUDIO_HOST_ENDIANNESS;
|
as.endianness = AUDIO_HOST_ENDIANNESS;
|
||||||
s->shift = as.nchannels == 2;
|
s->shift = as.nchannels == 2;
|
||||||
break;
|
break;
|
||||||
|
@ -307,7 +307,7 @@ static void cs_reset_voices (CSState *s, uint32_t val)
|
||||||
as.endianness = 1;
|
as.endianness = 1;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case 2:
|
case 2:
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
s->shift = as.nchannels;
|
s->shift = as.nchannels;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -414,14 +414,14 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl)
|
||||||
i,
|
i,
|
||||||
new_freq,
|
new_freq,
|
||||||
1 << (new_fmt & 1),
|
1 << (new_fmt & 1),
|
||||||
(new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
|
(new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8,
|
||||||
d->shift);
|
d->shift);
|
||||||
if (new_freq) {
|
if (new_freq) {
|
||||||
struct audsettings as;
|
struct audsettings as;
|
||||||
|
|
||||||
as.freq = new_freq;
|
as.freq = new_freq;
|
||||||
as.nchannels = 1 << (new_fmt & 1);
|
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;
|
as.endianness = 0;
|
||||||
|
|
||||||
if (i == ADC_CHANNEL) {
|
if (i == ADC_CHANNEL) {
|
||||||
|
|
|
@ -251,7 +251,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
as.freq = s->freq;
|
as.freq = s->freq;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = GUS_ENDIANNESS;
|
as.endianness = GUS_ENDIANNESS;
|
||||||
|
|
||||||
s->voice = AUD_open_out (
|
s->voice = AUD_open_out (
|
||||||
|
|
|
@ -99,9 +99,9 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (format & AC_FMT_BITS_MASK) {
|
switch (format & AC_FMT_BITS_MASK) {
|
||||||
case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break;
|
case AC_FMT_BITS_8: as->fmt = AUDIO_FORMAT_S8; break;
|
||||||
case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break;
|
case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break;
|
||||||
case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break;
|
case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1;
|
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[] = {
|
static const char *fmt2name[] = {
|
||||||
[ AUD_FMT_U8 ] = "PCM-U8",
|
[ AUDIO_FORMAT_U8 ] = "PCM-U8",
|
||||||
[ AUD_FMT_S8 ] = "PCM-S8",
|
[ AUDIO_FORMAT_S8 ] = "PCM-S8",
|
||||||
[ AUD_FMT_U16 ] = "PCM-U16",
|
[ AUDIO_FORMAT_U16 ] = "PCM-U16",
|
||||||
[ AUD_FMT_S16 ] = "PCM-S16",
|
[ AUDIO_FORMAT_S16 ] = "PCM-S16",
|
||||||
[ AUD_FMT_U32 ] = "PCM-U32",
|
[ AUDIO_FORMAT_U32 ] = "PCM-U32",
|
||||||
[ AUD_FMT_S32 ] = "PCM-S32",
|
[ AUDIO_FORMAT_S32 ] = "PCM-S32",
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct HDAAudioState HDAAudioState;
|
typedef struct HDAAudioState HDAAudioState;
|
||||||
|
|
|
@ -185,7 +185,7 @@ void lm4549_write(lm4549_state *s,
|
||||||
struct audsettings as;
|
struct audsettings as;
|
||||||
as.freq = value;
|
as.freq = value;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
s->voice = AUD_open_out(
|
s->voice = AUD_open_out(
|
||||||
|
@ -255,7 +255,7 @@ static int lm4549_post_load(void *opaque, int version_id)
|
||||||
struct audsettings as;
|
struct audsettings as;
|
||||||
as.freq = freq;
|
as.freq = freq;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
s->voice = AUD_open_out(
|
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 */
|
/* Open a default voice */
|
||||||
as.freq = 48000;
|
as.freq = 48000;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
s->voice = AUD_open_out(
|
s->voice = AUD_open_out(
|
||||||
|
|
|
@ -308,7 +308,7 @@ static void milkymist_ac97_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
as.freq = 48000;
|
as.freq = 48000;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = 1;
|
as.endianness = 1;
|
||||||
|
|
||||||
s->voice_in = AUD_open_in(&s->card, s->voice_in,
|
s->voice_in = AUD_open_in(&s->card, s->voice_in,
|
||||||
|
|
|
@ -162,7 +162,7 @@ static void pcspk_initfn(Object *obj)
|
||||||
|
|
||||||
static void pcspk_realizefn(DeviceState *dev, Error **errp)
|
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);
|
ISADevice *isadev = ISA_DEVICE(dev);
|
||||||
PCSpkState *s = PC_SPEAKER(dev);
|
PCSpkState *s = PC_SPEAKER(dev);
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ typedef struct SB16State {
|
||||||
int fmt_stereo;
|
int fmt_stereo;
|
||||||
int fmt_signed;
|
int fmt_signed;
|
||||||
int fmt_bits;
|
int fmt_bits;
|
||||||
audfmt_e fmt;
|
AudioFormat fmt;
|
||||||
int dma_auto;
|
int dma_auto;
|
||||||
int block_size;
|
int block_size;
|
||||||
int fifo;
|
int fifo;
|
||||||
|
@ -224,7 +224,7 @@ static void continue_dma8 (SB16State *s)
|
||||||
|
|
||||||
static void dma_cmd8 (SB16State *s, int mask, int dma_len)
|
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->use_hdma = 0;
|
||||||
s->fmt_bits = 8;
|
s->fmt_bits = 8;
|
||||||
s->fmt_signed = 0;
|
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 (16 == s->fmt_bits) {
|
||||||
if (s->fmt_signed) {
|
if (s->fmt_signed) {
|
||||||
s->fmt = AUD_FMT_S16;
|
s->fmt = AUDIO_FORMAT_S16;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
s->fmt = AUD_FMT_U16;
|
s->fmt = AUDIO_FORMAT_U16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (s->fmt_signed) {
|
if (s->fmt_signed) {
|
||||||
s->fmt = AUD_FMT_S8;
|
s->fmt = AUDIO_FORMAT_S8;
|
||||||
}
|
}
|
||||||
else {
|
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.freq = s->freq;
|
||||||
as.nchannels = 1;
|
as.nchannels = 1;
|
||||||
as.fmt = AUD_FMT_U8;
|
as.fmt = AUDIO_FORMAT_U8;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
s->voice = AUD_open_out (
|
s->voice = AUD_open_out (
|
||||||
|
|
|
@ -201,7 +201,7 @@ static void wm8750_set_format(WM8750State *s)
|
||||||
in_fmt.endianness = 0;
|
in_fmt.endianness = 0;
|
||||||
in_fmt.nchannels = 2;
|
in_fmt.nchannels = 2;
|
||||||
in_fmt.freq = s->adc_hz;
|
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],
|
s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0],
|
||||||
CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt);
|
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.endianness = 0;
|
||||||
out_fmt.nchannels = 2;
|
out_fmt.nchannels = 2;
|
||||||
out_fmt.freq = s->dac_hz;
|
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],
|
s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
|
||||||
CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt);
|
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)) {
|
if (s->idx_in >= sizeof(s->data_in)) {
|
||||||
wm8750_in_load(s);
|
wm8750_in_load(s);
|
||||||
if (s->idx_in >= sizeof(s->data_in)) {
|
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 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1260,7 +1260,7 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp)
|
||||||
|
|
||||||
as.freq = 44100;
|
as.freq = 44100;
|
||||||
as.nchannels = 2;
|
as.nchannels = 2;
|
||||||
as.fmt = AUD_FMT_S16;
|
as.fmt = AUDIO_FORMAT_S16;
|
||||||
as.endianness = 0;
|
as.endianness = 0;
|
||||||
|
|
||||||
AUD_register_card("xlnx_dp.audio", &s->aud_card);
|
AUD_register_card("xlnx_dp.audio", &s->aud_card);
|
||||||
|
|
|
@ -318,7 +318,7 @@ static void tsc2102_audio_output_update(TSC210xState *s)
|
||||||
fmt.endianness = 0;
|
fmt.endianness = 0;
|
||||||
fmt.nchannels = 2;
|
fmt.nchannels = 2;
|
||||||
fmt.freq = s->codec.tx_rate;
|
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],
|
s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0],
|
||||||
"tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
|
"tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt);
|
||||||
|
|
|
@ -650,7 +650,7 @@ static void usb_audio_realize(USBDevice *dev, Error **errp)
|
||||||
s->out.vol[1] = 240; /* 0 dB */
|
s->out.vol[1] = 240; /* 0 dB */
|
||||||
s->out.as.freq = USBAUDIO_SAMPLE_RATE;
|
s->out.as.freq = USBAUDIO_SAMPLE_RATE;
|
||||||
s->out.as.nchannels = 2;
|
s->out.as.nchannels = 2;
|
||||||
s->out.as.fmt = AUD_FMT_S16;
|
s->out.as.fmt = AUDIO_FORMAT_S16;
|
||||||
s->out.as.endianness = 0;
|
s->out.as.endianness = 0;
|
||||||
streambuf_init(&s->out.buf, s->buffer);
|
streambuf_init(&s->out.buf, s->buffer);
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,9 @@ util-obj-y += opts-visitor.o qapi-clone-visitor.o
|
||||||
util-obj-y += qmp-event.o
|
util-obj-y += qmp-event.o
|
||||||
util-obj-y += qapi-util.o
|
util-obj-y += qapi-util.o
|
||||||
|
|
||||||
QAPI_COMMON_MODULES = authz block-core block char common crypto introspect
|
QAPI_COMMON_MODULES = audio authz block-core block char common crypto
|
||||||
QAPI_COMMON_MODULES += job migration misc net rdma rocker run-state
|
QAPI_COMMON_MODULES += introspect job migration misc net rdma rocker
|
||||||
QAPI_COMMON_MODULES += sockets tpm trace transaction ui
|
QAPI_COMMON_MODULES += run-state sockets tpm trace transaction ui
|
||||||
QAPI_TARGET_MODULES = target
|
QAPI_TARGET_MODULES = target
|
||||||
QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES)
|
QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES)
|
||||||
|
|
||||||
|
|
|
@ -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' } }
|
|
@ -99,3 +99,4 @@
|
||||||
{ 'include': 'introspect.json' }
|
{ 'include': 'introspect.json' }
|
||||||
{ 'include': 'misc.json' }
|
{ 'include': 'misc.json' }
|
||||||
{ 'include': 'target.json' }
|
{ 'include': 'target.json' }
|
||||||
|
{ 'include': 'audio.json' }
|
||||||
|
|
|
@ -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
|
The @code{acl} option to the @code{-vnc} argument has been replaced
|
||||||
by the @code{tls-authz} and @code{sasl-authz} options.
|
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
|
@section QEMU Machine Protocol (QMP) commands
|
||||||
|
|
||||||
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
|
@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0)
|
||||||
|
|
236
qemu-options.hx
236
qemu-options.hx
|
@ -416,14 +416,244 @@ The default is @code{en-us}.
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
|
|
||||||
|
HXCOMM Deprecated by -audiodev
|
||||||
DEF("audio-help", 0, QEMU_OPTION_audio_help,
|
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)
|
QEMU_ARCH_ALL)
|
||||||
STEXI
|
STEXI
|
||||||
@item -audio-help
|
@item -audio-help
|
||||||
@findex -audio-help
|
@findex -audio-help
|
||||||
Will show the audio subsystem help: list of drivers, tunable
|
Will show the -audiodev equivalent of the currently specified
|
||||||
parameters.
|
(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
|
ETEXI
|
||||||
|
|
||||||
DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
|
DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw,
|
||||||
|
|
26
ui/vnc.c
26
ui/vnc.c
|
@ -1019,16 +1019,16 @@ static void vnc_update_throttle_offset(VncState *vs)
|
||||||
int bps;
|
int bps;
|
||||||
switch (vs->as.fmt) {
|
switch (vs->as.fmt) {
|
||||||
default:
|
default:
|
||||||
case AUD_FMT_U8:
|
case AUDIO_FORMAT_U8:
|
||||||
case AUD_FMT_S8:
|
case AUDIO_FORMAT_S8:
|
||||||
bps = 1;
|
bps = 1;
|
||||||
break;
|
break;
|
||||||
case AUD_FMT_U16:
|
case AUDIO_FORMAT_U16:
|
||||||
case AUD_FMT_S16:
|
case AUDIO_FORMAT_S16:
|
||||||
bps = 2;
|
bps = 2;
|
||||||
break;
|
break;
|
||||||
case AUD_FMT_U32:
|
case AUDIO_FORMAT_U32:
|
||||||
case AUD_FMT_S32:
|
case AUDIO_FORMAT_S32:
|
||||||
bps = 4;
|
bps = 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2375,12 +2375,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
|
||||||
if (len == 4)
|
if (len == 4)
|
||||||
return 10;
|
return 10;
|
||||||
switch (read_u8(data, 4)) {
|
switch (read_u8(data, 4)) {
|
||||||
case 0: vs->as.fmt = AUD_FMT_U8; break;
|
case 0: vs->as.fmt = AUDIO_FORMAT_U8; break;
|
||||||
case 1: vs->as.fmt = AUD_FMT_S8; break;
|
case 1: vs->as.fmt = AUDIO_FORMAT_S8; break;
|
||||||
case 2: vs->as.fmt = AUD_FMT_U16; break;
|
case 2: vs->as.fmt = AUDIO_FORMAT_U16; break;
|
||||||
case 3: vs->as.fmt = AUD_FMT_S16; break;
|
case 3: vs->as.fmt = AUDIO_FORMAT_S16; break;
|
||||||
case 4: vs->as.fmt = AUD_FMT_U32; break;
|
case 4: vs->as.fmt = AUDIO_FORMAT_U32; break;
|
||||||
case 5: vs->as.fmt = AUD_FMT_S32; break;
|
case 5: vs->as.fmt = AUDIO_FORMAT_S32; break;
|
||||||
default:
|
default:
|
||||||
VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
|
VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
|
||||||
vnc_client_error(vs);
|
vnc_client_error(vs);
|
||||||
|
@ -3111,7 +3111,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
|
||||||
|
|
||||||
vs->as.freq = 44100;
|
vs->as.freq = 44100;
|
||||||
vs->as.nchannels = 2;
|
vs->as.nchannels = 2;
|
||||||
vs->as.fmt = AUD_FMT_S16;
|
vs->as.fmt = AUDIO_FORMAT_S16;
|
||||||
vs->as.endianness = 0;
|
vs->as.endianness = 0;
|
||||||
|
|
||||||
qemu_mutex_init(&vs->output_mutex);
|
qemu_mutex_init(&vs->output_mutex);
|
||||||
|
|
7
vl.c
7
vl.c
|
@ -3285,9 +3285,12 @@ int main(int argc, char **argv, char **envp)
|
||||||
add_device_config(DEV_BT, optarg);
|
add_device_config(DEV_BT, optarg);
|
||||||
break;
|
break;
|
||||||
case QEMU_OPTION_audio_help:
|
case QEMU_OPTION_audio_help:
|
||||||
AUD_help ();
|
audio_legacy_help();
|
||||||
exit (0);
|
exit (0);
|
||||||
break;
|
break;
|
||||||
|
case QEMU_OPTION_audiodev:
|
||||||
|
audio_parse_option(optarg);
|
||||||
|
break;
|
||||||
case QEMU_OPTION_soundhw:
|
case QEMU_OPTION_soundhw:
|
||||||
select_soundhw (optarg);
|
select_soundhw (optarg);
|
||||||
break;
|
break;
|
||||||
|
@ -4454,6 +4457,8 @@ int main(int argc, char **argv, char **envp)
|
||||||
/* do monitor/qmp handling at preconfig state if requested */
|
/* do monitor/qmp handling at preconfig state if requested */
|
||||||
main_loop();
|
main_loop();
|
||||||
|
|
||||||
|
audio_init_audiodevs();
|
||||||
|
|
||||||
/* from here on runstate is RUN_STATE_PRELAUNCH */
|
/* from here on runstate is RUN_STATE_PRELAUNCH */
|
||||||
machine_run_board_init(current_machine);
|
machine_run_board_init(current_machine);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue