audio: second batch of -audiodev support, adding support for multiple backends.

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJdXQOFAAoJEEy22O7T6HE4/DIP+QGVIPrhnqdP4ZIG6FHMlgUO
 DS5lmd5TyHXLNBSTYn4dZfQ+V0fMmYrDe9xEMujKrRHJ0/rxhapPymvf0hniRevw
 WlcKKMQdW+cIW144ujk1T2ELjJdy/CqDnfb8rMr/CAeFW0qXSTjE8M178Ii1M6gd
 CI+3Rkt8VgmCXYR2b9xAX0bEs0ncjxTAlBSxEFpiA5ZpX1WvWxPQont7zzvANQb/
 l33WmD1UTymZT9vtFIOL6GsN4/kk4pY8+n42LkLGPyQ1iZuxFH0AmsXIPcKQvOV+
 w4qn/Kcrhvx8stYw7laPjuPzYzWSbHcC1CsoShbfdFpPw4Sp9rxKT8t1aiB/aeiP
 M4lbyHn3ZqwclWLFd7l8sTgIbe4OtYfhIWOx6f0cpdUxH8Qwkh/ij+c+yEYD3Kt3
 AMjtigQ29ixXquVNVjhlV770mmnaZ29ONtPTBq6Fwt+A9ksGtdNLs3SZmzoFkKPe
 0ByviDWhPdsjw7dRz/Pz5yZOgJHbJHvmkrCuQkKlJByOlUIgd4kVqVCZ9ZRaaBEw
 upw0g8QFStVmf7wGfflMT6sTGIXUSTAmxoWVWi8o+qFmV1uKtSpZU4pWa1IGMX/j
 T97/Uosee3vGFgcU1Ea0hnDpzNHUQTYMqJHVkg30avQnLh8WYkly6eo9yyQkVj+8
 9Oi+J3H/6vjUeTtP66f2
 =KtI3
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kraxel/tags/audio-20190821-pull-request' into staging

audio: second batch of -audiodev support, adding support for multiple backends.

# gpg: Signature made Wed 21 Aug 2019 09:40:37 BST
# 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-20190821-pull-request:
  audio: fix memory leak reported by ASAN
  audio: use size_t where makes sense
  audio: remove read and write pcm_ops
  paaudio: fix playback glitches
  audio: do not run each backend in audio_run
  audio: remove audio_MIN, audio_MAX
  paaudio: properly disconnect streams in fini_*
  paaudio: do not move stream when sink/source name is specified
  audio: audiodev= parameters no longer optional when -audiodev present
  paaudio: prepare for multiple audiodev
  audio: add audiodev properties to frontends
  audio: add audiodev property to vnc and wav_capture
  audio: basic support for multi backend audio
  audio: reduce glob_audio_state usage
  audio: Add missing fall through comments

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-08-21 15:18:50 +01:00
commit 33f18cf7dc
38 changed files with 794 additions and 655 deletions

View File

@ -39,6 +39,7 @@ struct pollhlp {
struct pollfd *pfds; struct pollfd *pfds;
int count; int count;
int mask; int mask;
AudioState *s;
}; };
typedef struct ALSAVoiceOut { typedef struct ALSAVoiceOut {
@ -199,11 +200,11 @@ static void alsa_poll_handler (void *opaque)
break; break;
case SND_PCM_STATE_PREPARED: case SND_PCM_STATE_PREPARED:
audio_run ("alsa run (prepared)"); audio_run(hlp->s, "alsa run (prepared)");
break; break;
case SND_PCM_STATE_RUNNING: case SND_PCM_STATE_RUNNING:
audio_run ("alsa run (running)"); audio_run(hlp->s, "alsa run (running)");
break; break;
default: default:
@ -269,11 +270,6 @@ static int alsa_poll_in (HWVoiceIn *hw)
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN); return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
} }
static int alsa_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
{ {
switch (fmt) { switch (fmt) {
@ -634,7 +630,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
while (alsa->pending) { while (alsa->pending) {
int left_till_end_samples = hw->samples - alsa->wpos; int left_till_end_samples = hw->samples - alsa->wpos;
int len = audio_MIN (alsa->pending, left_till_end_samples); int len = MIN (alsa->pending, left_till_end_samples);
char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift); char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
while (len) { while (len) {
@ -685,10 +681,10 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
} }
} }
static int alsa_run_out (HWVoiceOut *hw, int live) static size_t alsa_run_out(HWVoiceOut *hw, size_t live)
{ {
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
int decr; size_t decr;
snd_pcm_sframes_t avail; snd_pcm_sframes_t avail;
avail = alsa_get_avail (alsa->handle); avail = alsa_get_avail (alsa->handle);
@ -697,7 +693,7 @@ static int alsa_run_out (HWVoiceOut *hw, int live)
return 0; return 0;
} }
decr = audio_MIN (live, avail); decr = MIN (live, avail);
decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending); decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
alsa->pending += decr; alsa->pending += decr;
alsa_write_pending (alsa); alsa_write_pending (alsa);
@ -743,12 +739,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift); alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) { if (!alsa->pcm_buf) {
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", dolog("Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift); hw->samples, 1 << hw->info.shift);
alsa_anal_close1 (&handle); alsa_anal_close1 (&handle);
return -1; return -1;
} }
alsa->pollhlp.s = hw->s;
alsa->handle = handle; alsa->handle = handle;
alsa->dev = dev; alsa->dev = dev;
return 0; return 0;
@ -844,12 +841,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) { if (!alsa->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift); hw->samples, 1 << hw->info.shift);
alsa_anal_close1 (&handle); alsa_anal_close1 (&handle);
return -1; return -1;
} }
alsa->pollhlp.s = hw->s;
alsa->handle = handle; alsa->handle = handle;
alsa->dev = dev; alsa->dev = dev;
return 0; return 0;
@ -865,17 +863,17 @@ static void alsa_fini_in (HWVoiceIn *hw)
alsa->pcm_buf = NULL; alsa->pcm_buf = NULL;
} }
static int alsa_run_in (HWVoiceIn *hw) static size_t alsa_run_in(HWVoiceIn *hw)
{ {
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
int hwshift = hw->info.shift; int hwshift = hw->info.shift;
int i; int i;
int live = audio_pcm_hw_get_live_in (hw); size_t live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live; size_t dead = hw->samples - live;
int decr; size_t decr;
struct { struct {
int add; size_t add;
int len; size_t len;
} bufs[2] = { } bufs[2] = {
{ .add = hw->wpos, .len = 0 }, { .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 } { .add = 0, .len = 0 }
@ -915,7 +913,7 @@ static int alsa_run_in (HWVoiceIn *hw)
} }
} }
decr = audio_MIN (dead, avail); decr = MIN(dead, avail);
if (!decr) { if (!decr) {
return 0; return 0;
} }
@ -985,11 +983,6 @@ static int alsa_run_in (HWVoiceIn *hw)
return read_samples; return read_samples;
} }
static int alsa_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, 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;
@ -1073,13 +1066,11 @@ 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,
.run_out = alsa_run_out, .run_out = alsa_run_out,
.write = alsa_write,
.ctl_out = alsa_ctl_out, .ctl_out = alsa_ctl_out,
.init_in = alsa_init_in, .init_in = alsa_init_in,
.fini_in = alsa_fini_in, .fini_in = alsa_fini_in,
.run_in = alsa_run_in, .run_in = alsa_run_in,
.read = alsa_read,
.ctl_in = alsa_ctl_in, .ctl_in = alsa_ctl_in,
}; };

View File

@ -87,7 +87,8 @@ audio_driver *audio_driver_lookup(const char *name)
return NULL; return NULL;
} }
static AudioState glob_audio_state; static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
QTAILQ_HEAD_INITIALIZER(audio_states);
const struct mixeng_volume nominal_volume = { const struct mixeng_volume nominal_volume = {
.mute = 0, .mute = 0,
@ -100,6 +101,8 @@ const struct mixeng_volume nominal_volume = {
#endif #endif
}; };
static bool legacy_config = true;
#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
#error No its not #error No its not
#else #else
@ -306,6 +309,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
case AUDIO_FORMAT_S16: case AUDIO_FORMAT_S16:
sign = 1; sign = 1;
/* fall through */
case AUDIO_FORMAT_U16: case AUDIO_FORMAT_U16:
bits = 16; bits = 16;
shift = 1; shift = 1;
@ -313,6 +317,7 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
case AUDIO_FORMAT_S32: case AUDIO_FORMAT_S32:
sign = 1; sign = 1;
/* fall through */
case AUDIO_FORMAT_U32: case AUDIO_FORMAT_U32:
bits = 32; bits = 32;
shift = 2; shift = 2;
@ -399,12 +404,10 @@ static void noop_conv (struct st_sample *dst, const void *src, int samples)
(void) samples; (void) samples;
} }
static CaptureVoiceOut *audio_pcm_capture_find_specific ( static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
struct audsettings *as struct audsettings *as)
)
{ {
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
AudioState *s = &glob_audio_state;
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
if (audio_pcm_info_eq (&cap->hw.info, as)) { if (audio_pcm_info_eq (&cap->hw.info, as)) {
@ -481,7 +484,7 @@ static void audio_detach_capture (HWVoiceOut *hw)
static int audio_attach_capture (HWVoiceOut *hw) static int audio_attach_capture (HWVoiceOut *hw)
{ {
AudioState *s = &glob_audio_state; AudioState *s = hw->s;
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
audio_detach_capture (hw); audio_detach_capture (hw);
@ -525,41 +528,41 @@ static int audio_attach_capture (HWVoiceOut *hw)
/* /*
* Hard voice (capture) * Hard voice (capture)
*/ */
static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
{ {
SWVoiceIn *sw; SWVoiceIn *sw;
int m = hw->total_samples_captured; size_t m = hw->total_samples_captured;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active) { if (sw->active) {
m = audio_MIN (m, sw->total_hw_samples_acquired); m = MIN (m, sw->total_hw_samples_acquired);
} }
} }
return m; return m;
} }
int audio_pcm_hw_get_live_in (HWVoiceIn *hw) size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
{ {
int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw); size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
if (audio_bug(__func__, live < 0 || live > hw->samples)) { if (audio_bug(__func__, live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples); dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
return 0; return 0;
} }
return live; return live;
} }
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
int live, int pending) size_t live, size_t pending)
{ {
int left = hw->samples - pending; size_t left = hw->samples - pending;
int len = audio_MIN (left, live); size_t len = MIN (left, live);
int clipped = 0; size_t clipped = 0;
while (len) { while (len) {
struct st_sample *src = hw->mix_buf + hw->rpos; struct st_sample *src = hw->mix_buf + hw->rpos;
uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift); uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
int samples_till_end_of_buf = hw->samples - hw->rpos; size_t samples_till_end_of_buf = hw->samples - hw->rpos;
int samples_to_clip = audio_MIN (len, samples_till_end_of_buf); size_t samples_to_clip = MIN (len, samples_till_end_of_buf);
hw->clip (dst, src, samples_to_clip); hw->clip (dst, src, samples_to_clip);
@ -573,14 +576,14 @@ int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
/* /*
* Soft voice (capture) * Soft voice (capture)
*/ */
static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
{ {
HWVoiceIn *hw = sw->hw; HWVoiceIn *hw = sw->hw;
int live = hw->total_samples_captured - sw->total_hw_samples_acquired; ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
int rpos; ssize_t rpos;
if (audio_bug(__func__, live < 0 || live > hw->samples)) { if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples); dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
return 0; return 0;
} }
@ -593,17 +596,17 @@ static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
} }
} }
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size) static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
{ {
HWVoiceIn *hw = sw->hw; HWVoiceIn *hw = sw->hw;
int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
struct st_sample *src, *dst = sw->buf; struct st_sample *src, *dst = sw->buf;
rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
live = hw->total_samples_captured - sw->total_hw_samples_acquired; live = hw->total_samples_captured - sw->total_hw_samples_acquired;
if (audio_bug(__func__, live < 0 || live > hw->samples)) { if (audio_bug(__func__, live > hw->samples)) {
dolog ("live_in=%d hw->samples=%d\n", live, hw->samples); dolog("live_in=%zu hw->samples=%zu\n", live, hw->samples);
return 0; return 0;
} }
@ -613,13 +616,13 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
} }
swlim = (live * sw->ratio) >> 32; swlim = (live * sw->ratio) >> 32;
swlim = audio_MIN (swlim, samples); swlim = MIN (swlim, samples);
while (swlim) { while (swlim) {
src = hw->conv_buf + rpos; src = hw->conv_buf + rpos;
if (hw->wpos > rpos) {
isamp = hw->wpos - rpos; isamp = hw->wpos - rpos;
/* XXX: <= ? */ } else {
if (isamp <= 0) {
isamp = hw->samples - rpos; isamp = hw->samples - rpos;
} }
@ -628,11 +631,6 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
} }
osamp = swlim; osamp = swlim;
if (audio_bug(__func__, osamp < 0)) {
dolog ("osamp=%d\n", osamp);
return 0;
}
st_rate_flow (sw->rate, src, dst, &isamp, &osamp); st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
swlim -= osamp; swlim -= osamp;
rpos = (rpos + isamp) % hw->samples; rpos = (rpos + isamp) % hw->samples;
@ -653,15 +651,15 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
/* /*
* Hard voice (playback) * Hard voice (playback)
*/ */
static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) static size_t audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
{ {
SWVoiceOut *sw; SWVoiceOut *sw;
int m = INT_MAX; size_t m = SIZE_MAX;
int nb_live = 0; int nb_live = 0;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (sw->active || !sw->empty) { if (sw->active || !sw->empty) {
m = audio_MIN (m, sw->total_hw_samples_mixed); m = MIN (m, sw->total_hw_samples_mixed);
nb_live += 1; nb_live += 1;
} }
} }
@ -670,9 +668,9 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
return m; return m;
} }
static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
{ {
int smin; size_t smin;
int nb_live1; int nb_live1;
smin = audio_pcm_hw_find_min_out (hw, &nb_live1); smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
@ -681,10 +679,10 @@ static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
} }
if (nb_live1) { if (nb_live1) {
int live = smin; size_t live = smin;
if (audio_bug(__func__, live < 0 || live > hw->samples)) { if (audio_bug(__func__, live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples); dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
return 0; return 0;
} }
return live; return live;
@ -695,10 +693,10 @@ static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
/* /*
* Soft voice (playback) * Soft voice (playback)
*/ */
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
{ {
int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; size_t hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
int ret = 0, pos = 0, total = 0; size_t ret = 0, pos = 0, total = 0;
if (!sw) { if (!sw) {
return size; return size;
@ -707,8 +705,8 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
hwsamples = sw->hw->samples; hwsamples = sw->hw->samples;
live = sw->total_hw_samples_mixed; live = sw->total_hw_samples_mixed;
if (audio_bug(__func__, live < 0 || live > hwsamples)) { if (audio_bug(__func__, live > hwsamples)) {
dolog ("live=%d hw->samples=%d\n", live, hwsamples); dolog("live=%zu hw->samples=%zu\n", live, hwsamples);
return 0; return 0;
} }
@ -724,7 +722,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
dead = hwsamples - live; dead = hwsamples - live;
swlim = ((int64_t) dead << 32) / sw->ratio; swlim = ((int64_t) dead << 32) / sw->ratio;
swlim = audio_MIN (swlim, samples); swlim = MIN (swlim, samples);
if (swlim) { if (swlim) {
sw->conv (sw->buf, buf, swlim); sw->conv (sw->buf, buf, swlim);
@ -736,7 +734,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
while (swlim) { while (swlim) {
dead = hwsamples - live; dead = hwsamples - live;
left = hwsamples - wpos; left = hwsamples - wpos;
blck = audio_MIN (dead, left); blck = MIN (dead, left);
if (!blck) { if (!blck) {
break; break;
} }
@ -762,7 +760,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
#ifdef DEBUG_OUT #ifdef DEBUG_OUT
dolog ( dolog (
"%s: write size %d ret %d total sw %d\n", "%s: write size %zu ret %zu total sw %zu\n",
SW_NAME (sw), SW_NAME (sw),
size >> sw->info.shift, size >> sw->info.shift,
ret, ret,
@ -789,19 +787,15 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
/* /*
* Timer * Timer
*/ */
static int audio_is_timer_needed(AudioState *s)
static bool audio_timer_running;
static uint64_t audio_timer_last;
static int audio_is_timer_needed (void)
{ {
HWVoiceIn *hwi = NULL; HWVoiceIn *hwi = NULL;
HWVoiceOut *hwo = NULL; HWVoiceOut *hwo = NULL;
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
if (!hwo->poll_mode) return 1; if (!hwo->poll_mode) return 1;
} }
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
if (!hwi->poll_mode) return 1; if (!hwi->poll_mode) return 1;
} }
return 0; return 0;
@ -809,18 +803,18 @@ static int audio_is_timer_needed (void)
static void audio_reset_timer (AudioState *s) static void audio_reset_timer (AudioState *s)
{ {
if (audio_is_timer_needed ()) { if (audio_is_timer_needed(s)) {
timer_mod_anticipate_ns(s->ts, timer_mod_anticipate_ns(s->ts,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks); qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
if (!audio_timer_running) { if (!s->timer_running) {
audio_timer_running = true; s->timer_running = true;
audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); s->timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
trace_audio_timer_start(s->period_ticks / SCALE_MS); trace_audio_timer_start(s->period_ticks / SCALE_MS);
} }
} else { } else {
timer_del(s->ts); timer_del(s->ts);
if (audio_timer_running) { if (s->timer_running) {
audio_timer_running = false; s->timer_running = false;
trace_audio_timer_stop(); trace_audio_timer_stop();
} }
} }
@ -832,20 +826,20 @@ static void audio_timer (void *opaque)
AudioState *s = opaque; AudioState *s = opaque;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
diff = now - audio_timer_last; diff = now - s->timer_last;
if (diff > s->period_ticks * 3 / 2) { if (diff > s->period_ticks * 3 / 2) {
trace_audio_timer_delayed(diff / SCALE_MS); trace_audio_timer_delayed(diff / SCALE_MS);
} }
audio_timer_last = now; s->timer_last = now;
audio_run("timer"); audio_run(s, "timer");
audio_reset_timer(s); audio_reset_timer(s);
} }
/* /*
* Public API * Public API
*/ */
int AUD_write (SWVoiceOut *sw, void *buf, int size) size_t AUD_write(SWVoiceOut *sw, void *buf, size_t size)
{ {
if (!sw) { if (!sw) {
/* XXX: Consider options */ /* XXX: Consider options */
@ -857,10 +851,10 @@ int AUD_write (SWVoiceOut *sw, void *buf, int size)
return 0; return 0;
} }
return sw->hw->pcm_ops->write(sw, buf, size); return audio_pcm_sw_write(sw, buf, size);
} }
int AUD_read (SWVoiceIn *sw, void *buf, int size) size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
{ {
if (!sw) { if (!sw) {
/* XXX: Consider options */ /* XXX: Consider options */
@ -872,7 +866,7 @@ int AUD_read (SWVoiceIn *sw, void *buf, int size)
return 0; return 0;
} }
return sw->hw->pcm_ops->read(sw, buf, size); return audio_pcm_sw_read(sw, buf, size);
} }
int AUD_get_buffer_size_out (SWVoiceOut *sw) int AUD_get_buffer_size_out (SWVoiceOut *sw)
@ -890,7 +884,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw = sw->hw; hw = sw->hw;
if (sw->active != on) { if (sw->active != on) {
AudioState *s = &glob_audio_state; AudioState *s = sw->s;
SWVoiceOut *temp_sw; SWVoiceOut *temp_sw;
SWVoiceCap *sc; SWVoiceCap *sc;
@ -937,7 +931,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
hw = sw->hw; hw = sw->hw;
if (sw->active != on) { if (sw->active != on) {
AudioState *s = &glob_audio_state; AudioState *s = sw->s;
SWVoiceIn *temp_sw; SWVoiceIn *temp_sw;
if (on) { if (on) {
@ -969,17 +963,17 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
} }
} }
static int audio_get_avail (SWVoiceIn *sw) static size_t audio_get_avail (SWVoiceIn *sw)
{ {
int live; size_t live;
if (!sw) { if (!sw) {
return 0; return 0;
} }
live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
if (audio_bug(__func__, live < 0 || live > sw->hw->samples)) { if (audio_bug(__func__, live > sw->hw->samples)) {
dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); dolog("live=%zu sw->hw->samples=%zu\n", live, sw->hw->samples);
return 0; return 0;
} }
@ -992,9 +986,9 @@ static int audio_get_avail (SWVoiceIn *sw)
return (((int64_t) live << 32) / sw->ratio) << sw->info.shift; return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
} }
static int audio_get_free (SWVoiceOut *sw) static size_t audio_get_free(SWVoiceOut *sw)
{ {
int live, dead; size_t live, dead;
if (!sw) { if (!sw) {
return 0; return 0;
@ -1002,8 +996,8 @@ static int audio_get_free (SWVoiceOut *sw)
live = sw->total_hw_samples_mixed; live = sw->total_hw_samples_mixed;
if (audio_bug(__func__, live < 0 || live > sw->hw->samples)) { if (audio_bug(__func__, live > sw->hw->samples)) {
dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples); dolog("live=%zu sw->hw->samples=%zu\n", live, sw->hw->samples);
return 0; return 0;
} }
@ -1018,9 +1012,10 @@ static int audio_get_free (SWVoiceOut *sw)
return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
} }
static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
size_t samples)
{ {
int n; size_t n;
if (hw->enabled) { if (hw->enabled) {
SWVoiceCap *sc; SWVoiceCap *sc;
@ -1031,16 +1026,16 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
n = samples; n = samples;
while (n) { while (n) {
int till_end_of_hw = hw->samples - rpos2; size_t till_end_of_hw = hw->samples - rpos2;
int to_write = audio_MIN (till_end_of_hw, n); size_t to_write = MIN(till_end_of_hw, n);
int bytes = to_write << hw->info.shift; size_t bytes = to_write << hw->info.shift;
int written; size_t written;
sw->buf = hw->mix_buf + rpos2; sw->buf = hw->mix_buf + rpos2;
written = audio_pcm_sw_write (sw, NULL, bytes); written = audio_pcm_sw_write (sw, NULL, bytes);
if (written - bytes) { if (written - bytes) {
dolog ("Could not mix %d bytes into a capture " dolog("Could not mix %zu bytes into a capture "
"buffer, mixed %d\n", "buffer, mixed %zu\n",
bytes, written); bytes, written);
break; break;
} }
@ -1050,7 +1045,7 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
} }
} }
n = audio_MIN (samples, hw->samples - rpos); n = MIN(samples, hw->samples - rpos);
mixeng_clear(hw->mix_buf + rpos, n); mixeng_clear(hw->mix_buf + rpos, n);
mixeng_clear(hw->mix_buf, samples - n); mixeng_clear(hw->mix_buf, samples - n);
} }
@ -1060,17 +1055,17 @@ static void audio_run_out (AudioState *s)
HWVoiceOut *hw = NULL; HWVoiceOut *hw = NULL;
SWVoiceOut *sw; SWVoiceOut *sw;
while ((hw = audio_pcm_hw_find_any_enabled_out (hw))) { while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
int played; size_t played, live, prev_rpos, free;
int live, free, nb_live, cleanup_required, prev_rpos; int nb_live, cleanup_required;
live = audio_pcm_hw_get_live_out (hw, &nb_live); live = audio_pcm_hw_get_live_out (hw, &nb_live);
if (!nb_live) { if (!nb_live) {
live = 0; live = 0;
} }
if (audio_bug(__func__, live < 0 || live > hw->samples)) { if (audio_bug(__func__, live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples); dolog ("live=%zu hw->samples=%zu\n", live, hw->samples);
continue; continue;
} }
@ -1105,13 +1100,13 @@ static void audio_run_out (AudioState *s)
played = hw->pcm_ops->run_out (hw, live); played = hw->pcm_ops->run_out (hw, live);
replay_audio_out(&played); replay_audio_out(&played);
if (audio_bug(__func__, hw->rpos >= hw->samples)) { if (audio_bug(__func__, hw->rpos >= hw->samples)) {
dolog ("hw->rpos=%d hw->samples=%d played=%d\n", dolog("hw->rpos=%zu hw->samples=%zu played=%zu\n",
hw->rpos, hw->samples, played); hw->rpos, hw->samples, played);
hw->rpos = 0; hw->rpos = 0;
} }
#ifdef DEBUG_OUT #ifdef DEBUG_OUT
dolog ("played=%d\n", played); dolog("played=%zu\n", played);
#endif #endif
if (played) { if (played) {
@ -1126,7 +1121,7 @@ static void audio_run_out (AudioState *s)
} }
if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) { if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) {
dolog ("played=%d sw->total_hw_samples_mixed=%d\n", dolog("played=%zu sw->total_hw_samples_mixed=%zu\n",
played, sw->total_hw_samples_mixed); played, sw->total_hw_samples_mixed);
played = sw->total_hw_samples_mixed; played = sw->total_hw_samples_mixed;
} }
@ -1165,9 +1160,9 @@ static void audio_run_in (AudioState *s)
{ {
HWVoiceIn *hw = NULL; HWVoiceIn *hw = NULL;
while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) { while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
SWVoiceIn *sw; SWVoiceIn *sw;
int captured = 0, min; size_t captured = 0, min;
if (replay_mode != REPLAY_MODE_PLAY) { if (replay_mode != REPLAY_MODE_PLAY) {
captured = hw->pcm_ops->run_in(hw); captured = hw->pcm_ops->run_in(hw);
@ -1182,7 +1177,7 @@ static void audio_run_in (AudioState *s)
sw->total_hw_samples_acquired -= min; sw->total_hw_samples_acquired -= min;
if (sw->active) { if (sw->active) {
int avail; size_t avail;
avail = audio_get_avail (sw); avail = audio_get_avail (sw);
if (avail > 0) { if (avail > 0) {
@ -1198,15 +1193,15 @@ static void audio_run_capture (AudioState *s)
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
int live, rpos, captured; size_t live, rpos, captured;
HWVoiceOut *hw = &cap->hw; HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw; SWVoiceOut *sw;
captured = live = audio_pcm_hw_get_live_out (hw, NULL); captured = live = audio_pcm_hw_get_live_out (hw, NULL);
rpos = hw->rpos; rpos = hw->rpos;
while (live) { while (live) {
int left = hw->samples - rpos; size_t left = hw->samples - rpos;
int to_capture = audio_MIN (live, left); size_t to_capture = MIN(live, left);
struct st_sample *src; struct st_sample *src;
struct capture_callback *cb; struct capture_callback *cb;
@ -1229,7 +1224,7 @@ static void audio_run_capture (AudioState *s)
} }
if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) { if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) {
dolog ("captured=%d sw->total_hw_samples_mixed=%d\n", dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n",
captured, sw->total_hw_samples_mixed); captured, sw->total_hw_samples_mixed);
captured = sw->total_hw_samples_mixed; captured = sw->total_hw_samples_mixed;
} }
@ -1240,13 +1235,12 @@ static void audio_run_capture (AudioState *s)
} }
} }
void audio_run (const char *msg) void audio_run(AudioState *s, const char *msg)
{ {
AudioState *s = &glob_audio_state;
audio_run_out(s); audio_run_out(s);
audio_run_in(s); audio_run_in(s);
audio_run_capture(s); audio_run_capture(s);
#ifdef DEBUG_POLL #ifdef DEBUG_POLL
{ {
static double prevtime; static double prevtime;
@ -1271,8 +1265,8 @@ static int audio_driver_init(AudioState *s, struct audio_driver *drv,
s->drv_opaque = drv->init(dev); s->drv_opaque = drv->init(dev);
if (s->drv_opaque) { if (s->drv_opaque) {
audio_init_nb_voices_out (drv); audio_init_nb_voices_out(s, drv);
audio_init_nb_voices_in (drv); audio_init_nb_voices_in(s, drv);
s->drv = drv; s->drv = drv;
return 0; return 0;
} }
@ -1293,11 +1287,11 @@ static void audio_vm_change_state_handler (void *opaque, int running,
int op = running ? VOICE_ENABLE : VOICE_DISABLE; int op = running ? VOICE_ENABLE : VOICE_DISABLE;
s->vm_running = running; s->vm_running = running;
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
hwo->pcm_ops->ctl_out(hwo, op); hwo->pcm_ops->ctl_out(hwo, op);
} }
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
hwi->pcm_ops->ctl_in(hwi, op); hwi->pcm_ops->ctl_in(hwi, op);
} }
audio_reset_timer (s); audio_reset_timer (s);
@ -1310,14 +1304,12 @@ bool audio_is_cleaning_up(void)
return is_cleaning_up; return is_cleaning_up;
} }
void audio_cleanup(void) static void free_audio_state(AudioState *s)
{ {
AudioState *s = &glob_audio_state;
HWVoiceOut *hwo, *hwon; HWVoiceOut *hwo, *hwon;
HWVoiceIn *hwi, *hwin; HWVoiceIn *hwi, *hwin;
is_cleaning_up = true; QLIST_FOREACH_SAFE(hwo, &s->hw_head_out, entries, hwon) {
QLIST_FOREACH_SAFE(hwo, &glob_audio_state.hw_head_out, entries, hwon) {
SWVoiceCap *sc; SWVoiceCap *sc;
if (hwo->enabled) { if (hwo->enabled) {
@ -1336,7 +1328,7 @@ void audio_cleanup(void)
QLIST_REMOVE(hwo, entries); QLIST_REMOVE(hwo, entries);
} }
QLIST_FOREACH_SAFE(hwi, &glob_audio_state.hw_head_in, entries, hwin) { QLIST_FOREACH_SAFE(hwi, &s->hw_head_in, entries, hwin) {
if (hwi->enabled) { if (hwi->enabled) {
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE); hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
} }
@ -1353,6 +1345,23 @@ void audio_cleanup(void)
qapi_free_Audiodev(s->dev); qapi_free_Audiodev(s->dev);
s->dev = NULL; s->dev = NULL;
} }
if (s->ts) {
timer_free(s->ts);
s->ts = NULL;
}
g_free(s);
}
void audio_cleanup(void)
{
is_cleaning_up = true;
while (!QTAILQ_EMPTY(&audio_states)) {
AudioState *s = QTAILQ_FIRST(&audio_states);
QTAILQ_REMOVE(&audio_states, s, list);
free_audio_state(s);
}
} }
static const VMStateDescription vmstate_audio = { static const VMStateDescription vmstate_audio = {
@ -1379,28 +1388,34 @@ static AudiodevListEntry *audiodev_find(
return NULL; return NULL;
} }
static int audio_init(Audiodev *dev) /*
* if we have dev, this function was called because of an -audiodev argument =>
* initialize a new state with it
* if dev == NULL => legacy implicit initialization, return the already created
* state or create a new one
*/
static AudioState *audio_init(Audiodev *dev, const char *name)
{ {
static bool atexit_registered;
size_t i; size_t i;
int done = 0; int done = 0;
const char *drvname = NULL; const char *drvname = NULL;
VMChangeStateEntry *e; VMChangeStateEntry *e;
AudioState *s = &glob_audio_state; AudioState *s;
struct audio_driver *driver; struct audio_driver *driver;
/* silence gcc warning about uninitialized variable */ /* silence gcc warning about uninitialized variable */
AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head);
if (s->drv) {
if (dev) {
dolog("Cannot create more than one audio backend, sorry\n");
qapi_free_Audiodev(dev);
}
return -1;
}
if (dev) { if (dev) {
/* -audiodev option */ /* -audiodev option */
legacy_config = false;
drvname = AudiodevDriver_str(dev->driver); drvname = AudiodevDriver_str(dev->driver);
} else if (!QTAILQ_EMPTY(&audio_states)) {
if (!legacy_config) {
dolog("You must specify an audiodev= for the device %s\n", name);
exit(1);
}
return QTAILQ_FIRST(&audio_states);
} else { } else {
/* legacy implicit initialization */ /* legacy implicit initialization */
head = audio_handle_legacy_opts(); head = audio_handle_legacy_opts();
@ -1414,12 +1429,18 @@ static int audio_init(Audiodev *dev)
dev = QSIMPLEQ_FIRST(&head)->dev; dev = QSIMPLEQ_FIRST(&head)->dev;
audio_validate_opts(dev, &error_abort); audio_validate_opts(dev, &error_abort);
} }
s = g_malloc0(sizeof(AudioState));
s->dev = dev; s->dev = dev;
QLIST_INIT (&s->hw_head_out); QLIST_INIT (&s->hw_head_out);
QLIST_INIT (&s->hw_head_in); QLIST_INIT (&s->hw_head_in);
QLIST_INIT (&s->cap_head); QLIST_INIT (&s->cap_head);
if (!atexit_registered) {
atexit(audio_cleanup); atexit(audio_cleanup);
atexit_registered = true;
}
QTAILQ_INSERT_TAIL(&audio_states, s, list);
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
@ -1484,7 +1505,7 @@ static int audio_init(Audiodev *dev)
QLIST_INIT (&s->card_head); QLIST_INIT (&s->card_head);
vmstate_register (NULL, 0, &vmstate_audio, s); vmstate_register (NULL, 0, &vmstate_audio, s);
return 0; return s;
} }
void audio_free_audiodev_list(AudiodevListHead *head) void audio_free_audiodev_list(AudiodevListHead *head)
@ -1499,10 +1520,13 @@ void audio_free_audiodev_list(AudiodevListHead *head)
void AUD_register_card (const char *name, QEMUSoundCard *card) void AUD_register_card (const char *name, QEMUSoundCard *card)
{ {
audio_init(NULL); if (!card->state) {
card->state = audio_init(NULL, name);
}
card->name = g_strdup (name); card->name = g_strdup (name);
memset (&card->entries, 0, sizeof (card->entries)); memset (&card->entries, 0, sizeof (card->entries));
QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries); QLIST_INSERT_HEAD(&card->state->card_head, card, entries);
} }
void AUD_remove_card (QEMUSoundCard *card) void AUD_remove_card (QEMUSoundCard *card)
@ -1513,15 +1537,23 @@ void AUD_remove_card (QEMUSoundCard *card)
CaptureVoiceOut *AUD_add_capture( CaptureVoiceOut *AUD_add_capture(
AudioState *s,
struct audsettings *as, struct audsettings *as,
struct audio_capture_ops *ops, struct audio_capture_ops *ops,
void *cb_opaque void *cb_opaque
) )
{ {
AudioState *s = &glob_audio_state;
CaptureVoiceOut *cap; CaptureVoiceOut *cap;
struct capture_callback *cb; struct capture_callback *cb;
if (!s) {
if (!legacy_config) {
dolog("You must specify audiodev when trying to capture\n");
return NULL;
}
s = audio_init(NULL, NULL);
}
if (audio_validate_settings (as)) { if (audio_validate_settings (as)) {
dolog ("Invalid settings were passed when trying to add capture\n"); dolog ("Invalid settings were passed when trying to add capture\n");
audio_print_settings (as); audio_print_settings (as);
@ -1532,7 +1564,7 @@ CaptureVoiceOut *AUD_add_capture (
cb->ops = *ops; cb->ops = *ops;
cb->opaque = cb_opaque; cb->opaque = cb_opaque;
cap = audio_pcm_capture_find_specific (as); cap = audio_pcm_capture_find_specific(s, as);
if (cap) { if (cap) {
QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
return cap; return cap;
@ -1544,6 +1576,7 @@ CaptureVoiceOut *AUD_add_capture (
cap = g_malloc0(sizeof(*cap)); cap = g_malloc0(sizeof(*cap));
hw = &cap->hw; hw = &cap->hw;
hw->s = s;
QLIST_INIT (&hw->sw_head); QLIST_INIT (&hw->sw_head);
QLIST_INIT (&cap->cb_head); QLIST_INIT (&cap->cb_head);
@ -1564,7 +1597,7 @@ CaptureVoiceOut *AUD_add_capture (
QLIST_INSERT_HEAD (&s->cap_head, cap, entries); QLIST_INSERT_HEAD (&s->cap_head, cap, entries);
QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
QLIST_FOREACH(hw, &glob_audio_state.hw_head_out, entries) { QLIST_FOREACH(hw, &s->hw_head_out, entries) {
audio_attach_capture (hw); audio_attach_capture (hw);
} }
return cap; return cap;
@ -1749,7 +1782,7 @@ void audio_init_audiodevs(void)
AudiodevListEntry *e; AudiodevListEntry *e;
QSIMPLEQ_FOREACH(e, &audiodevs, next) { QSIMPLEQ_FOREACH(e, &audiodevs, next) {
audio_init(e->dev); audio_init(e->dev, NULL);
} }
} }
@ -1810,3 +1843,25 @@ int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
return audio_buffer_samples(pdo, as, def_usecs) * return audio_buffer_samples(pdo, as, def_usecs) *
audioformat_bytes_per_sample(as->fmt); audioformat_bytes_per_sample(as->fmt);
} }
AudioState *audio_state_by_name(const char *name)
{
AudioState *s;
QTAILQ_FOREACH(s, &audio_states, list) {
assert(s->dev);
if (strcmp(name, s->dev->id) == 0) {
return s;
}
}
return NULL;
}
const char *audio_get_id(QEMUSoundCard *card)
{
if (card->state) {
assert(card->state->dev);
return card->state->dev->id;
} else {
return "";
}
}

View File

@ -27,6 +27,7 @@
#include "qemu/queue.h" #include "qemu/queue.h"
#include "qapi/qapi-types-audio.h" #include "qapi/qapi-types-audio.h"
#include "hw/qdev-properties.h"
typedef void (*audio_callback_fn) (void *opaque, int avail); typedef void (*audio_callback_fn) (void *opaque, int avail);
@ -78,8 +79,10 @@ typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut; typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn; typedef struct SWVoiceIn SWVoiceIn;
typedef struct AudioState AudioState;
typedef struct QEMUSoundCard { typedef struct QEMUSoundCard {
char *name; char *name;
AudioState *state;
QLIST_ENTRY (QEMUSoundCard) entries; QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard; } QEMUSoundCard;
@ -93,6 +96,7 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
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(
AudioState *s,
struct audsettings *as, struct audsettings *as,
struct audio_capture_ops *ops, struct audio_capture_ops *ops,
void *opaque void *opaque
@ -109,7 +113,7 @@ SWVoiceOut *AUD_open_out (
); );
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size); size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
int AUD_get_buffer_size_out (SWVoiceOut *sw); int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on); void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw); int AUD_is_active_out (SWVoiceOut *sw);
@ -130,7 +134,7 @@ SWVoiceIn *AUD_open_in (
); );
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size); size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
void AUD_set_active_in (SWVoiceIn *sw, int on); void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw); int AUD_is_active_in (SWVoiceIn *sw);
@ -143,25 +147,8 @@ static inline void *advance (void *p, int incr)
return (d + incr); return (d + incr);
} }
#ifdef __GNUC__ int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
#define audio_MIN(a, b) ( __extension__ ({ \ int freq, int bits, int nchannels);
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)>(tb)?(tb):(ta)); \
}))
#define audio_MAX(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)<(tb)?(tb):(ta)); \
}))
#else
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
#endif
int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels);
bool audio_is_cleaning_up(void); bool audio_is_cleaning_up(void);
void audio_cleanup(void); void audio_cleanup(void);
@ -175,4 +162,10 @@ void audio_parse_option(const char *opt);
void audio_init_audiodevs(void); void audio_init_audiodevs(void);
void audio_legacy_help(void); void audio_legacy_help(void);
AudioState *audio_state_by_name(const char *name);
const char *audio_get_id(QEMUSoundCard *card);
#define DEFINE_AUDIO_PROPERTIES(_s, _f) \
DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
#endif /* QEMU_AUDIO_H */ #endif /* QEMU_AUDIO_H */

View File

@ -49,9 +49,11 @@ struct audio_pcm_info {
int swap_endianness; int swap_endianness;
}; };
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap; typedef struct SWVoiceCap SWVoiceCap;
typedef struct HWVoiceOut { typedef struct HWVoiceOut {
AudioState *s;
int enabled; int enabled;
int poll_mode; int poll_mode;
int pending_disable; int pending_disable;
@ -59,12 +61,12 @@ typedef struct HWVoiceOut {
f_sample *clip; f_sample *clip;
int rpos; size_t rpos;
uint64_t ts_helper; uint64_t ts_helper;
struct st_sample *mix_buf; struct st_sample *mix_buf;
int samples; size_t samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
int ctl_caps; int ctl_caps;
@ -73,19 +75,20 @@ typedef struct HWVoiceOut {
} HWVoiceOut; } HWVoiceOut;
typedef struct HWVoiceIn { typedef struct HWVoiceIn {
AudioState *s;
int enabled; int enabled;
int poll_mode; int poll_mode;
struct audio_pcm_info info; struct audio_pcm_info info;
t_sample *conv; t_sample *conv;
int wpos; size_t wpos;
int total_samples_captured; size_t total_samples_captured;
uint64_t ts_helper; uint64_t ts_helper;
struct st_sample *conv_buf; struct st_sample *conv_buf;
int samples; size_t samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head; QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
int ctl_caps; int ctl_caps;
struct audio_pcm_ops *pcm_ops; struct audio_pcm_ops *pcm_ops;
@ -94,12 +97,13 @@ typedef struct HWVoiceIn {
struct SWVoiceOut { struct SWVoiceOut {
QEMUSoundCard *card; QEMUSoundCard *card;
AudioState *s;
struct audio_pcm_info info; struct audio_pcm_info info;
t_sample *conv; t_sample *conv;
int64_t ratio; int64_t ratio;
struct st_sample *buf; struct st_sample *buf;
void *rate; void *rate;
int total_hw_samples_mixed; size_t total_hw_samples_mixed;
int active; int active;
int empty; int empty;
HWVoiceOut *hw; HWVoiceOut *hw;
@ -111,11 +115,12 @@ struct SWVoiceOut {
struct SWVoiceIn { struct SWVoiceIn {
QEMUSoundCard *card; QEMUSoundCard *card;
AudioState *s;
int active; int active;
struct audio_pcm_info info; struct audio_pcm_info info;
int64_t ratio; int64_t ratio;
void *rate; void *rate;
int total_hw_samples_acquired; size_t total_hw_samples_acquired;
struct st_sample *buf; struct st_sample *buf;
f_sample *clip; f_sample *clip;
HWVoiceIn *hw; HWVoiceIn *hw;
@ -144,14 +149,12 @@ struct audio_driver {
struct audio_pcm_ops { struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque); int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw); void (*fini_out)(HWVoiceOut *hw);
int (*run_out) (HWVoiceOut *hw, int live); size_t (*run_out)(HWVoiceOut *hw, size_t live);
int (*write) (SWVoiceOut *sw, void *buf, int size);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque); int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw); void (*fini_in) (HWVoiceIn *hw);
int (*run_in) (HWVoiceIn *hw); size_t (*run_in)(HWVoiceIn *hw);
int (*read) (SWVoiceIn *sw, void *buf, int size);
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
}; };
@ -188,6 +191,11 @@ typedef struct AudioState {
int nb_hw_voices_in; int nb_hw_voices_in;
int vm_running; int vm_running;
int64_t period_ticks; int64_t period_ticks;
bool timer_running;
uint64_t timer_last;
QTAILQ_ENTRY(AudioState) list;
} AudioState; } AudioState;
extern const struct mixeng_volume nominal_volume; extern const struct mixeng_volume nominal_volume;
@ -200,18 +208,15 @@ audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw);
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
size_t live, size_t pending);
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
int live, int pending);
int audio_bug (const char *funcname, int cond); int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size); void *audio_calloc (const char *funcname, int nmemb, size_t size);
void audio_run (const char *msg); void audio_run(AudioState *s, const char *msg);
#define VOICE_ENABLE 1 #define VOICE_ENABLE 1
#define VOICE_DISABLE 2 #define VOICE_DISABLE 2
@ -219,7 +224,7 @@ void audio_run (const char *msg);
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME) #define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
static inline int audio_ring_dist (int dst, int src, int len) static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
{ {
return (dst >= src) ? (dst - src) : (len - src + dst); return (dst >= src) ? (dst - src) : (len - src + dst);
} }

View File

@ -36,9 +36,9 @@
#define HWBUF hw->conv_buf #define HWBUF hw->conv_buf
#endif #endif
static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv) static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
struct audio_driver *drv)
{ {
AudioState *s = &glob_audio_state;
int max_voices = glue (drv->max_voices_, TYPE); int max_voices = glue (drv->max_voices_, TYPE);
int voice_size = glue (drv->voice_size_, TYPE); int voice_size = glue (drv->voice_size_, TYPE);
@ -75,16 +75,16 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
HWBUF = NULL; HWBUF = NULL;
} }
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw) static bool glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
{ {
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample)); HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
if (!HWBUF) { if (!HWBUF) {
dolog ("Could not allocate " NAME " buffer (%d samples)\n", dolog("Could not allocate " NAME " buffer (%zu samples)\n",
hw->samples); hw->samples);
return -1; return false;
} }
return 0; return true;
} }
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@ -183,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{ {
AudioState *s = &glob_audio_state;
HW *hw = *hwp; HW *hw = *hwp;
AudioState *s = hw->s;
if (!hw->sw_head.lh_first) { if (!hw->sw_head.lh_first) {
#ifdef DAC #ifdef DAC
@ -199,15 +199,14 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
} }
} }
static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw) static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
{ {
AudioState *s = &glob_audio_state;
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first; return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
} }
static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw) static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
{ {
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
if (hw->enabled) { if (hw->enabled) {
return hw; return hw;
} }
@ -215,12 +214,10 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
return NULL; return NULL;
} }
static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
HW *hw, struct audsettings *as)
struct audsettings *as
)
{ {
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) { while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
if (audio_pcm_info_eq (&hw->info, as)) { if (audio_pcm_info_eq (&hw->info, as)) {
return hw; return hw;
} }
@ -228,10 +225,10 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
return NULL; return NULL;
} }
static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
struct audsettings *as)
{ {
HW *hw; HW *hw;
AudioState *s = &glob_audio_state;
struct audio_driver *drv = s->drv; struct audio_driver *drv = s->drv;
if (!glue (s->nb_hw_voices_, TYPE)) { if (!glue (s->nb_hw_voices_, TYPE)) {
@ -255,6 +252,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
return NULL; return NULL;
} }
hw->s = s;
hw->pcm_ops = drv->pcm_ops; hw->pcm_ops = drv->pcm_ops;
hw->ctl_caps = drv->ctl_caps; hw->ctl_caps = drv->ctl_caps;
@ -267,7 +265,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
} }
if (audio_bug(__func__, hw->samples <= 0)) { if (audio_bug(__func__, hw->samples <= 0)) {
dolog ("hw->samples=%d\n", hw->samples); dolog("hw->samples=%zd\n", hw->samples);
goto err1; goto err1;
} }
@ -281,7 +279,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
[hw->info.swap_endianness] [hw->info.swap_endianness]
[audio_bits_to_index (hw->info.bits)]; [audio_bits_to_index (hw->info.bits)];
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { if (!glue(audio_pcm_hw_alloc_resources_, TYPE)(hw)) {
goto err1; goto err1;
} }
@ -328,33 +326,33 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
abort(); abort();
} }
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
{ {
HW *hw; HW *hw;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) { if (pdo->fixed_settings) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (as); hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
if (hw) { if (hw) {
return hw; return hw;
} }
} }
hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as); hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
if (hw) { if (hw) {
return hw; return hw;
} }
hw = glue (audio_pcm_hw_add_new_, TYPE) (as); hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
if (hw) { if (hw) {
return hw; return hw;
} }
return glue (audio_pcm_hw_find_any_, TYPE) (NULL); return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
} }
static SW *glue(audio_pcm_create_voice_pair_, TYPE)( static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
AudioState *s,
const char *sw_name, const char *sw_name,
struct audsettings *as struct audsettings *as
) )
@ -362,7 +360,6 @@ 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); AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) { if (pdo->fixed_settings) {
@ -378,8 +375,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
sw_name ? sw_name : "unknown", sizeof (*sw)); sw_name ? sw_name : "unknown", sizeof (*sw));
goto err1; goto err1;
} }
sw->s = s;
hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as); hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
if (!hw) { if (!hw) {
goto err2; goto err2;
} }
@ -430,7 +428,7 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as struct audsettings *as
) )
{ {
AudioState *s = &glob_audio_state; AudioState *s = card->state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); 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)) {
@ -476,7 +474,7 @@ SW *glue (AUD_open_, TYPE) (
} }
} }
else { else {
sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as); sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
if (!sw) { if (!sw) {
dolog ("Failed to create voice `%s'\n", name); dolog ("Failed to create voice `%s'\n", name);
return NULL; return NULL;

View File

@ -43,9 +43,9 @@ typedef struct coreaudioVoiceOut {
UInt32 audioDevicePropertyBufferFrameSize; UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription; AudioStreamBasicDescription outputStreamBasicDescription;
AudioDeviceIOProcID ioprocid; AudioDeviceIOProcID ioprocid;
int live; size_t live;
int decr; size_t decr;
int rpos; size_t rpos;
} coreaudioVoiceOut; } coreaudioVoiceOut;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@ -397,9 +397,9 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
return 0; return 0;
} }
static int coreaudio_run_out (HWVoiceOut *hw, int live) static size_t coreaudio_run_out(HWVoiceOut *hw, size_t live)
{ {
int decr; size_t decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (coreaudio_lock (core, "coreaudio_run_out")) { if (coreaudio_lock (core, "coreaudio_run_out")) {
@ -413,7 +413,7 @@ static int coreaudio_run_out (HWVoiceOut *hw, int live)
core->live); core->live);
} }
decr = audio_MIN (core->decr, live); decr = MIN (core->decr, live);
core->decr -= decr; core->decr -= decr;
core->live = live - decr; core->live = live - decr;
@ -489,11 +489,6 @@ static OSStatus audioDeviceIOProc(
return 0; return 0;
} }
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque) void *drv_opaque)
{ {
@ -692,7 +687,6 @@ 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,
.run_out = coreaudio_run_out, .run_out = coreaudio_run_out,
.write = coreaudio_write,
.ctl_out = coreaudio_ctl_out .ctl_out = coreaudio_ctl_out
}; };

View File

@ -454,24 +454,20 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
return 0; return 0;
} }
static int dsound_write (SWVoiceOut *sw, void *buf, int len) static size_t dsound_run_out(HWVoiceOut *hw, size_t live)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int dsound_run_out (HWVoiceOut *hw, int live)
{ {
int err; int err;
HRESULT hr; HRESULT hr;
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
int len, hwshift; size_t len;
int hwshift;
DWORD blen1, blen2; DWORD blen1, blen2;
DWORD len1, len2; DWORD len1, len2;
DWORD decr; DWORD decr;
DWORD wpos, ppos, old_pos; DWORD wpos, ppos, old_pos;
LPVOID p1, p2; LPVOID p1, p2;
int bufsize; size_t bufsize;
dsound *s = ds->s; dsound *s = ds->s;
AudiodevDsoundOptions *dso = &s->dev->u.dsound; AudiodevDsoundOptions *dso = &s->dev->u.dsound;
@ -538,8 +534,8 @@ static int dsound_run_out (HWVoiceOut *hw, int live)
} }
} }
if (audio_bug(__func__, len < 0 || len > bufsize)) { if (audio_bug(__func__, len > bufsize)) {
dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n", dolog("len=%zu bufsize=%zu old_pos=%ld ppos=%ld\n",
len, bufsize, old_pos, ppos); len, bufsize, old_pos, ppos);
return 0; return 0;
} }
@ -645,18 +641,13 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
return 0; return 0;
} }
static int dsound_read (SWVoiceIn *sw, void *buf, int len) static size_t dsound_run_in(HWVoiceIn *hw)
{
return audio_pcm_sw_read (sw, buf, len);
}
static int dsound_run_in (HWVoiceIn *hw)
{ {
int err; int err;
HRESULT hr; HRESULT hr;
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer; LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
int live, len, dead; size_t live, len, dead;
DWORD blen1, blen2; DWORD blen1, blen2;
DWORD len1, len2; DWORD len1, len2;
DWORD decr; DWORD decr;
@ -707,7 +698,7 @@ static int dsound_run_in (HWVoiceIn *hw)
if (!len) { if (!len) {
return 0; return 0;
} }
len = audio_MIN (len, dead); len = MIN (len, dead);
err = dsound_lock_in ( err = dsound_lock_in (
dscb, dscb,
@ -856,13 +847,11 @@ 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,
.run_out = dsound_run_out, .run_out = dsound_run_out,
.write = dsound_write,
.ctl_out = dsound_ctl_out, .ctl_out = dsound_ctl_out,
.init_in = dsound_init_in, .init_in = dsound_init_in,
.fini_in = dsound_fini_in, .fini_in = dsound_fini_in,
.run_in = dsound_run_in, .run_in = dsound_run_in,
.read = dsound_read,
.ctl_in = dsound_ctl_in .ctl_in = dsound_ctl_in
}; };

View File

@ -33,6 +33,7 @@ struct st_sample { mixeng_real l; mixeng_real r; };
struct mixeng_volume { int mute; int64_t r; int64_t l; }; struct mixeng_volume { int mute; int64_t r; int64_t l; };
struct st_sample { int64_t l; int64_t r; }; struct st_sample { int64_t l; int64_t r; };
#endif #endif
typedef struct st_sample st_sample;
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
@ -41,10 +42,10 @@ extern t_sample *mixeng_conv[2][2][2][3];
extern f_sample *mixeng_clip[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3];
void *st_rate_start (int inrate, int outrate); void *st_rate_start (int inrate, int outrate);
void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
int *isamp, int *osamp); size_t *isamp, size_t *osamp);
void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
int *isamp, int *osamp); size_t *isamp, size_t *osamp);
void st_rate_stop (void *opaque); void st_rate_stop (void *opaque);
void mixeng_clear (struct st_sample *buf, int len); void mixeng_clear (struct st_sample *buf, int len);
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);

View File

@ -41,10 +41,10 @@ typedef struct NoVoiceIn {
int64_t old_ticks; int64_t old_ticks;
} NoVoiceIn; } NoVoiceIn;
static int no_run_out (HWVoiceOut *hw, int live) static size_t no_run_out(HWVoiceOut *hw, size_t live)
{ {
NoVoiceOut *no = (NoVoiceOut *) hw; NoVoiceOut *no = (NoVoiceOut *) hw;
int decr, samples; size_t decr, samples;
int64_t now; int64_t now;
int64_t ticks; int64_t ticks;
int64_t bytes; int64_t bytes;
@ -52,20 +52,15 @@ static int no_run_out (HWVoiceOut *hw, int live)
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - no->old_ticks; ticks = now - no->old_ticks;
bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
bytes = audio_MIN(bytes, INT_MAX); bytes = MIN(bytes, SIZE_MAX);
samples = bytes >> hw->info.shift; samples = bytes >> hw->info.shift;
no->old_ticks = now; no->old_ticks = now;
decr = audio_MIN (live, samples); decr = MIN (live, samples);
hw->rpos = (hw->rpos + decr) % hw->samples; hw->rpos = (hw->rpos + decr) % hw->samples;
return decr; return decr;
} }
static int no_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write(sw, buf, len);
}
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{ {
audio_pcm_init_info (&hw->info, as); audio_pcm_init_info (&hw->info, as);
@ -97,12 +92,12 @@ static void no_fini_in (HWVoiceIn *hw)
(void) hw; (void) hw;
} }
static int no_run_in (HWVoiceIn *hw) static size_t no_run_in(HWVoiceIn *hw)
{ {
NoVoiceIn *no = (NoVoiceIn *) hw; NoVoiceIn *no = (NoVoiceIn *) hw;
int live = audio_pcm_hw_get_live_in (hw); size_t live = audio_pcm_hw_get_live_in(hw);
int dead = hw->samples - live; size_t dead = hw->samples - live;
int samples = 0; size_t samples = 0;
if (dead) { if (dead) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@ -111,25 +106,13 @@ static int no_run_in (HWVoiceIn *hw)
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND); muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
no->old_ticks = now; no->old_ticks = now;
bytes = audio_MIN (bytes, INT_MAX); bytes = MIN (bytes, SIZE_MAX);
samples = bytes >> hw->info.shift; samples = bytes >> hw->info.shift;
samples = audio_MIN (samples, dead); samples = MIN (samples, dead);
} }
return samples; return samples;
} }
static int no_read (SWVoiceIn *sw, void *buf, int size)
{
/* use custom code here instead of audio_pcm_sw_read() to avoid
* useless resampling/mixing */
int samples = size >> sw->info.shift;
int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
int to_clear = audio_MIN (samples, total);
sw->total_hw_samples_acquired += total;
audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
return to_clear << sw->info.shift;
}
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
{ {
(void) hw; (void) hw;
@ -151,13 +134,11 @@ static struct audio_pcm_ops no_pcm_ops = {
.init_out = no_init_out, .init_out = no_init_out,
.fini_out = no_fini_out, .fini_out = no_fini_out,
.run_out = no_run_out, .run_out = no_run_out,
.write = no_write,
.ctl_out = no_ctl_out, .ctl_out = no_ctl_out,
.init_in = no_init_in, .init_in = no_init_in,
.fini_in = no_fini_in, .fini_in = no_fini_in,
.run_in = no_run_in, .run_in = no_run_in,
.read = no_read,
.ctl_in = no_ctl_in .ctl_in = no_ctl_in
}; };

View File

@ -110,33 +110,28 @@ static void oss_anal_close (int *fdp)
static void oss_helper_poll_out (void *opaque) static void oss_helper_poll_out (void *opaque)
{ {
(void) opaque; AudioState *s = opaque;
audio_run ("oss_poll_out"); audio_run(s, "oss_poll_out");
} }
static void oss_helper_poll_in (void *opaque) static void oss_helper_poll_in (void *opaque)
{ {
(void) opaque; AudioState *s = opaque;
audio_run ("oss_poll_in"); audio_run(s, "oss_poll_in");
} }
static void oss_poll_out (HWVoiceOut *hw) static void oss_poll_out (HWVoiceOut *hw)
{ {
OSSVoiceOut *oss = (OSSVoiceOut *) hw; OSSVoiceOut *oss = (OSSVoiceOut *) hw;
qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
} }
static void oss_poll_in (HWVoiceIn *hw) static void oss_poll_in (HWVoiceIn *hw)
{ {
OSSVoiceIn *oss = (OSSVoiceIn *) hw; OSSVoiceIn *oss = (OSSVoiceIn *) hw;
qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
}
static int oss_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
} }
static int aud_to_ossfmt (AudioFormat fmt, int endianness) static int aud_to_ossfmt (AudioFormat fmt, int endianness)
@ -388,7 +383,7 @@ static void oss_write_pending (OSSVoiceOut *oss)
int samples_written; int samples_written;
ssize_t bytes_written; ssize_t bytes_written;
int samples_till_end = hw->samples - oss->wpos; int samples_till_end = hw->samples - oss->wpos;
int samples_to_write = audio_MIN (oss->pending, samples_till_end); int samples_to_write = MIN (oss->pending, samples_till_end);
int bytes_to_write = samples_to_write << hw->info.shift; int bytes_to_write = samples_to_write << hw->info.shift;
void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift); void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
@ -416,13 +411,14 @@ static void oss_write_pending (OSSVoiceOut *oss)
} }
} }
static int oss_run_out (HWVoiceOut *hw, int live) static size_t oss_run_out(HWVoiceOut *hw, size_t live)
{ {
OSSVoiceOut *oss = (OSSVoiceOut *) hw; OSSVoiceOut *oss = (OSSVoiceOut *) hw;
int err, decr; int err;
size_t decr;
struct audio_buf_info abinfo; struct audio_buf_info abinfo;
struct count_info cntinfo; struct count_info cntinfo;
int bufsize; size_t bufsize;
bufsize = hw->samples << hw->info.shift; bufsize = hw->samples << hw->info.shift;
@ -437,7 +433,7 @@ static int oss_run_out (HWVoiceOut *hw, int live)
pos = hw->rpos << hw->info.shift; pos = hw->rpos << hw->info.shift;
bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize); bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
decr = audio_MIN (bytes >> hw->info.shift, live); decr = MIN (bytes >> hw->info.shift, live);
} }
else { else {
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo); err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
@ -456,7 +452,7 @@ static int oss_run_out (HWVoiceOut *hw, int live)
return 0; return 0;
} }
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live); decr = MIN (abinfo.bytes >> hw->info.shift, live);
if (!decr) { if (!decr) {
return 0; return 0;
} }
@ -481,7 +477,7 @@ static void oss_fini_out (HWVoiceOut *hw)
if (oss->mmapped) { if (oss->mmapped) {
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) { if (err) {
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n", oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
oss->pcm_buf, hw->samples << hw->info.shift); oss->pcm_buf, hw->samples << hw->info.shift);
} }
} }
@ -548,7 +544,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
0 0
); );
if (oss->pcm_buf == MAP_FAILED) { if (oss->pcm_buf == MAP_FAILED) {
oss_logerr (errno, "Failed to map %d bytes of DAC\n", oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
hw->samples << hw->info.shift); hw->samples << hw->info.shift);
} }
else { else {
@ -573,7 +569,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
if (!oss->mmapped) { if (!oss->mmapped) {
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift); err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) { if (err) {
oss_logerr (errno, "Failed to unmap buffer %p size %d\n", oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
oss->pcm_buf, hw->samples << hw->info.shift); oss->pcm_buf, hw->samples << hw->info.shift);
} }
} }
@ -586,7 +582,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
1 << hw->info.shift); 1 << hw->info.shift);
if (!oss->pcm_buf) { if (!oss->pcm_buf) {
dolog ( dolog (
"Could not allocate DAC buffer (%d samples, each %d bytes)\n", "Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
hw->samples, hw->samples,
1 << hw->info.shift 1 << hw->info.shift
); );
@ -698,7 +694,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!oss->pcm_buf) { if (!oss->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift); hw->samples, 1 << hw->info.shift);
oss_anal_close (&fd); oss_anal_close (&fd);
return -1; return -1;
@ -719,17 +715,17 @@ static void oss_fini_in (HWVoiceIn *hw)
oss->pcm_buf = NULL; oss->pcm_buf = NULL;
} }
static int oss_run_in (HWVoiceIn *hw) static size_t oss_run_in(HWVoiceIn *hw)
{ {
OSSVoiceIn *oss = (OSSVoiceIn *) hw; OSSVoiceIn *oss = (OSSVoiceIn *) hw;
int hwshift = hw->info.shift; int hwshift = hw->info.shift;
int i; int i;
int live = audio_pcm_hw_get_live_in (hw); size_t live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live; size_t dead = hw->samples - live;
size_t read_samples = 0; size_t read_samples = 0;
struct { struct {
int add; size_t add;
int len; size_t len;
} bufs[2] = { } bufs[2] = {
{ .add = hw->wpos, .len = 0 }, { .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 } { .add = 0, .len = 0 }
@ -756,7 +752,7 @@ static int oss_run_in (HWVoiceIn *hw)
if (nread > 0) { if (nread > 0) {
if (nread & hw->info.align) { if (nread & hw->info.align) {
dolog ("warning: Misaligned read %zd (requested %d), " dolog("warning: Misaligned read %zd (requested %zu), "
"alignment %d\n", nread, bufs[i].add << hwshift, "alignment %d\n", nread, bufs[i].add << hwshift,
hw->info.align + 1); hw->info.align + 1);
} }
@ -773,7 +769,7 @@ static int oss_run_in (HWVoiceIn *hw)
default: default:
oss_logerr( oss_logerr(
errno, errno,
"Failed to read %d bytes of audio (to %p)\n", "Failed to read %zu bytes of audio (to %p)\n",
bufs[i].len, p bufs[i].len, p
); );
break; break;
@ -788,11 +784,6 @@ static int oss_run_in (HWVoiceIn *hw)
return read_samples; return read_samples;
} }
static int oss_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, 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;
@ -855,13 +846,11 @@ 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,
.run_out = oss_run_out, .run_out = oss_run_out,
.write = oss_write,
.ctl_out = oss_ctl_out, .ctl_out = oss_ctl_out,
.init_in = oss_init_in, .init_in = oss_init_in,
.fini_in = oss_fini_in, .fini_in = oss_fini_in,
.run_in = oss_run_in, .run_in = oss_run_in,
.read = oss_read,
.ctl_in = oss_ctl_in .ctl_in = oss_ctl_in
}; };

View File

@ -11,41 +11,52 @@
#include "audio_int.h" #include "audio_int.h"
#include "audio_pt_int.h" #include "audio_pt_int.h"
typedef struct { typedef struct PAConnection {
Audiodev *dev; char *server;
int refcount;
QTAILQ_ENTRY(PAConnection) list;
pa_threaded_mainloop *mainloop; pa_threaded_mainloop *mainloop;
pa_context *context; pa_context *context;
} PAConnection;
static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
QTAILQ_HEAD_INITIALIZER(pa_conns);
typedef struct {
Audiodev *dev;
PAConnection *conn;
} paaudio; } paaudio;
typedef struct { typedef struct {
HWVoiceOut hw; HWVoiceOut hw;
int done; size_t done;
int live; size_t live;
int decr; size_t decr;
int rpos; size_t rpos;
pa_stream *stream; pa_stream *stream;
void *pcm_buf; void *pcm_buf;
struct audio_pt pt; struct audio_pt pt;
paaudio *g; paaudio *g;
int samples; size_t samples;
} PAVoiceOut; } PAVoiceOut;
typedef struct { typedef struct {
HWVoiceIn hw; HWVoiceIn hw;
int done; size_t done;
int dead; size_t dead;
int incr; size_t incr;
int wpos; size_t wpos;
pa_stream *stream; pa_stream *stream;
void *pcm_buf; void *pcm_buf;
struct audio_pt pt; struct audio_pt pt;
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; size_t samples;
} PAVoiceIn; } PAVoiceIn;
static void qpa_audio_fini(void *opaque); static void qpa_conn_fini(PAConnection *c);
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
{ {
@ -108,11 +119,11 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror) static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
{ {
paaudio *g = p->g; PAConnection *c = p->g->conn;
pa_threaded_mainloop_lock (g->mainloop); pa_threaded_mainloop_lock(c->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
while (length > 0) { while (length > 0) {
size_t l; size_t l;
@ -121,11 +132,11 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror
int r; int r;
r = pa_stream_peek (p->stream, &p->read_data, &p->read_length); r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
if (!p->read_data) { if (!p->read_data) {
pa_threaded_mainloop_wait (g->mainloop); pa_threaded_mainloop_wait(c->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
} else { } else {
p->read_index = 0; p->read_index = 0;
} }
@ -148,53 +159,53 @@ static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror
p->read_length = 0; p->read_length = 0;
p->read_index = 0; p->read_index = 0;
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail); CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
} }
} }
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
return 0; return 0;
unlock_and_fail: unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
return -1; return -1;
} }
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror) static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
{ {
paaudio *g = p->g; PAConnection *c = p->g->conn;
pa_threaded_mainloop_lock (g->mainloop); pa_threaded_mainloop_lock(c->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
while (length > 0) { while (length > 0) {
size_t l; size_t l;
int r; int r;
while (!(l = pa_stream_writable_size (p->stream))) { while (!(l = pa_stream_writable_size (p->stream))) {
pa_threaded_mainloop_wait (g->mainloop); pa_threaded_mainloop_wait(c->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail); CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
} }
CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail); CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
if (l > length) { if (l > length) {
l = length; l = length;
} }
r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE); r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail); CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
data = (const uint8_t *) data + l; data = (const uint8_t *) data + l;
length -= l; length -= l;
} }
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
return 0; return 0;
unlock_and_fail: unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
return -1; return -1;
} }
@ -208,7 +219,7 @@ static void *qpa_thread_out (void *arg)
} }
for (;;) { for (;;) {
int decr, to_mix, rpos; size_t decr, to_mix, rpos;
for (;;) { for (;;) {
if (pa->done) { if (pa->done) {
@ -224,7 +235,7 @@ static void *qpa_thread_out (void *arg)
} }
} }
decr = to_mix = audio_MIN(pa->live, pa->samples >> 5); decr = to_mix = 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__)) {
@ -233,7 +244,7 @@ static void *qpa_thread_out (void *arg)
while (to_mix) { while (to_mix) {
int error; int error;
int chunk = audio_MIN (to_mix, hw->samples - rpos); size_t chunk = MIN (to_mix, hw->samples - rpos);
struct st_sample *src = hw->mix_buf + rpos; struct st_sample *src = hw->mix_buf + rpos;
hw->clip (pa->pcm_buf, src, chunk); hw->clip (pa->pcm_buf, src, chunk);
@ -262,16 +273,16 @@ static void *qpa_thread_out (void *arg)
return NULL; return NULL;
} }
static int qpa_run_out (HWVoiceOut *hw, int live) static size_t qpa_run_out(HWVoiceOut *hw, size_t live)
{ {
int decr; size_t decr;
PAVoiceOut *pa = (PAVoiceOut *) hw; PAVoiceOut *pa = (PAVoiceOut *) hw;
if (audio_pt_lock(&pa->pt, __func__)) { if (audio_pt_lock(&pa->pt, __func__)) {
return 0; return 0;
} }
decr = audio_MIN (live, pa->decr); decr = MIN (live, pa->decr);
pa->decr -= decr; pa->decr -= decr;
pa->live = live - decr; pa->live = live - decr;
hw->rpos = pa->rpos; hw->rpos = pa->rpos;
@ -284,11 +295,6 @@ static int qpa_run_out (HWVoiceOut *hw, int live)
return decr; return decr;
} }
static int qpa_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
/* capture */ /* capture */
static void *qpa_thread_in (void *arg) static void *qpa_thread_in (void *arg)
{ {
@ -300,7 +306,7 @@ static void *qpa_thread_in (void *arg)
} }
for (;;) { for (;;) {
int incr, to_grab, wpos; size_t incr, to_grab, wpos;
for (;;) { for (;;) {
if (pa->done) { if (pa->done) {
@ -316,7 +322,7 @@ static void *qpa_thread_in (void *arg)
} }
} }
incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5); incr = to_grab = 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__)) {
@ -325,7 +331,7 @@ static void *qpa_thread_in (void *arg)
while (to_grab) { while (to_grab) {
int error; int error;
int chunk = audio_MIN (to_grab, hw->samples - wpos); size_t chunk = MIN (to_grab, hw->samples - wpos);
void *buf = advance (pa->pcm_buf, wpos); void *buf = advance (pa->pcm_buf, wpos);
if (qpa_simple_read (pa, buf, if (qpa_simple_read (pa, buf,
@ -353,9 +359,9 @@ static void *qpa_thread_in (void *arg)
return NULL; return NULL;
} }
static int qpa_run_in (HWVoiceIn *hw) static size_t qpa_run_in(HWVoiceIn *hw)
{ {
int live, incr, dead; size_t live, incr, dead;
PAVoiceIn *pa = (PAVoiceIn *) hw; PAVoiceIn *pa = (PAVoiceIn *) hw;
if (audio_pt_lock(&pa->pt, __func__)) { if (audio_pt_lock(&pa->pt, __func__)) {
@ -364,7 +370,7 @@ static int qpa_run_in (HWVoiceIn *hw)
live = audio_pcm_hw_get_live_in (hw); live = audio_pcm_hw_get_live_in (hw);
dead = hw->samples - live; dead = hw->samples - live;
incr = audio_MIN (dead, pa->incr); incr = MIN (dead, pa->incr);
pa->incr -= incr; pa->incr -= incr;
pa->dead = dead - incr; pa->dead = dead - incr;
hw->wpos = pa->wpos; hw->wpos = pa->wpos;
@ -377,11 +383,6 @@ static int qpa_run_in (HWVoiceIn *hw)
return incr; return incr;
} }
static int qpa_read (SWVoiceIn *sw, void *buf, int len)
{
return audio_pcm_sw_read (sw, buf, len);
}
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
{ {
int format; int format;
@ -432,13 +433,13 @@ static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
static void context_state_cb (pa_context *c, void *userdata) static void context_state_cb (pa_context *c, void *userdata)
{ {
paaudio *g = userdata; PAConnection *conn = userdata;
switch (pa_context_get_state(c)) { switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED: case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal (g->mainloop, 0); pa_threaded_mainloop_signal(conn->mainloop, 0);
break; break;
case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_UNCONNECTED:
@ -451,14 +452,14 @@ static void context_state_cb (pa_context *c, void *userdata)
static void stream_state_cb (pa_stream *s, void * userdata) static void stream_state_cb (pa_stream *s, void * userdata)
{ {
paaudio *g = userdata; PAConnection *c = userdata;
switch (pa_stream_get_state (s)) { switch (pa_stream_get_state (s)) {
case PA_STREAM_READY: case PA_STREAM_READY:
case PA_STREAM_FAILED: case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED: case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal (g->mainloop, 0); pa_threaded_mainloop_signal(c->mainloop, 0);
break; break;
case PA_STREAM_UNCONNECTED: case PA_STREAM_UNCONNECTED:
@ -469,13 +470,13 @@ static void stream_state_cb (pa_stream *s, void * userdata)
static void stream_request_cb (pa_stream *s, size_t length, void *userdata) static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
{ {
paaudio *g = userdata; PAConnection *c = userdata;
pa_threaded_mainloop_signal (g->mainloop, 0); pa_threaded_mainloop_signal(c->mainloop, 0);
} }
static pa_stream *qpa_simple_new ( static pa_stream *qpa_simple_new (
paaudio *g, PAConnection *c,
const char *name, const char *name,
pa_stream_direction_t dir, pa_stream_direction_t dir,
const char *dev, const char *dev,
@ -486,50 +487,51 @@ static pa_stream *qpa_simple_new (
{ {
int r; int r;
pa_stream *stream; pa_stream *stream;
pa_stream_flags_t flags;
pa_threaded_mainloop_lock (g->mainloop); pa_threaded_mainloop_lock(c->mainloop);
stream = pa_stream_new (g->context, name, ss, map); stream = pa_stream_new(c->context, name, ss, map);
if (!stream) { if (!stream) {
goto fail; goto fail;
} }
pa_stream_set_state_callback (stream, stream_state_cb, g); pa_stream_set_state_callback(stream, stream_state_cb, c);
pa_stream_set_read_callback (stream, stream_request_cb, g); pa_stream_set_read_callback(stream, stream_request_cb, c);
pa_stream_set_write_callback (stream, stream_request_cb, g); pa_stream_set_write_callback(stream, stream_request_cb, c);
flags =
PA_STREAM_INTERPOLATE_TIMING
| PA_STREAM_AUTO_TIMING_UPDATE
| PA_STREAM_EARLY_REQUESTS;
if (dev) {
/* don't move the stream if the user specified a sink/source */
flags |= PA_STREAM_DONT_MOVE;
}
if (dir == PA_STREAM_PLAYBACK) { if (dir == PA_STREAM_PLAYBACK) {
r = pa_stream_connect_playback (stream, dev, attr, r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
PA_STREAM_INTERPOLATE_TIMING
#ifdef PA_STREAM_ADJUST_LATENCY
|PA_STREAM_ADJUST_LATENCY
#endif
|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
} else { } else {
r = pa_stream_connect_record (stream, dev, attr, r = pa_stream_connect_record(stream, dev, attr, flags);
PA_STREAM_INTERPOLATE_TIMING
#ifdef PA_STREAM_ADJUST_LATENCY
|PA_STREAM_ADJUST_LATENCY
#endif
|PA_STREAM_AUTO_TIMING_UPDATE);
} }
if (r < 0) { if (r < 0) {
goto fail; goto fail;
} }
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
return stream; return stream;
fail: fail:
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
if (stream) { if (stream) {
pa_stream_unref (stream); pa_stream_unref (stream);
} }
*rerror = pa_context_errno (g->context); *rerror = pa_context_errno(c->context);
return NULL; return NULL;
} }
@ -545,6 +547,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
paaudio *g = pa->g = drv_opaque; paaudio *g = pa->g = drv_opaque;
AudiodevPaOptions *popts = &g->dev->u.pa; AudiodevPaOptions *popts = &g->dev->u.pa;
AudiodevPaPerDirectionOptions *ppdo = popts->out; AudiodevPaPerDirectionOptions *ppdo = popts->out;
PAConnection *c = g->conn;
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;
@ -558,7 +561,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new ( pa->stream = qpa_simple_new (
g, c,
"qemu", "qemu",
PA_STREAM_PLAYBACK, PA_STREAM_PLAYBACK,
ppdo->has_name ? ppdo->name : NULL, ppdo->has_name ? ppdo->name : NULL,
@ -579,7 +582,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
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) {
dolog ("Could not allocate buffer (%d bytes)\n", dolog("Could not allocate buffer (%zu bytes)\n",
hw->samples << hw->info.shift); hw->samples << hw->info.shift);
goto fail2; goto fail2;
} }
@ -612,6 +615,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
paaudio *g = pa->g = drv_opaque; paaudio *g = pa->g = drv_opaque;
AudiodevPaOptions *popts = &g->dev->u.pa; AudiodevPaOptions *popts = &g->dev->u.pa;
AudiodevPaPerDirectionOptions *ppdo = popts->in; AudiodevPaPerDirectionOptions *ppdo = popts->in;
PAConnection *c = g->conn;
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;
@ -625,7 +629,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new ( pa->stream = qpa_simple_new (
g, c,
"qemu", "qemu",
PA_STREAM_RECORD, PA_STREAM_RECORD,
ppdo->has_name ? ppdo->name : NULL, ppdo->has_name ? ppdo->name : NULL,
@ -646,7 +650,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
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) {
dolog ("Could not allocate buffer (%d bytes)\n", dolog("Could not allocate buffer (%zu bytes)\n",
hw->samples << hw->info.shift); hw->samples << hw->info.shift);
goto fail2; goto fail2;
} }
@ -669,6 +673,27 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
return -1; return -1;
} }
static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
{
int err;
pa_threaded_mainloop_lock(c->mainloop);
/*
* wait until actually connects. workaround pa bug #247
* https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247
*/
while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
pa_threaded_mainloop_wait(c->mainloop);
}
err = pa_stream_disconnect(stream);
if (err != 0) {
dolog("Failed to disconnect! err=%d\n", err);
}
pa_stream_unref(stream);
pa_threaded_mainloop_unlock(c->mainloop);
}
static void qpa_fini_out (HWVoiceOut *hw) static void qpa_fini_out (HWVoiceOut *hw)
{ {
void *ret; void *ret;
@ -680,7 +705,7 @@ static void qpa_fini_out (HWVoiceOut *hw)
audio_pt_join(&pa->pt, &ret, __func__); audio_pt_join(&pa->pt, &ret, __func__);
if (pa->stream) { if (pa->stream) {
pa_stream_unref (pa->stream); qpa_simple_disconnect(pa->g->conn, pa->stream);
pa->stream = NULL; pa->stream = NULL;
} }
@ -700,7 +725,7 @@ static void qpa_fini_in (HWVoiceIn *hw)
audio_pt_join(&pa->pt, &ret, __func__); audio_pt_join(&pa->pt, &ret, __func__);
if (pa->stream) { if (pa->stream) {
pa_stream_unref (pa->stream); qpa_simple_disconnect(pa->g->conn, pa->stream);
pa->stream = NULL; pa->stream = NULL;
} }
@ -714,7 +739,7 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
PAVoiceOut *pa = (PAVoiceOut *) hw; PAVoiceOut *pa = (PAVoiceOut *) hw;
pa_operation *op; pa_operation *op;
pa_cvolume v; pa_cvolume v;
paaudio *g = pa->g; PAConnection *c = pa->g->conn;
#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */ #ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
pa_cvolume_init (&v); /* function is present in 0.9.13+ */ pa_cvolume_init (&v); /* function is present in 0.9.13+ */
@ -734,28 +759,29 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
pa_threaded_mainloop_lock (g->mainloop); pa_threaded_mainloop_lock(c->mainloop);
op = pa_context_set_sink_input_volume (g->context, op = pa_context_set_sink_input_volume(c->context,
pa_stream_get_index (pa->stream), pa_stream_get_index (pa->stream),
&v, NULL, NULL); &v, NULL, NULL);
if (!op) if (!op) {
qpa_logerr (pa_context_errno (g->context), qpa_logerr(pa_context_errno(c->context),
"set_sink_input_volume() failed\n"); "set_sink_input_volume() failed\n");
else } else {
pa_operation_unref(op); pa_operation_unref(op);
}
op = pa_context_set_sink_input_mute (g->context, op = pa_context_set_sink_input_mute(c->context,
pa_stream_get_index (pa->stream), pa_stream_get_index (pa->stream),
sw->vol.mute, NULL, NULL); sw->vol.mute, NULL, NULL);
if (!op) { if (!op) {
qpa_logerr (pa_context_errno (g->context), qpa_logerr(pa_context_errno(c->context),
"set_sink_input_mute() failed\n"); "set_sink_input_mute() failed\n");
} else { } else {
pa_operation_unref(op); pa_operation_unref(op);
} }
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
} }
} }
return 0; return 0;
@ -766,7 +792,7 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
PAVoiceIn *pa = (PAVoiceIn *) hw; PAVoiceIn *pa = (PAVoiceIn *) hw;
pa_operation *op; pa_operation *op;
pa_cvolume v; pa_cvolume v;
paaudio *g = pa->g; PAConnection *c = pa->g->conn;
#ifdef PA_CHECK_VERSION #ifdef PA_CHECK_VERSION
pa_cvolume_init (&v); pa_cvolume_init (&v);
@ -786,29 +812,29 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX; v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX; v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
pa_threaded_mainloop_lock (g->mainloop); pa_threaded_mainloop_lock(c->mainloop);
op = pa_context_set_source_output_volume (g->context, op = pa_context_set_source_output_volume(c->context,
pa_stream_get_index(pa->stream), pa_stream_get_index(pa->stream),
&v, NULL, NULL); &v, NULL, NULL);
if (!op) { if (!op) {
qpa_logerr (pa_context_errno (g->context), qpa_logerr(pa_context_errno(c->context),
"set_source_output_volume() failed\n"); "set_source_output_volume() failed\n");
} else { } else {
pa_operation_unref(op); pa_operation_unref(op);
} }
op = pa_context_set_source_output_mute (g->context, op = pa_context_set_source_output_mute(c->context,
pa_stream_get_index (pa->stream), pa_stream_get_index (pa->stream),
sw->vol.mute, NULL, NULL); sw->vol.mute, NULL, NULL);
if (!op) { if (!op) {
qpa_logerr (pa_context_errno (g->context), qpa_logerr(pa_context_errno(c->context),
"set_source_output_mute() failed\n"); "set_source_output_mute() failed\n");
} else { } else {
pa_operation_unref (op); pa_operation_unref (op);
} }
pa_threaded_mainloop_unlock (g->mainloop); pa_threaded_mainloop_unlock(c->mainloop);
} }
} }
return 0; return 0;
@ -828,11 +854,75 @@ static int qpa_validate_per_direction_opts(Audiodev *dev,
return 1; return 1;
} }
/* common */
static void *qpa_conn_init(const char *server)
{
PAConnection *c = g_malloc0(sizeof(PAConnection));
QTAILQ_INSERT_TAIL(&pa_conns, c, list);
c->mainloop = pa_threaded_mainloop_new();
if (!c->mainloop) {
goto fail;
}
c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
server);
if (!c->context) {
goto fail;
}
pa_context_set_state_callback(c->context, context_state_cb, c);
if (pa_context_connect(c->context, server, 0, NULL) < 0) {
qpa_logerr(pa_context_errno(c->context),
"pa_context_connect() failed\n");
goto fail;
}
pa_threaded_mainloop_lock(c->mainloop);
if (pa_threaded_mainloop_start(c->mainloop) < 0) {
goto unlock_and_fail;
}
for (;;) {
pa_context_state_t state;
state = pa_context_get_state(c->context);
if (state == PA_CONTEXT_READY) {
break;
}
if (!PA_CONTEXT_IS_GOOD(state)) {
qpa_logerr(pa_context_errno(c->context),
"Wrong context state\n");
goto unlock_and_fail;
}
/* Wait until the context is ready */
pa_threaded_mainloop_wait(c->mainloop);
}
pa_threaded_mainloop_unlock(c->mainloop);
return c;
unlock_and_fail:
pa_threaded_mainloop_unlock(c->mainloop);
fail:
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
qpa_conn_fini(c);
return NULL;
}
static void *qpa_audio_init(Audiodev *dev) static void *qpa_audio_init(Audiodev *dev)
{ {
paaudio *g; paaudio *g;
AudiodevPaOptions *popts = &dev->u.pa; AudiodevPaOptions *popts = &dev->u.pa;
const char *server; const char *server;
PAConnection *c;
assert(dev->driver == AUDIODEV_DRIVER_PA);
if (!popts->has_server) { if (!popts->has_server) {
char pidfile[64]; char pidfile[64];
@ -849,93 +939,64 @@ static void *qpa_audio_init(Audiodev *dev)
} }
} }
assert(dev->driver == AUDIODEV_DRIVER_PA);
g = g_malloc(sizeof(paaudio));
server = popts->has_server ? popts->server : NULL;
if (!qpa_validate_per_direction_opts(dev, popts->in)) { if (!qpa_validate_per_direction_opts(dev, popts->in)) {
goto fail; return NULL;
} }
if (!qpa_validate_per_direction_opts(dev, popts->out)) { if (!qpa_validate_per_direction_opts(dev, popts->out)) {
goto fail; return NULL;
} }
g = g_malloc0(sizeof(paaudio));
server = popts->has_server ? popts->server : NULL;
g->dev = dev; g->dev = dev;
g->mainloop = NULL;
g->context = NULL;
g->mainloop = pa_threaded_mainloop_new (); QTAILQ_FOREACH(c, &pa_conns, list) {
if (!g->mainloop) { if (server == NULL || c->server == NULL ?
goto fail; server == c->server :
} strcmp(server, c->server) == 0) {
g->conn = c;
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop),
server);
if (!g->context) {
goto fail;
}
pa_context_set_state_callback (g->context, context_state_cb, g);
if (pa_context_connect(g->context, server, 0, NULL) < 0) {
qpa_logerr (pa_context_errno (g->context),
"pa_context_connect() failed\n");
goto fail;
}
pa_threaded_mainloop_lock (g->mainloop);
if (pa_threaded_mainloop_start (g->mainloop) < 0) {
goto unlock_and_fail;
}
for (;;) {
pa_context_state_t state;
state = pa_context_get_state (g->context);
if (state == PA_CONTEXT_READY) {
break; break;
} }
if (!PA_CONTEXT_IS_GOOD (state)) {
qpa_logerr (pa_context_errno (g->context),
"Wrong context state\n");
goto unlock_and_fail;
} }
if (!g->conn) {
/* Wait until the context is ready */ g->conn = qpa_conn_init(server);
pa_threaded_mainloop_wait (g->mainloop);
} }
if (!g->conn) {
pa_threaded_mainloop_unlock (g->mainloop); g_free(g);
return g;
unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
fail:
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
qpa_audio_fini(g);
return NULL; return NULL;
} }
++g->conn->refcount;
return g;
}
static void qpa_conn_fini(PAConnection *c)
{
if (c->mainloop) {
pa_threaded_mainloop_stop(c->mainloop);
}
if (c->context) {
pa_context_disconnect(c->context);
pa_context_unref(c->context);
}
if (c->mainloop) {
pa_threaded_mainloop_free(c->mainloop);
}
QTAILQ_REMOVE(&pa_conns, c, list);
g_free(c);
}
static void qpa_audio_fini (void *opaque) static void qpa_audio_fini (void *opaque)
{ {
paaudio *g = opaque; paaudio *g = opaque;
PAConnection *c = g->conn;
if (g->mainloop) { if (--c->refcount == 0) {
pa_threaded_mainloop_stop (g->mainloop); qpa_conn_fini(c);
}
if (g->context) {
pa_context_disconnect (g->context);
pa_context_unref (g->context);
}
if (g->mainloop) {
pa_threaded_mainloop_free (g->mainloop);
} }
g_free(g); g_free(g);
@ -945,13 +1006,11 @@ 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,
.run_out = qpa_run_out, .run_out = qpa_run_out,
.write = qpa_write,
.ctl_out = qpa_ctl_out, .ctl_out = qpa_ctl_out,
.init_in = qpa_init_in, .init_in = qpa_init_in,
.fini_in = qpa_fini_in, .fini_in = qpa_fini_in,
.run_in = qpa_run_in, .run_in = qpa_run_in,
.read = qpa_read,
.ctl_in = qpa_ctl_in .ctl_in = qpa_ctl_in
}; };

View File

@ -28,7 +28,7 @@
* Return number of samples processed. * Return number of samples processed.
*/ */
void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf, void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
int *isamp, int *osamp) size_t *isamp, size_t *osamp)
{ {
struct rate *rate = opaque; struct rate *rate = opaque;
struct st_sample *istart, *iend; struct st_sample *istart, *iend;

View File

@ -41,8 +41,8 @@
typedef struct SDLVoiceOut { typedef struct SDLVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
int live; size_t live;
int decr; size_t decr;
} SDLVoiceOut; } SDLVoiceOut;
static struct SDLAudioState { static struct SDLAudioState {
@ -184,22 +184,22 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLVoiceOut *sdl = opaque; SDLVoiceOut *sdl = opaque;
SDLAudioState *s = &glob_sdl; SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw; HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift; size_t samples = len >> hw->info.shift;
int to_mix, decr; size_t to_mix, decr;
if (s->exit || !sdl->live) { if (s->exit || !sdl->live) {
return; return;
} }
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */ /* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */
to_mix = audio_MIN(samples, sdl->live); to_mix = MIN(samples, sdl->live);
decr = to_mix; decr = to_mix;
while (to_mix) { while (to_mix) {
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); size_t chunk = MIN(to_mix, hw->samples - hw->rpos);
struct st_sample *src = hw->mix_buf + hw->rpos; struct st_sample *src = hw->mix_buf + hw->rpos;
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ /* dolog ("in callback to_mix %zu, chunk %zu\n", to_mix, chunk); */
hw->clip(buf, src, chunk); hw->clip(buf, src, chunk);
hw->rpos = (hw->rpos + chunk) % hw->samples; hw->rpos = (hw->rpos + chunk) % hw->samples;
to_mix -= chunk; to_mix -= chunk;
@ -209,7 +209,7 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
sdl->live -= decr; sdl->live -= decr;
sdl->decr += decr; sdl->decr += decr;
/* dolog ("done len=%d\n", len); */ /* dolog ("done len=%zu\n", len); */
/* SDL2 does not clear the remaining buffer for us, so do it on our own */ /* SDL2 does not clear the remaining buffer for us, so do it on our own */
if (samples) { if (samples) {
@ -217,14 +217,9 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
} }
} }
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) static size_t sdl_run_out(HWVoiceOut *hw, size_t live)
{ {
return audio_pcm_sw_write (sw, buf, len); size_t decr;
}
static int sdl_run_out (HWVoiceOut *hw, int live)
{
int decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw; SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDL_LockAudio(); SDL_LockAudio();
@ -236,7 +231,7 @@ static int sdl_run_out (HWVoiceOut *hw, int live)
sdl->live); sdl->live);
} }
decr = audio_MIN (sdl->decr, live); decr = MIN (sdl->decr, live);
sdl->decr -= decr; sdl->decr -= decr;
sdl->live = live; sdl->live = live;
@ -342,7 +337,6 @@ 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,
.run_out = sdl_run_out, .run_out = sdl_run_out,
.write = sdl_write_out,
.ctl_out = sdl_ctl_out, .ctl_out = sdl_ctl_out,
}; };

View File

@ -152,31 +152,31 @@ static void line_out_fini (HWVoiceOut *hw)
spice_server_remove_interface (&out->sin.base); spice_server_remove_interface (&out->sin.base);
} }
static int line_out_run (HWVoiceOut *hw, int live) static size_t line_out_run (HWVoiceOut *hw, size_t live)
{ {
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
int rpos, decr; size_t rpos, decr;
int samples; size_t samples;
if (!live) { if (!live) {
return 0; return 0;
} }
decr = rate_get_samples (&hw->info, &out->rate); decr = rate_get_samples (&hw->info, &out->rate);
decr = audio_MIN (live, decr); decr = MIN (live, decr);
samples = decr; samples = decr;
rpos = hw->rpos; rpos = hw->rpos;
while (samples) { while (samples) {
int left_till_end_samples = hw->samples - rpos; int left_till_end_samples = hw->samples - rpos;
int len = audio_MIN (samples, left_till_end_samples); int len = MIN (samples, left_till_end_samples);
if (!out->frame) { if (!out->frame) {
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
out->fpos = out->frame; out->fpos = out->frame;
} }
if (out->frame) { if (out->frame) {
len = audio_MIN (len, out->fsize); len = MIN (len, out->fsize);
hw->clip (out->fpos, hw->mix_buf + rpos, len); hw->clip (out->fpos, hw->mix_buf + rpos, len);
out->fsize -= len; out->fsize -= len;
out->fpos += len; out->fpos += len;
@ -192,11 +192,6 @@ static int line_out_run (HWVoiceOut *hw, int live)
return decr; return decr;
} }
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
{ {
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
@ -280,12 +275,12 @@ static void line_in_fini (HWVoiceIn *hw)
spice_server_remove_interface (&in->sin.base); spice_server_remove_interface (&in->sin.base);
} }
static int line_in_run (HWVoiceIn *hw) static size_t line_in_run(HWVoiceIn *hw)
{ {
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
int num_samples; size_t num_samples;
int ready; int ready;
int len[2]; size_t len[2];
uint64_t delta_samp; uint64_t delta_samp;
const uint32_t *samples; const uint32_t *samples;
@ -294,7 +289,7 @@ static int line_in_run (HWVoiceIn *hw)
} }
delta_samp = rate_get_samples (&hw->info, &in->rate); delta_samp = rate_get_samples (&hw->info, &in->rate);
num_samples = audio_MIN (num_samples, delta_samp); num_samples = MIN (num_samples, delta_samp);
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples); ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
samples = in->samples; samples = in->samples;
@ -304,7 +299,7 @@ static int line_in_run (HWVoiceIn *hw)
ready = LINE_IN_SAMPLES; ready = LINE_IN_SAMPLES;
} }
num_samples = audio_MIN (ready, num_samples); num_samples = MIN (ready, num_samples);
if (hw->wpos + num_samples > hw->samples) { if (hw->wpos + num_samples > hw->samples) {
len[0] = hw->samples - hw->wpos; len[0] = hw->samples - hw->wpos;
@ -325,11 +320,6 @@ static int line_in_run (HWVoiceIn *hw)
return num_samples; return num_samples;
} }
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
{ {
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
@ -377,13 +367,11 @@ 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,
.run_out = line_out_run, .run_out = line_out_run,
.write = line_out_write,
.ctl_out = line_out_ctl, .ctl_out = line_out_ctl,
.init_in = line_in_init, .init_in = line_in_init,
.fini_in = line_in_fini, .fini_in = line_in_fini,
.run_in = line_in_run, .run_in = line_in_run,
.read = line_in_read,
.ctl_in = line_in_ctl, .ctl_in = line_in_ctl,
}; };

View File

@ -40,10 +40,10 @@ typedef struct WAVVoiceOut {
int total_samples; int total_samples;
} WAVVoiceOut; } WAVVoiceOut;
static int wav_run_out (HWVoiceOut *hw, int live) static size_t wav_run_out(HWVoiceOut *hw, size_t live)
{ {
WAVVoiceOut *wav = (WAVVoiceOut *) hw; WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int rpos, decr, samples; size_t rpos, decr, samples;
uint8_t *dst; uint8_t *dst;
struct st_sample *src; struct st_sample *src;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
@ -59,12 +59,12 @@ static int wav_run_out (HWVoiceOut *hw, int live)
} }
wav->old_ticks = now; wav->old_ticks = now;
decr = audio_MIN (live, samples); decr = MIN (live, samples);
samples = decr; samples = decr;
rpos = hw->rpos; rpos = hw->rpos;
while (samples) { while (samples) {
int left_till_end_samples = hw->samples - rpos; int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples); int convert_samples = MIN (samples, left_till_end_samples);
src = hw->mix_buf + rpos; src = hw->mix_buf + rpos;
dst = advance (wav->pcm_buf, rpos << hw->info.shift); dst = advance (wav->pcm_buf, rpos << hw->info.shift);
@ -84,11 +84,6 @@ static int wav_run_out (HWVoiceOut *hw, int live)
return decr; return decr;
} }
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
/* VICE code: Store number as little endian. */ /* VICE code: Store number as little endian. */
static void le_store (uint8_t *buf, uint32_t val, int len) static void le_store (uint8_t *buf, uint32_t val, int len)
{ {
@ -144,7 +139,7 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
hw->samples = 1024; hw->samples = 1024;
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!wav->pcm_buf) { if (!wav->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n", dolog("Could not allocate buffer (%zu bytes)\n",
hw->samples << hw->info.shift); hw->samples << hw->info.shift);
return -1; return -1;
} }
@ -240,7 +235,6 @@ 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,
.run_out = wav_run_out, .run_out = wav_run_out,
.write = wav_write_out,
.ctl_out = wav_ctl_out, .ctl_out = wav_ctl_out,
}; };

View File

@ -104,8 +104,8 @@ static struct capture_ops wav_capture_ops = {
.info = wav_capture_info .info = wav_capture_info
}; };
int wav_start_capture (CaptureState *s, const char *path, int freq, int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
int bits, int nchannels) int freq, int bits, int nchannels)
{ {
WAVState *wav; WAVState *wav;
uint8_t hdr[] = { uint8_t hdr[] = {
@ -170,7 +170,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
goto error_free; goto error_free;
} }
cap = AUD_add_capture (&as, &ops, wav); cap = AUD_add_capture(state, &as, &ops, wav);
if (!cap) { if (!cap) {
error_report("Failed to add audio capture"); error_report("Failed to add audio capture");
goto error_free; goto error_free;

View File

@ -819,16 +819,17 @@ ETEXI
{ {
.name = "wavcapture", .name = "wavcapture",
.args_type = "path:F,freq:i?,bits:i?,nchannels:i?", .args_type = "path:F,audiodev:s,freq:i?,bits:i?,nchannels:i?",
.params = "path [frequency [bits [channels]]]", .params = "path audiodev [frequency [bits [channels]]]",
.help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)", .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)",
.cmd = hmp_wavcapture, .cmd = hmp_wavcapture,
}, },
STEXI STEXI
@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]] @item wavcapture @var{filename} @var{audiodev} [@var{frequency} [@var{bits} [@var{channels}]]]
@findex wavcapture @findex wavcapture
Capture audio into @var{filename}. Using sample rate @var{frequency} Capture audio into @var{filename} from @var{audiodev}, using sample rate
bits per sample @var{bits} and number of channels @var{channels}. @var{frequency} bits per sample @var{bits} and number of channels
@var{channels}.
Defaults: Defaults:
@itemize @minus @itemize @minus

View File

@ -965,7 +965,7 @@ static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
uint32_t temp = r->picb << 1; uint32_t temp = r->picb << 1;
uint32_t written = 0; uint32_t written = 0;
int to_copy = 0; int to_copy = 0;
temp = audio_MIN (temp, max); temp = MIN (temp, max);
if (!temp) { if (!temp) {
*stop = 1; *stop = 1;
@ -974,7 +974,7 @@ static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
while (temp) { while (temp) {
int copied; int copied;
to_copy = audio_MIN (temp, sizeof (tmpbuf)); to_copy = MIN (temp, sizeof (tmpbuf));
pci_dma_read (&s->dev, addr, tmpbuf, to_copy); pci_dma_read (&s->dev, addr, tmpbuf, to_copy);
copied = AUD_write (s->voice_po, tmpbuf, to_copy); copied = AUD_write (s->voice_po, tmpbuf, to_copy);
dolog ("write_audio max=%x to_copy=%x copied=%x\n", dolog ("write_audio max=%x to_copy=%x copied=%x\n",
@ -1020,7 +1020,7 @@ static void write_bup (AC97LinkState *s, int elapsed)
} }
while (elapsed) { while (elapsed) {
int temp = audio_MIN (elapsed, sizeof (s->silence)); int temp = MIN (elapsed, sizeof (s->silence));
while (temp) { while (temp) {
int copied = AUD_write (s->voice_po, s->silence, temp); int copied = AUD_write (s->voice_po, s->silence, temp);
if (!copied) if (!copied)
@ -1041,7 +1041,7 @@ static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
int to_copy = 0; int to_copy = 0;
SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi; SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
temp = audio_MIN (temp, max); temp = MIN (temp, max);
if (!temp) { if (!temp) {
*stop = 1; *stop = 1;
@ -1050,7 +1050,7 @@ static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
while (temp) { while (temp) {
int acquired; int acquired;
to_copy = audio_MIN (temp, sizeof (tmpbuf)); to_copy = MIN (temp, sizeof (tmpbuf));
acquired = AUD_read (voice, tmpbuf, to_copy); acquired = AUD_read (voice, tmpbuf, to_copy);
if (!acquired) { if (!acquired) {
*stop = 1; *stop = 1;
@ -1410,6 +1410,7 @@ static int ac97_init (PCIBus *bus)
} }
static Property ac97_properties[] = { static Property ac97_properties[] = {
DEFINE_AUDIO_PROPERTIES(AC97LinkState, card),
DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0), DEFINE_PROP_UINT32 ("use_broken_id", AC97LinkState, use_broken_id, 0),
DEFINE_PROP_END_OF_LIST (), DEFINE_PROP_END_OF_LIST (),
}; };

View File

@ -195,7 +195,7 @@ static void adlib_callback (void *opaque, int free)
return; return;
} }
to_play = audio_MIN (s->left, samples); to_play = MIN (s->left, samples);
while (to_play) { while (to_play) {
written = write_audio (s, to_play); written = write_audio (s, to_play);
@ -210,7 +210,7 @@ static void adlib_callback (void *opaque, int free)
} }
} }
samples = audio_MIN (samples, s->samples - s->pos); samples = MIN (samples, s->samples - s->pos);
if (!samples) { if (!samples) {
return; return;
} }
@ -299,6 +299,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp)
} }
static Property adlib_properties[] = { static Property adlib_properties[] = {
DEFINE_AUDIO_PROPERTIES(AdlibState, card),
DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220), DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220),
DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100),
DEFINE_PROP_END_OF_LIST (), DEFINE_PROP_END_OF_LIST (),

View File

@ -536,7 +536,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos,
int copied; int copied;
size_t to_copy; size_t to_copy;
to_copy = audio_MIN (temp, left); to_copy = MIN (temp, left);
if (to_copy > sizeof (tmpbuf)) { if (to_copy > sizeof (tmpbuf)) {
to_copy = sizeof (tmpbuf); to_copy = sizeof (tmpbuf);
} }
@ -579,7 +579,7 @@ static int cs_dma_read (void *opaque, int nchan, int dma_pos, int dma_len)
till = (s->dregs[Playback_Lower_Base_Count] till = (s->dregs[Playback_Lower_Base_Count]
| (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift; | (s->dregs[Playback_Upper_Base_Count] << 8)) << s->shift;
till -= s->transferred; till -= s->transferred;
copy = audio_MIN (till, copy); copy = MIN (till, copy);
} }
if ((copy <= 0) || (dma_len <= 0)) { if ((copy <= 0) || (dma_len <= 0)) {
@ -690,6 +690,7 @@ static int cs4231a_init (ISABus *bus)
} }
static Property cs4231a_properties[] = { static Property cs4231a_properties[] = {
DEFINE_AUDIO_PROPERTIES(CSState, card),
DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534), DEFINE_PROP_UINT32 ("iobase", CSState, port, 0x534),
DEFINE_PROP_UINT32 ("irq", CSState, irq, 9), DEFINE_PROP_UINT32 ("irq", CSState, irq, 9),
DEFINE_PROP_UINT32 ("dma", CSState, dma, 3), DEFINE_PROP_UINT32 ("dma", CSState, dma, 3),

View File

@ -645,7 +645,7 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
int size = d->frame_cnt & 0xffff; int size = d->frame_cnt & 0xffff;
int left = ((size - cnt + 1) << 2) + d->leftover; int left = ((size - cnt + 1) << 2) + d->leftover;
int transferred = 0; int transferred = 0;
int temp = audio_MIN (max, audio_MIN (left, csc_bytes)); int temp = MIN (max, MIN (left, csc_bytes));
int index = d - &s->chan[0]; int index = d - &s->chan[0];
addr += (cnt << 2) + d->leftover; addr += (cnt << 2) + d->leftover;
@ -654,7 +654,7 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
while (temp) { while (temp) {
int acquired, to_copy; int acquired, to_copy;
to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); to_copy = MIN ((size_t) temp, sizeof (tmpbuf));
acquired = AUD_read (s->adc_voice, tmpbuf, to_copy); acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
if (!acquired) if (!acquired)
break; break;
@ -672,7 +672,7 @@ static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel,
while (temp) { while (temp) {
int copied, to_copy; int copied, to_copy;
to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf)); to_copy = MIN ((size_t) temp, sizeof (tmpbuf));
pci_dma_read (&s->dev, addr, tmpbuf, to_copy); pci_dma_read (&s->dev, addr, tmpbuf, to_copy);
copied = AUD_write (voice, tmpbuf, to_copy); copied = AUD_write (voice, tmpbuf, to_copy);
if (!copied) if (!copied)
@ -887,6 +887,11 @@ static int es1370_init (PCIBus *bus)
return 0; return 0;
} }
static Property es1370_properties[] = {
DEFINE_AUDIO_PROPERTIES(ES1370State, card),
DEFINE_PROP_END_OF_LIST(),
};
static void es1370_class_init (ObjectClass *klass, void *data) static void es1370_class_init (ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS (klass); DeviceClass *dc = DEVICE_CLASS (klass);
@ -903,6 +908,7 @@ static void es1370_class_init (ObjectClass *klass, void *data)
dc->desc = "ENSONIQ AudioPCI ES1370"; dc->desc = "ENSONIQ AudioPCI ES1370";
dc->vmsd = &vmstate_es1370; dc->vmsd = &vmstate_es1370;
dc->reset = es1370_on_reset; dc->reset = es1370_on_reset;
dc->props = es1370_properties;
} }
static const TypeInfo es1370_info = { static const TypeInfo es1370_info = {
@ -923,4 +929,3 @@ static void es1370_register_types (void)
} }
type_init (es1370_register_types) type_init (es1370_register_types)

View File

@ -119,7 +119,7 @@ static void GUS_callback (void *opaque, int free)
GUSState *s = opaque; GUSState *s = opaque;
samples = free >> s->shift; samples = free >> s->shift;
to_play = audio_MIN (samples, s->left); to_play = MIN (samples, s->left);
while (to_play) { while (to_play) {
int written = write_audio (s, to_play); int written = write_audio (s, to_play);
@ -134,7 +134,7 @@ static void GUS_callback (void *opaque, int free)
net += written; net += written;
} }
samples = audio_MIN (samples, s->samples); samples = MIN (samples, s->samples);
if (samples) { if (samples) {
gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf); gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
@ -194,7 +194,7 @@ static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
ldebug ("read DMA %#x %d\n", dma_pos, dma_len); ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma); mode = k->has_autoinitialization(s->isa_dma, s->emu.gusdma);
while (left) { while (left) {
int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf)); int to_copy = MIN ((size_t) left, sizeof (tmpbuf));
int copied; int copied;
ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos); ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
@ -299,6 +299,7 @@ static int GUS_init (ISABus *bus)
} }
static Property gus_properties[] = { static Property gus_properties[] = {
DEFINE_AUDIO_PROPERTIES(GUSState, card),
DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100), DEFINE_PROP_UINT32 ("freq", GUSState, freq, 44100),
DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240), DEFINE_PROP_UINT32 ("iobase", GUSState, port, 0x240),
DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7), DEFINE_PROP_UINT32 ("irq", GUSState, emu.gusirq, 7),

View File

@ -235,10 +235,10 @@ static void hda_audio_input_timer(void *opaque)
goto out_timer; goto out_timer;
} }
int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos); int64_t to_transfer = MIN(wpos - rpos, wanted_rpos - rpos);
while (to_transfer) { while (to_transfer) {
uint32_t start = (rpos & B_MASK); uint32_t start = (rpos & B_MASK);
uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); uint32_t chunk = MIN(B_SIZE - start, to_transfer);
int rc = hda_codec_xfer( int rc = hda_codec_xfer(
&st->state->hda, st->stream, false, st->buf + start, chunk); &st->state->hda, st->stream, false, st->buf + start, chunk);
if (!rc) { if (!rc) {
@ -263,13 +263,13 @@ static void hda_audio_input_cb(void *opaque, int avail)
int64_t wpos = st->wpos; int64_t wpos = st->wpos;
int64_t rpos = st->rpos; int64_t rpos = st->rpos;
int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail); int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), avail);
hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1))); hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1)));
while (to_transfer) { while (to_transfer) {
uint32_t start = (uint32_t) (wpos & B_MASK); uint32_t start = (uint32_t) (wpos & B_MASK);
uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer);
uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk); uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
wpos += read; wpos += read;
to_transfer -= read; to_transfer -= read;
@ -299,10 +299,10 @@ static void hda_audio_output_timer(void *opaque)
goto out_timer; goto out_timer;
} }
int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos); int64_t to_transfer = MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
while (to_transfer) { while (to_transfer) {
uint32_t start = (wpos & B_MASK); uint32_t start = (wpos & B_MASK);
uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); uint32_t chunk = MIN(B_SIZE - start, to_transfer);
int rc = hda_codec_xfer( int rc = hda_codec_xfer(
&st->state->hda, st->stream, true, st->buf + start, chunk); &st->state->hda, st->stream, true, st->buf + start, chunk);
if (!rc) { if (!rc) {
@ -327,7 +327,7 @@ static void hda_audio_output_cb(void *opaque, int avail)
int64_t wpos = st->wpos; int64_t wpos = st->wpos;
int64_t rpos = st->rpos; int64_t rpos = st->rpos;
int64_t to_transfer = audio_MIN(wpos - rpos, avail); int64_t to_transfer = MIN(wpos - rpos, avail);
if (wpos - rpos == B_SIZE) { if (wpos - rpos == B_SIZE) {
/* drop buffer, reset timer adjust */ /* drop buffer, reset timer adjust */
@ -342,7 +342,7 @@ static void hda_audio_output_cb(void *opaque, int avail)
while (to_transfer) { while (to_transfer) {
uint32_t start = (uint32_t) (rpos & B_MASK); uint32_t start = (uint32_t) (rpos & B_MASK);
uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); uint32_t chunk = (uint32_t) MIN(B_SIZE - start, to_transfer);
uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk); uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
rpos += written; rpos += written;
to_transfer -= written; to_transfer -= written;
@ -841,6 +841,7 @@ static const VMStateDescription vmstate_hda_audio = {
}; };
static Property hda_audio_properties[] = { static Property hda_audio_properties[] = {
DEFINE_AUDIO_PROPERTIES(HDAAudioState, card),
DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0),
DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true),
DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true),

View File

@ -185,7 +185,7 @@ static void ac97_in_cb(void *opaque, int avail_b)
MilkymistAC97State *s = opaque; MilkymistAC97State *s = opaque;
uint8_t buf[4096]; uint8_t buf[4096];
uint32_t remaining = s->regs[R_U_REMAINING]; uint32_t remaining = s->regs[R_U_REMAINING];
int temp = audio_MIN(remaining, avail_b); int temp = MIN(remaining, avail_b);
uint32_t addr = s->regs[R_U_ADDR]; uint32_t addr = s->regs[R_U_ADDR];
int transferred = 0; int transferred = 0;
@ -199,7 +199,7 @@ static void ac97_in_cb(void *opaque, int avail_b)
while (temp) { while (temp) {
int acquired, to_copy; int acquired, to_copy;
to_copy = audio_MIN(temp, sizeof(buf)); to_copy = MIN(temp, sizeof(buf));
acquired = AUD_read(s->voice_in, buf, to_copy); acquired = AUD_read(s->voice_in, buf, to_copy);
if (!acquired) { if (!acquired) {
break; break;
@ -228,7 +228,7 @@ static void ac97_out_cb(void *opaque, int free_b)
MilkymistAC97State *s = opaque; MilkymistAC97State *s = opaque;
uint8_t buf[4096]; uint8_t buf[4096];
uint32_t remaining = s->regs[R_D_REMAINING]; uint32_t remaining = s->regs[R_D_REMAINING];
int temp = audio_MIN(remaining, free_b); int temp = MIN(remaining, free_b);
uint32_t addr = s->regs[R_D_ADDR]; uint32_t addr = s->regs[R_D_ADDR];
int transferred = 0; int transferred = 0;
@ -242,7 +242,7 @@ static void ac97_out_cb(void *opaque, int free_b)
while (temp) { while (temp) {
int copied, to_copy; int copied, to_copy;
to_copy = audio_MIN(temp, sizeof(buf)); to_copy = MIN(temp, sizeof(buf));
cpu_physical_memory_read(addr, buf, to_copy); cpu_physical_memory_read(addr, buf, to_copy);
copied = AUD_write(s->voice_out, buf, to_copy); copied = AUD_write(s->voice_out, buf, to_copy);
if (!copied) { if (!copied) {
@ -330,6 +330,11 @@ static const VMStateDescription vmstate_milkymist_ac97 = {
} }
}; };
static Property milkymist_ac97_properties[] = {
DEFINE_AUDIO_PROPERTIES(MilkymistAC97State, card),
DEFINE_PROP_END_OF_LIST(),
};
static void milkymist_ac97_class_init(ObjectClass *klass, void *data) static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
@ -337,6 +342,7 @@ static void milkymist_ac97_class_init(ObjectClass *klass, void *data)
dc->realize = milkymist_ac97_realize; dc->realize = milkymist_ac97_realize;
dc->reset = milkymist_ac97_reset; dc->reset = milkymist_ac97_reset;
dc->vmsd = &vmstate_milkymist_ac97; dc->vmsd = &vmstate_milkymist_ac97;
dc->props = milkymist_ac97_properties;
} }
static const TypeInfo milkymist_ac97_info = { static const TypeInfo milkymist_ac97_info = {

View File

@ -103,7 +103,7 @@ static void pcspk_callback(void *opaque, int free)
} }
while (free > 0) { while (free > 0) {
n = audio_MIN(s->samples - s->play_pos, (unsigned int)free); n = MIN(s->samples - s->play_pos, (unsigned int)free);
n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n); n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
if (!n) if (!n)
break; break;
@ -209,6 +209,7 @@ static const VMStateDescription vmstate_spk = {
}; };
static Property pcspk_properties[] = { static Property pcspk_properties[] = {
DEFINE_AUDIO_PROPERTIES(PCSpkState, card),
DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, -1), DEFINE_PROP_UINT32("iobase", PCSpkState, iobase, -1),
DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),

View File

@ -625,6 +625,7 @@ static const VMStateDescription vmstate_pl041 = {
}; };
static Property pl041_device_properties[] = { static Property pl041_device_properties[] = {
DEFINE_AUDIO_PROPERTIES(PL041State, codec.card),
/* Non-compact FIFO depth property */ /* Non-compact FIFO depth property */
DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth, DEFINE_PROP_UINT32("nc_fifo_depth", PL041State, fifo_depth,
DEFAULT_FIFO_DEPTH), DEFAULT_FIFO_DEPTH),

View File

@ -1169,7 +1169,7 @@ static int write_audio (SB16State *s, int nchan, int dma_pos,
int copied; int copied;
size_t to_copy; size_t to_copy;
to_copy = audio_MIN (temp, left); to_copy = MIN (temp, left);
if (to_copy > sizeof (tmpbuf)) { if (to_copy > sizeof (tmpbuf)) {
to_copy = sizeof (tmpbuf); to_copy = sizeof (tmpbuf);
} }
@ -1422,6 +1422,7 @@ static int SB16_init (ISABus *bus)
} }
static Property sb16_properties[] = { static Property sb16_properties[] = {
DEFINE_AUDIO_PROPERTIES(SB16State, card),
DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */ DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */
DEFINE_PROP_UINT32 ("iobase", SB16State, port, 0x220), DEFINE_PROP_UINT32 ("iobase", SB16State, port, 0x220),
DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5), DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5),

View File

@ -70,7 +70,7 @@ static inline void wm8750_in_load(WM8750State *s)
{ {
if (s->idx_in + s->req_in <= sizeof(s->data_in)) if (s->idx_in + s->req_in <= sizeof(s->data_in))
return; return;
s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in); s->idx_in = MAX(0, (int) sizeof(s->data_in) - s->req_in);
AUD_read(*s->in[0], s->data_in + s->idx_in, AUD_read(*s->in[0], s->data_in + s->idx_in,
sizeof(s->data_in) - s->idx_in); sizeof(s->data_in) - s->idx_in);
} }
@ -702,6 +702,11 @@ void wm8750_set_bclk_in(void *opaque, int new_hz)
wm8750_clk_update(s, 1); wm8750_clk_update(s, 1);
} }
static Property wm8750_properties[] = {
DEFINE_AUDIO_PROPERTIES(WM8750State, card),
DEFINE_PROP_END_OF_LIST(),
};
static void wm8750_class_init(ObjectClass *klass, void *data) static void wm8750_class_init(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
@ -712,6 +717,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data)
sc->recv = wm8750_rx; sc->recv = wm8750_rx;
sc->send = wm8750_tx; sc->send = wm8750_tx;
dc->vmsd = &vmstate_wm8750; dc->vmsd = &vmstate_wm8750;
dc->props = wm8750_properties;
} }
static const TypeInfo wm8750_info = { static const TypeInfo wm8750_info = {

View File

@ -11,6 +11,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "audio/audio.h"
#include "net/net.h" #include "net/net.h"
#include "hw/qdev-properties.h" #include "hw/qdev-properties.h"
#include "qapi/error.h" #include "qapi/error.h"
@ -353,6 +354,62 @@ const PropertyInfo qdev_prop_netdev = {
}; };
/* --- audiodev --- */
static void get_audiodev(Object *obj, Visitor *v, const char* name,
void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
char *p = g_strdup(audio_get_id(card));
visit_type_str(v, name, &p, errp);
g_free(p);
}
static void set_audiodev(Object *obj, Visitor *v, const char* name,
void *opaque, Error **errp)
{
DeviceState *dev = DEVICE(obj);
Property *prop = opaque;
QEMUSoundCard *card = qdev_get_prop_ptr(dev, prop);
AudioState *state;
Error *local_err = NULL;
int err = 0;
char *str;
if (dev->realized) {
qdev_prop_set_after_realize(dev, name, errp);
return;
}
visit_type_str(v, name, &str, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
state = audio_state_by_name(str);
if (!state) {
err = -ENOENT;
goto out;
}
card->state = state;
out:
error_set_from_qdev_prop_error(errp, err, dev, prop, str);
g_free(str);
}
const PropertyInfo qdev_prop_audiodev = {
.name = "str",
.description = "ID of an audiodev to use as a backend",
/* release done on shutdown */
.get = get_audiodev,
.set = set_audiodev,
};
void qdev_prop_set_drive(DeviceState *dev, const char *name, void qdev_prop_set_drive(DeviceState *dev, const char *name,
BlockBackend *value, Error **errp) BlockBackend *value, Error **errp)
{ {

View File

@ -667,6 +667,7 @@ static const VMStateDescription vmstate_usb_audio = {
}; };
static Property usb_audio_properties[] = { static Property usb_audio_properties[] = {
DEFINE_AUDIO_PROPERTIES(USBAudioState, card),
DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0), DEFINE_PROP_UINT32("debug", USBAudioState, debug, 0),
DEFINE_PROP_UINT32("buffer", USBAudioState, buffer, DEFINE_PROP_UINT32("buffer", USBAudioState, buffer,
32 * USBAUDIO_PACKET_SIZE), 32 * USBAUDIO_PACKET_SIZE),

View File

@ -33,6 +33,7 @@ extern const PropertyInfo qdev_prop_blocksize;
extern const PropertyInfo qdev_prop_pci_host_devaddr; extern const PropertyInfo qdev_prop_pci_host_devaddr;
extern const PropertyInfo qdev_prop_uuid; extern const PropertyInfo qdev_prop_uuid;
extern const PropertyInfo qdev_prop_arraylen; extern const PropertyInfo qdev_prop_arraylen;
extern const PropertyInfo qdev_prop_audiodev;
extern const PropertyInfo qdev_prop_link; extern const PropertyInfo qdev_prop_link;
extern const PropertyInfo qdev_prop_off_auto_pcibar; extern const PropertyInfo qdev_prop_off_auto_pcibar;
extern const PropertyInfo qdev_prop_pcie_link_speed; extern const PropertyInfo qdev_prop_pcie_link_speed;
@ -234,6 +235,8 @@ extern const PropertyInfo qdev_prop_pcie_link_width;
+ type_check(QemuUUID, typeof_field(_state, _field)), \ + type_check(QemuUUID, typeof_field(_state, _field)), \
.set_default = true, \ .set_default = true, \
} }
#define DEFINE_PROP_AUDIODEV(_n, _s, _f) \
DEFINE_PROP(_n, _s, _f, qdev_prop_audiodev, QEMUSoundCard)
#define DEFINE_PROP_END_OF_LIST() \ #define DEFINE_PROP_END_OF_LIST() \
{} {}

View File

@ -179,9 +179,9 @@ void replay_net_packet_event(ReplayNetState *rns, unsigned flags,
/* Audio */ /* Audio */
/*! Saves/restores number of played samples of audio out operation. */ /*! Saves/restores number of played samples of audio out operation. */
void replay_audio_out(int *played); void replay_audio_out(size_t *played);
/*! Saves/restores recorded samples of audio in operation. */ /*! Saves/restores recorded samples of audio in operation. */
void replay_audio_in(int *recorded, void *samples, int *wpos, int size); void replay_audio_in(size_t *recorded, void *samples, size_t *wpos, size_t size);
/* VM state operations */ /* VM state operations */

View File

@ -1142,21 +1142,21 @@ static void hmp_stopcapture(Monitor *mon, const QDict *qdict)
static void hmp_wavcapture(Monitor *mon, const QDict *qdict) static void hmp_wavcapture(Monitor *mon, const QDict *qdict)
{ {
const char *path = qdict_get_str(qdict, "path"); const char *path = qdict_get_str(qdict, "path");
int has_freq = qdict_haskey(qdict, "freq"); int freq = qdict_get_try_int(qdict, "freq", 44100);
int freq = qdict_get_try_int(qdict, "freq", -1); int bits = qdict_get_try_int(qdict, "bits", 16);
int has_bits = qdict_haskey(qdict, "bits"); int nchannels = qdict_get_try_int(qdict, "nchannels", 2);
int bits = qdict_get_try_int(qdict, "bits", -1); const char *audiodev = qdict_get_str(qdict, "audiodev");
int has_channels = qdict_haskey(qdict, "nchannels");
int nchannels = qdict_get_try_int(qdict, "nchannels", -1);
CaptureState *s; CaptureState *s;
AudioState *as = audio_state_by_name(audiodev);
if (!as) {
monitor_printf(mon, "Audiodev '%s' not found\n", audiodev);
return;
}
s = g_malloc0 (sizeof (*s)); s = g_malloc0 (sizeof (*s));
freq = has_freq ? freq : 44100; if (wav_start_capture(as, s, path, freq, bits, nchannels)) {
bits = has_bits ? bits : 16;
nchannels = has_channels ? nchannels : 2;
if (wav_start_capture (s, path, freq, bits, nchannels)) {
monitor_printf(mon, "Failed to add wave capture\n"); monitor_printf(mon, "Failed to add wave capture\n");
g_free (s); g_free (s);
return; return;

View File

@ -1978,6 +1978,12 @@ can help the device and guest to keep up and not lose events in case
events are arriving in bulk. Possible causes for the latter are flaky events are arriving in bulk. Possible causes for the latter are flaky
network connections, or scripts for automated testing. network connections, or scripts for automated testing.
@item audiodev=@var{audiodev}
Use the specified @var{audiodev} when the VNC client requests audio
transmission. When not using an -audiodev argument, this option must
be omitted, otherwise is must be present and specify a valid audiodev.
@end table @end table
ETEXI ETEXI

View File

@ -15,18 +15,18 @@
#include "replay-internal.h" #include "replay-internal.h"
#include "audio/audio.h" #include "audio/audio.h"
void replay_audio_out(int *played) void replay_audio_out(size_t *played)
{ {
if (replay_mode == REPLAY_MODE_RECORD) { if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked()); g_assert(replay_mutex_locked());
replay_save_instructions(); replay_save_instructions();
replay_put_event(EVENT_AUDIO_OUT); replay_put_event(EVENT_AUDIO_OUT);
replay_put_dword(*played); replay_put_qword(*played);
} else if (replay_mode == REPLAY_MODE_PLAY) { } else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked()); g_assert(replay_mutex_locked());
replay_account_executed_instructions(); replay_account_executed_instructions();
if (replay_next_event_is(EVENT_AUDIO_OUT)) { if (replay_next_event_is(EVENT_AUDIO_OUT)) {
*played = replay_get_dword(); *played = replay_get_qword();
replay_finish_event(); replay_finish_event();
} else { } else {
error_report("Missing audio out event in the replay log"); error_report("Missing audio out event in the replay log");
@ -35,7 +35,7 @@ void replay_audio_out(int *played)
} }
} }
void replay_audio_in(int *recorded, void *samples, int *wpos, int size) void replay_audio_in(size_t *recorded, void *samples, size_t *wpos, size_t size)
{ {
int pos; int pos;
uint64_t left, right; uint64_t left, right;
@ -43,8 +43,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
g_assert(replay_mutex_locked()); g_assert(replay_mutex_locked());
replay_save_instructions(); replay_save_instructions();
replay_put_event(EVENT_AUDIO_IN); replay_put_event(EVENT_AUDIO_IN);
replay_put_dword(*recorded); replay_put_qword(*recorded);
replay_put_dword(*wpos); replay_put_qword(*wpos);
for (pos = (*wpos - *recorded + size) % size ; pos != *wpos for (pos = (*wpos - *recorded + size) % size ; pos != *wpos
; pos = (pos + 1) % size) { ; pos = (pos + 1) % size) {
audio_sample_to_uint64(samples, pos, &left, &right); audio_sample_to_uint64(samples, pos, &left, &right);
@ -55,8 +55,8 @@ void replay_audio_in(int *recorded, void *samples, int *wpos, int size)
g_assert(replay_mutex_locked()); g_assert(replay_mutex_locked());
replay_account_executed_instructions(); replay_account_executed_instructions();
if (replay_next_event_is(EVENT_AUDIO_IN)) { if (replay_next_event_is(EVENT_AUDIO_IN)) {
*recorded = replay_get_dword(); *recorded = replay_get_qword();
*wpos = replay_get_dword(); *wpos = replay_get_qword();
for (pos = (*wpos - *recorded + size) % size ; pos != *wpos for (pos = (*wpos - *recorded + size) % size ; pos != *wpos
; pos = (pos + 1) % size) { ; pos = (pos + 1) % size) {
left = replay_get_qword(); left = replay_get_qword();

View File

@ -22,7 +22,7 @@
/* Current version of the replay mechanism. /* Current version of the replay mechanism.
Increase it when file format changes. */ Increase it when file format changes. */
#define REPLAY_VERSION 0xe02007 #define REPLAY_VERSION 0xe02008
/* Size of replay log header */ /* Size of replay log header */
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))

View File

@ -1224,7 +1224,7 @@ static void audio_add(VncState *vs)
ops.destroy = audio_capture_destroy; ops.destroy = audio_capture_destroy;
ops.capture = audio_capture; ops.capture = audio_capture;
vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs); vs->audio_cap = AUD_add_capture(vs->vd->audio_state, &vs->as, &ops, vs);
if (!vs->audio_cap) { if (!vs->audio_cap) {
error_report("Failed to add audio capture"); error_report("Failed to add audio capture");
} }
@ -3371,6 +3371,9 @@ static QemuOptsList qemu_vnc_opts = {
},{ },{
.name = "non-adaptive", .name = "non-adaptive",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
},{
.name = "audiodev",
.type = QEMU_OPT_STRING,
}, },
{ /* end of list */ } { /* end of list */ }
}, },
@ -3808,6 +3811,7 @@ void vnc_display_open(const char *id, Error **errp)
const char *saslauthz; const char *saslauthz;
int lock_key_sync = 1; int lock_key_sync = 1;
int key_delay_ms; int key_delay_ms;
const char *audiodev;
if (!vd) { if (!vd) {
error_setg(errp, "VNC display not active"); error_setg(errp, "VNC display not active");
@ -3993,6 +3997,15 @@ void vnc_display_open(const char *id, Error **errp)
} }
vd->ledstate = 0; vd->ledstate = 0;
audiodev = qemu_opt_get(opts, "audiodev");
if (audiodev) {
vd->audio_state = audio_state_by_name(audiodev);
if (!vd->audio_state) {
error_setg(errp, "Audiodev '%s' not found", audiodev);
goto fail;
}
}
device_id = qemu_opt_get(opts, "display"); device_id = qemu_opt_get(opts, "display");
if (device_id) { if (device_id) {
int head = qemu_opt_get_number(opts, "head", 0); int head = qemu_opt_get_number(opts, "head", 0);

View File

@ -182,6 +182,8 @@ struct VncDisplay
#ifdef CONFIG_VNC_SASL #ifdef CONFIG_VNC_SASL
VncDisplaySASL sasl; VncDisplaySASL sasl;
#endif #endif
AudioState *audio_state;
}; };
typedef struct VncTight { typedef struct VncTight {