aboutsummaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2011-01-02 12:58:51 +0100
committerDavid 'Digit' Turner <digit@android.com>2011-01-02 13:05:31 +0100
commit5d0e37bc290d1743cb5acf76eb6608f1303f27dd (patch)
tree142486cd40940a38aa4fe5947521b39dc2cec339 /audio
parente3650680f44fed0262d33eb4f486e5c1e58ddc32 (diff)
downloadexternal_qemu-5d0e37bc290d1743cb5acf76eb6608f1303f27dd.zip
external_qemu-5d0e37bc290d1743cb5acf76eb6608f1303f27dd.tar.gz
external_qemu-5d0e37bc290d1743cb5acf76eb6608f1303f27dd.tar.bz2
upstream: audio sub-system improvements.
This patch updates the audio subsystem to match the one in upstream. Note that this gets rid of the ability to specify different audio backends for input and output, which was never really used. A future patch will remove the -audio-in and -audio-out options and related code. Change-Id: I37c21672bcb15ef1f0e928c56bf99fbecda2bce6
Diffstat (limited to 'audio')
-rw-r--r--audio/alsaaudio.c550
-rw-r--r--audio/audio.c598
-rw-r--r--audio/audio_int.h19
-rw-r--r--audio/audio_pt_int.c25
-rw-r--r--audio/audio_template.h14
-rw-r--r--audio/audio_win_int.c108
-rw-r--r--audio/audio_win_int.h10
-rw-r--r--audio/coreaudio.c179
-rw-r--r--audio/dsound_template.h8
-rw-r--r--audio/dsoundaudio.c260
-rw-r--r--audio/esdaudio.c500
-rw-r--r--audio/fmodaudio.c143
-rw-r--r--audio/mixeng.c2
-rw-r--r--audio/noaudio.c53
-rw-r--r--audio/ossaudio.c446
-rw-r--r--audio/paaudio.c8
-rw-r--r--audio/wavaudio.c9
-rw-r--r--audio/winaudio.c3
18 files changed, 1788 insertions, 1147 deletions
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 0f5ee9e..1cbbaa4 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -1,7 +1,7 @@
/*
* QEMU ALSA audio driver
*
- * Copyright (c) 2008 The Android Open Source Project
+ * Copyright (c) 2008-2010 The Android Open Source Project
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -24,8 +24,13 @@
*/
#include <alsa/asoundlib.h>
#include "qemu-common.h"
+#include "qemu-char.h"
#include "audio.h"
+#if QEMU_GNUC_PREREQ(4, 3)
+#pragma GCC diagnostic ignored "-Waddress"
+#endif
+
#define AUDIO_CAP "alsa"
#include "audio_int.h"
#include <dlfcn.h>
@@ -81,6 +86,10 @@
DYNLINK_FUNC(int,snd_pcm_hw_params_set_buffer_size_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \
DYNLINK_FUNC(int,snd_pcm_hw_params_set_period_size_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir)) \
DYNLINK_FUNC(int,snd_pcm_hw_params_get_format,(const snd_pcm_hw_params_t *params, snd_pcm_format_t *val)) \
+ DYNLINK_FUNC(int,snd_pcm_resume,(snd_pcm_t *pcm)) \
+ DYNLINK_FUNC(int,snd_pcm_poll_descriptors_revents,(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)) \
+ DYNLINK_FUNC(int,snd_pcm_poll_descriptors_count,(snd_pcm_t *pcm)) \
+ DYNLINK_FUNC(int,snd_pcm_poll_descriptors,(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)) \
#define DYNLINK_FUNCTIONS_INIT \
alsa_dynlink_init
@@ -96,16 +105,27 @@
static void* alsa_lib;
+struct pollhlp {
+ snd_pcm_t *handle;
+ struct pollfd *pfds;
+ int count;
+ int mask;
+};
+
typedef struct ALSAVoiceOut {
HWVoiceOut hw;
+ int wpos;
+ int pending;
void *pcm_buf;
snd_pcm_t *handle;
+ struct pollhlp pollhlp;
} ALSAVoiceOut;
typedef struct ALSAVoiceIn {
HWVoiceIn hw;
snd_pcm_t *handle;
void *pcm_buf;
+ struct pollhlp pollhlp;
} ALSAVoiceIn;
static struct {
@@ -126,7 +146,8 @@ static struct {
int period_size_out_overridden;
int verbose;
} conf = {
- .buffer_size_out = 1024,
+ .buffer_size_out = 4096,
+ .period_size_out = 1024,
.pcm_name_out = "default",
.pcm_name_in = "default",
};
@@ -178,7 +199,23 @@ static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
AUD_log (AUDIO_CAP, "Reason: %s\n", FF(snd_strerror) (err));
}
-static void alsa_anal_close (snd_pcm_t **handlep)
+static void alsa_fini_poll (struct pollhlp *hlp)
+{
+ int i;
+ struct pollfd *pfds = hlp->pfds;
+
+ if (pfds) {
+ for (i = 0; i < hlp->count; ++i) {
+ qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
+ }
+ qemu_free (pfds);
+ }
+ hlp->pfds = NULL;
+ hlp->count = 0;
+ hlp->handle = NULL;
+}
+
+static void alsa_anal_close1 (snd_pcm_t **handlep)
{
int err = FF(snd_pcm_close) (*handlep);
if (err) {
@@ -187,6 +224,167 @@ static void alsa_anal_close (snd_pcm_t **handlep)
*handlep = NULL;
}
+static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp)
+{
+ alsa_fini_poll (hlp);
+ alsa_anal_close1 (handlep);
+}
+
+static int alsa_recover (snd_pcm_t *handle)
+{
+ int err = FF(snd_pcm_prepare) (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Failed to prepare handle %p\n", handle);
+ return -1;
+ }
+ return 0;
+}
+
+static int alsa_resume (snd_pcm_t *handle)
+{
+ int err = FF(snd_pcm_resume) (handle);
+ if (err < 0) {
+ alsa_logerr (err, "Failed to resume handle %p\n", handle);
+ return -1;
+ }
+ return 0;
+}
+
+static void alsa_poll_handler (void *opaque)
+{
+ int err, count;
+ snd_pcm_state_t state;
+ struct pollhlp *hlp = opaque;
+ unsigned short revents;
+
+ count = poll (hlp->pfds, hlp->count, 0);
+ if (count < 0) {
+ dolog ("alsa_poll_handler: poll %s\n", strerror (errno));
+ return;
+ }
+
+ if (!count) {
+ return;
+ }
+
+ /* XXX: ALSA example uses initial count, not the one returned by
+ poll, correct? */
+ err = FF(snd_pcm_poll_descriptors_revents) (hlp->handle, hlp->pfds,
+ hlp->count, &revents);
+ if (err < 0) {
+ alsa_logerr (err, "snd_pcm_poll_descriptors_revents");
+ return;
+ }
+
+ if (!(revents & hlp->mask)) {
+ if (conf.verbose) {
+ dolog ("revents = %d\n", revents);
+ }
+ return;
+ }
+
+ state = FF(snd_pcm_state) (hlp->handle);
+ switch (state) {
+ case SND_PCM_STATE_SETUP:
+ alsa_recover (hlp->handle);
+ break;
+
+ case SND_PCM_STATE_XRUN:
+ alsa_recover (hlp->handle);
+ break;
+
+ case SND_PCM_STATE_SUSPENDED:
+ alsa_resume (hlp->handle);
+ break;
+
+ case SND_PCM_STATE_PREPARED:
+ audio_run ("alsa run (prepared)");
+ break;
+
+ case SND_PCM_STATE_RUNNING:
+ audio_run ("alsa run (running)");
+ break;
+
+ default:
+ dolog ("Unexpected state %d\n", state);
+ }
+}
+
+static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask)
+{
+ int i, count, err;
+ struct pollfd *pfds;
+
+ count = FF(snd_pcm_poll_descriptors_count) (handle);
+ if (count <= 0) {
+ dolog ("Could not initialize poll mode\n"
+ "Invalid number of poll descriptors %d\n", count);
+ return -1;
+ }
+
+ pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds));
+ if (!pfds) {
+ dolog ("Could not initialize poll mode\n");
+ return -1;
+ }
+
+ err = FF(snd_pcm_poll_descriptors) (handle, pfds, count);
+ if (err < 0) {
+ alsa_logerr (err, "Could not initialize poll mode\n"
+ "Could not obtain poll descriptors\n");
+ qemu_free (pfds);
+ return -1;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (pfds[i].events & POLLIN) {
+ err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler,
+ NULL, hlp);
+ }
+ if (pfds[i].events & POLLOUT) {
+ if (conf.verbose) {
+ dolog ("POLLOUT %d %d\n", i, pfds[i].fd);
+ }
+ err = qemu_set_fd_handler (pfds[i].fd, NULL,
+ alsa_poll_handler, hlp);
+ }
+ if (conf.verbose) {
+ dolog ("Set handler events=%#x index=%d fd=%d err=%d\n",
+ pfds[i].events, i, pfds[i].fd, err);
+ }
+
+ if (err) {
+ dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n",
+ pfds[i].events, i, pfds[i].fd, err);
+
+ while (i--) {
+ qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL);
+ }
+ qemu_free (pfds);
+ return -1;
+ }
+ }
+ hlp->pfds = pfds;
+ hlp->count = count;
+ hlp->handle = handle;
+ hlp->mask = mask;
+ return 0;
+}
+
+static int alsa_poll_out (HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+ return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT);
+}
+
+static int alsa_poll_in (HWVoiceIn *hw)
+{
+ ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+ 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);
@@ -285,10 +483,11 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
}
static void alsa_dump_info (struct alsa_params_req *req,
- struct alsa_params_obt *obt)
+ struct alsa_params_obt *obt,
+ snd_pcm_format_t obtfmt)
{
dolog ("parameter | requested value | obtained value\n");
- dolog ("format | %10d | %10d\n", req->fmt, obt->fmt);
+ dolog ("format | %10d | %10d\n", req->fmt, obtfmt);
dolog ("channels | %10d | %10d\n",
req->nchannels, obt->nchannels);
dolog ("frequency | %10d | %10d\n", req->freq, obt->freq);
@@ -474,7 +673,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
- if ((req->override_mask & 1) && (obt - req->period_size))
+ if (((req->override_mask & 1) && (obt - req->period_size)))
dolog ("Requested period %s %u was rejected, using %lu\n",
size_in_usec ? "time" : "size", req->period_size, obt);
}
@@ -491,7 +690,6 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
- err = FF(snd_pcm_hw_params_get_format)(hw_params, &obtfmt);
err = FF(snd_pcm_hw_params_get_format) (hw_params, &obtfmt);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to get format\n");
@@ -542,33 +740,23 @@ static int alsa_open (int in, struct alsa_params_req *req,
*handlep = handle;
if (conf.verbose &&
- (obt->fmt != req->fmt ||
+ (obtfmt != req->fmt ||
obt->nchannels != req->nchannels ||
obt->freq != req->freq)) {
- dolog ("Audio paramters for %s\n", typ);
- alsa_dump_info (req, obt);
+ dolog ("Audio parameters for %s\n", typ);
+ alsa_dump_info (req, obt, obtfmt);
}
#ifdef DEBUG
- alsa_dump_info (req, obt);
+ alsa_dump_info (req, obt, obtfmt);
#endif
return 0;
err:
- alsa_anal_close (&handle);
+ alsa_anal_close1 (&handle);
return -1;
}
-static int alsa_recover (snd_pcm_t *handle)
-{
- int err = FF(snd_pcm_prepare) (handle);
- if (err < 0) {
- alsa_logerr (err, "Failed to prepare handle %p\n", handle);
- return -1;
- }
- return 0;
-}
-
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
{
snd_pcm_sframes_t avail;
@@ -591,41 +779,19 @@ static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
return avail;
}
-static int alsa_run_out (HWVoiceOut *hw)
+static void alsa_write_pending (ALSAVoiceOut *alsa)
{
- ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
- int rpos, live, decr;
- int samples;
- uint8_t *dst;
- struct st_sample *src;
- snd_pcm_sframes_t avail;
-
- live = audio_pcm_hw_get_live_out (hw);
- if (!live) {
- return 0;
- }
-
- avail = alsa_get_avail (alsa->handle);
- if (avail < 0) {
- dolog ("Could not get number of available playback frames\n");
- return 0;
- }
-
- decr = audio_MIN (live, avail);
- samples = decr;
- rpos = hw->rpos;
- while (samples) {
- int left_till_end_samples = hw->samples - rpos;
- int len = audio_MIN (samples, left_till_end_samples);
- snd_pcm_sframes_t written;
-
- src = hw->mix_buf + rpos;
- dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
+ HWVoiceOut *hw = &alsa->hw;
- hw->clip (dst, src, len);
+ while (alsa->pending) {
+ int left_till_end_samples = hw->samples - alsa->wpos;
+ int len = audio_MIN (alsa->pending, left_till_end_samples);
+ char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
while (len) {
- written = FF(snd_pcm_writei) (alsa->handle, dst, len);
+ snd_pcm_sframes_t written;
+
+ written = FF(snd_pcm_writei) (alsa->handle, src, len);
if (written <= 0) {
switch (written) {
@@ -633,39 +799,65 @@ static int alsa_run_out (HWVoiceOut *hw)
if (conf.verbose) {
dolog ("Failed to write %d frames (wrote zero)\n", len);
}
- goto exit;
+ return;
case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (written, "Failed to write %d frames\n",
len);
- goto exit;
+ return;
}
if (conf.verbose) {
dolog ("Recovering from playback xrun\n");
}
continue;
+ case -ESTRPIPE:
+ /* stream is suspended and waiting for an
+ application recovery */
+ if (alsa_resume (alsa->handle)) {
+ alsa_logerr (written, "Failed to write %d frames\n",
+ len);
+ return;
+ }
+ if (conf.verbose) {
+ dolog ("Resuming suspended output stream\n");
+ }
+ continue;
+
case -EAGAIN:
- goto exit;
+ return;
default:
- alsa_logerr (written, "Failed to write %d frames to %p\n",
- len, dst);
- goto exit;
+ alsa_logerr (written, "Failed to write %d frames from %p\n",
+ len, src);
+ return;
}
}
- rpos = (rpos + written) % hw->samples;
- samples -= written;
+ alsa->wpos = (alsa->wpos + written) % hw->samples;
+ alsa->pending -= written;
len -= written;
- dst = advance (dst, written << hw->info.shift);
- src += written;
}
}
+}
- exit:
- hw->rpos = rpos;
+static int alsa_run_out (HWVoiceOut *hw, int live)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ int decr;
+ snd_pcm_sframes_t avail;
+
+ avail = alsa_get_avail (alsa->handle);
+ if (avail < 0) {
+ dolog ("Could not get number of available playback frames\n");
+ return 0;
+ }
+
+ decr = audio_MIN (live, avail);
+ decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
+ alsa->pending += decr;
+ alsa_write_pending (alsa);
return decr;
}
@@ -674,7 +866,7 @@ static void alsa_fini_out (HWVoiceOut *hw)
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
ldebug ("alsa_fini\n");
- alsa_anal_close (&alsa->handle);
+ alsa_anal_close (&alsa->handle, &alsa->pollhlp);
if (alsa->pcm_buf) {
qemu_free (alsa->pcm_buf);
@@ -701,8 +893,9 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
req.period_size = conf.period_size_out;
req.buffer_size = conf.buffer_size_out;
req.size_in_usec = conf.size_in_usec_out;
- req.override_mask = !!conf.period_size_out_overridden
- | (!!conf.buffer_size_out_overridden << 1);
+ req.override_mask =
+ (conf.period_size_out_overridden ? 1 : 0) |
+ (conf.buffer_size_out_overridden ? 2 : 0);
if (alsa_open (0, &req, &obt, &handle)) {
goto Exit;
@@ -720,7 +913,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
if (!alsa->pcm_buf) {
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
- alsa_anal_close (&handle);
+ alsa_anal_close1 (&handle);
goto Exit;
}
@@ -762,8 +955,21 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
switch (cmd) {
case VOICE_ENABLE:
- ldebug ("enabling voice\n");
- return alsa_voice_ctl (alsa->handle, "playback", 0);
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ ldebug ("enabling voice\n");
+ if (poll_mode && alsa_poll_out (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+ return alsa_voice_ctl (alsa->handle, "playback", 0);
+ }
case VOICE_DISABLE:
ldebug ("disabling voice\n");
@@ -792,8 +998,9 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
req.period_size = conf.period_size_in;
req.buffer_size = conf.buffer_size_in;
req.size_in_usec = conf.size_in_usec_in;
- req.override_mask = !!conf.period_size_in_overridden
- | (!!conf.buffer_size_in_overridden << 1);
+ req.override_mask =
+ (conf.period_size_in_overridden ? 1 : 0) |
+ (conf.buffer_size_in_overridden ? 2 : 0);
if (alsa_open (1, &req, &obt, &handle)) {
goto Exit;
@@ -811,7 +1018,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
if (!alsa->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
- alsa_anal_close (&handle);
+ alsa_anal_close1 (&handle);
goto Exit;
}
@@ -829,7 +1036,7 @@ static void alsa_fini_in (HWVoiceIn *hw)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
- alsa_anal_close (&alsa->handle);
+ alsa_anal_close (&alsa->handle, &alsa->pollhlp);
if (alsa->pcm_buf) {
qemu_free (alsa->pcm_buf);
@@ -849,8 +1056,8 @@ static int alsa_run_in (HWVoiceIn *hw)
int add;
int len;
} bufs[2] = {
- { hw->wpos, 0 },
- { 0, 0 }
+ { .add = hw->wpos, .len = 0 },
+ { .add = 0, .len = 0 }
};
snd_pcm_sframes_t avail;
snd_pcm_uframes_t read_samples = 0;
@@ -865,8 +1072,30 @@ static int alsa_run_in (HWVoiceIn *hw)
return 0;
}
- if (!avail && (FF(snd_pcm_state) (alsa->handle) == SND_PCM_STATE_PREPARED)) {
- avail = hw->samples;
+ if (!avail) {
+ snd_pcm_state_t state;
+
+ state = FF(snd_pcm_state) (alsa->handle);
+ switch (state) {
+ case SND_PCM_STATE_PREPARED:
+ avail = hw->samples;
+ break;
+ case SND_PCM_STATE_SUSPENDED:
+ /* stream is suspended and waiting for an application recovery */
+ if (alsa_resume (alsa->handle)) {
+ dolog ("Failed to resume suspended input stream\n");
+ return 0;
+ }
+ if (conf.verbose) {
+ dolog ("Resuming suspended input stream\n");
+ }
+ break;
+ default:
+ if (conf.verbose) {
+ dolog ("No frames available and ALSA state is %d\n", state);
+ }
+ return 0;
+ }
}
decr = audio_MIN (dead, avail);
@@ -954,11 +1183,29 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
switch (cmd) {
case VOICE_ENABLE:
- ldebug ("enabling voice\n");
- return alsa_voice_ctl (alsa->handle, "capture", 0);
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ ldebug ("enabling voice\n");
+ if (poll_mode && alsa_poll_in (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+
+ return alsa_voice_ctl (alsa->handle, "capture", 0);
+ }
case VOICE_DISABLE:
ldebug ("disabling voice\n");
+ if (hw->poll_mode) {
+ hw->poll_mode = 0;
+ alsa_fini_poll (&alsa->pollhlp);
+ }
return alsa_voice_ctl (alsa->handle, "capture", 1);
}
@@ -1002,63 +1249,98 @@ static void alsa_audio_fini (void *opaque)
}
static struct audio_option alsa_options[] = {
- {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
- "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
- {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
- "DAC period size (0 to go with system default)",
- &conf.period_size_out_overridden, 0},
- {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
- "DAC buffer size (0 to go with system default)",
- &conf.buffer_size_out_overridden, 0},
-
- {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
- "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
- {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
- "ADC period size (0 to go with system default)",
- &conf.period_size_in_overridden, 0},
- {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
- "ADC buffer size (0 to go with system default)",
- &conf.buffer_size_in_overridden, 0},
-
- {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
- "(undocumented)", NULL, 0},
-
- {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
- "DAC device name (for instance dmix)", NULL, 0},
-
- {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
- "ADC device name", NULL, 0},
-
- {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
- "Behave in a more verbose way", NULL, 0},
-
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "DAC_SIZE_IN_USEC",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.size_in_usec_out,
+ .descr = "DAC period/buffer size in microseconds (otherwise in frames)"
+ },
+ {
+ .name = "DAC_PERIOD_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.period_size_out,
+ .descr = "DAC period size (0 to go with system default)",
+ .overriddenp = &conf.period_size_out_overridden
+ },
+ {
+ .name = "DAC_BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.buffer_size_out,
+ .descr = "DAC buffer size (0 to go with system default)",
+ .overriddenp = &conf.buffer_size_out_overridden
+ },
+ {
+ .name = "ADC_SIZE_IN_USEC",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.size_in_usec_in,
+ .descr =
+ "ADC period/buffer size in microseconds (otherwise in frames)"
+ },
+ {
+ .name = "ADC_PERIOD_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.period_size_in,
+ .descr = "ADC period size (0 to go with system default)",
+ .overriddenp = &conf.period_size_in_overridden
+ },
+ {
+ .name = "ADC_BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.buffer_size_in,
+ .descr = "ADC buffer size (0 to go with system default)",
+ .overriddenp = &conf.buffer_size_in_overridden
+ },
+ {
+ .name = "THRESHOLD",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.threshold,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "DAC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.pcm_name_out,
+ .descr = "DAC device name (for instance dmix)"
+ },
+ {
+ .name = "ADC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.pcm_name_in,
+ .descr = "ADC device name"
+ },
+ {
+ .name = "VERBOSE",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.verbose,
+ .descr = "Behave in a more verbose way"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops alsa_pcm_ops = {
- alsa_init_out,
- alsa_fini_out,
- alsa_run_out,
- alsa_write,
- alsa_ctl_out,
-
- alsa_init_in,
- alsa_fini_in,
- alsa_run_in,
- alsa_read,
- alsa_ctl_in
+ .init_out = alsa_init_out,
+ .fini_out = alsa_fini_out,
+ .run_out = alsa_run_out,
+ .write = alsa_write,
+ .ctl_out = alsa_ctl_out,
+
+ .init_in = alsa_init_in,
+ .fini_in = alsa_fini_in,
+ .run_in = alsa_run_in,
+ .read = alsa_read,
+ .ctl_in = alsa_ctl_in,
};
struct audio_driver alsa_audio_driver = {
- INIT_FIELD (name = ) "alsa",
- INIT_FIELD (descr = ) "ALSA audio (www.alsa-project.org)",
- INIT_FIELD (options = ) alsa_options,
- INIT_FIELD (init = ) alsa_audio_init,
- INIT_FIELD (fini = ) alsa_audio_fini,
- INIT_FIELD (pcm_ops = ) &alsa_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) INT_MAX,
- INIT_FIELD (max_voices_in = ) INT_MAX,
- INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn)
+ .name = "alsa",
+ .descr = "ALSA http://www.alsa-project.org",
+ .options = alsa_options,
+ .init = alsa_audio_init,
+ .fini = alsa_audio_fini,
+ .pcm_ops = &alsa_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (ALSAVoiceOut),
+ .voice_size_in = sizeof (ALSAVoiceIn)
};
diff --git a/audio/audio.c b/audio/audio.c
index 6f107dc..e095b1a 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -158,71 +158,59 @@ static struct {
} period;
int plive;
int log_to_monitor;
+ int try_poll_in;
+ int try_poll_out;
} conf = {
- { /* DAC fixed settings */
- 1, /* enabled */
- 1, /* nb_voices */
- 1, /* greedy */
- {
- 44100, /* freq */
- 2, /* nchannels */
- AUD_FMT_S16, /* fmt */
- AUDIO_HOST_ENDIANNESS
+ .fixed_out = { /* DAC fixed settings */
+ .enabled = 1,
+ .nb_voices = 1,
+ .greedy = 1,
+ .settings = {
+ .freq = 44100,
+ .nchannels = 2,
+ .fmt = AUD_FMT_S16,
+ .endianness = AUDIO_HOST_ENDIANNESS,
}
},
- { /* ADC fixed settings */
- 1, /* enabled */
- 1, /* nb_voices */
- 1, /* greedy */
- {
- 44100, /* freq */
- 2, /* nchannels */
- AUD_FMT_S16, /* fmt */
- AUDIO_HOST_ENDIANNESS
+ .fixed_in = { /* ADC fixed settings */
+ .enabled = 1,
+ .nb_voices = 1,
+ .greedy = 1,
+ .settings = {
+ .freq = 44100,
+ .nchannels = 2,
+ .fmt = AUD_FMT_S16,
+ .endianness = AUDIO_HOST_ENDIANNESS,
}
},
- { 250 }, /* period */
- 0, /* plive */
- 0 /* log_to_monitor */
+ .period = { .hertz = 250 },
+ .plive = 0,
+ .log_to_monitor = 0,
+ .try_poll_in = 1,
+ .try_poll_out = 1,
};
static AudioState glob_audio_state;
struct mixeng_volume nominal_volume = {
- 0,
+ .mute = 0,
#ifdef FLOAT_MIXENG
- 1.0,
- 1.0
+ .r = 1.0,
+ .l = 1.0,
#else
- 1ULL << 32,
- 1ULL << 32
+ .r = 1ULL << 32,
+ .l = 1ULL << 32,
#endif
};
-#if 0
-/* http://www.df.lth.se/~john_e/gems/gem002d.html */
-/* http://www.multi-platforms.com/Tips/PopCount.htm */
-uint32_t popcount (uint32_t u)
-{
- u = ((u&0x55555555) + ((u>>1)&0x55555555));
- u = ((u&0x33333333) + ((u>>2)&0x33333333));
- u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
- u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
- u = ( u&0x0000ffff) + (u>>16);
- return u;
-}
-
-inline uint32_t lsbindex (uint32_t u)
-{
- return popcount ((u&-u)-1);
-}
-#endif
-
#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
#error No its not
#else
+static void audio_print_options (const char *prefix,
+ struct audio_option *opt);
+
int audio_bug (const char *funcname, int cond)
{
if (cond) {
@@ -230,10 +218,16 @@ int audio_bug (const char *funcname, int cond)
AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
if (!shown) {
+ struct audio_driver *d;
+
shown = 1;
AUD_log (NULL, "Save all your work and restart without audio\n");
- AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n");
+ AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n");
AUD_log (NULL, "I am sorry\n");
+ d = glob_audio_state.drv;
+ if (d) {
+ audio_print_options (d->name, d->options);
+ }
}
AUD_log (NULL, "Context:\n");
@@ -966,6 +960,28 @@ int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
return live;
}
+int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
+ int live, int pending)
+{
+ int left = hw->samples - pending;
+ int len = audio_MIN (left, live);
+ int clipped = 0;
+
+ while (len) {
+ struct st_sample *src = hw->mix_buf + hw->rpos;
+ uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
+ int samples_till_end_of_buf = hw->samples - hw->rpos;
+ int samples_to_clip = audio_MIN (len, samples_till_end_of_buf);
+
+ hw->clip (dst, src, samples_to_clip);
+
+ hw->rpos = (hw->rpos + samples_to_clip) % hw->samples;
+ len -= samples_to_clip;
+ clipped += samples_to_clip;
+ }
+ return clipped;
+}
+
/*
* Soft voice (capture)
*/
@@ -1062,16 +1078,17 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
return m;
}
-int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
+static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
{
int smin;
+ int nb_live1;
- smin = audio_pcm_hw_find_min_out (hw, nb_live);
-
- if (!*nb_live) {
- return 0;
+ smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
+ if (nb_live) {
+ *nb_live = nb_live1;
}
- else {
+
+ if (nb_live1) {
int live = smin;
if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
@@ -1080,19 +1097,7 @@ int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
}
return live;
}
-}
-
-int audio_pcm_hw_get_live_out (HWVoiceOut *hw)
-{
- int nb_live;
- int live;
-
- live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
- dolog ("live=%d hw->samples=%d\n", live, hw->samples);
- return 0;
- }
- return live;
+ return 0;
}
/*
@@ -1185,6 +1190,76 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
#undef DAC
#include "audio_template.h"
+/*
+ * Timer
+ */
+static void audio_timer (void *opaque)
+{
+ AudioState *s = opaque;
+#if 0
+#define MAX_DIFFS 100
+ int64_t now = qemu_get_clock(vm_clock);
+ static int64_t last = 0;
+ static float diffs[MAX_DIFFS];
+ static int num_diffs;
+
+ if (last == 0)
+ last = now;
+ else {
+ diffs[num_diffs] = (float)((now-last)/1e6); /* last diff in ms */
+ if (++num_diffs == MAX_DIFFS) {
+ double min_diff = 1e6, max_diff = -1e6;
+ double all_diff = 0.;
+ int nn;
+
+ for (nn = 0; nn < num_diffs; nn++) {
+ if (diffs[nn] < min_diff) min_diff = diffs[nn];
+ if (diffs[nn] > max_diff) max_diff = diffs[nn];
+ all_diff += diffs[nn];
+ }
+ all_diff *= 1.0/num_diffs;
+ printf("audio timer: min_diff=%6.2g max_diff=%6.2g avg_diff=%6.2g samples=%d\n",
+ min_diff, max_diff, all_diff, num_diffs);
+ num_diffs = 0;
+ }
+ }
+ last = now;
+#endif
+
+ audio_run ("timer");
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+}
+
+
+static int audio_is_timer_needed (void)
+{
+ HWVoiceIn *hwi = NULL;
+ HWVoiceOut *hwo = NULL;
+
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+ if (!hwo->poll_mode) return 1;
+ }
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+ if (!hwi->poll_mode) return 1;
+ }
+ return 0;
+}
+
+static void audio_reset_timer (void)
+{
+ AudioState *s = &glob_audio_state;
+
+ if (audio_is_timer_needed ()) {
+ qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
+ }
+ else {
+ qemu_del_timer (s->ts);
+ }
+}
+
+/*
+ * Public API
+ */
int AUD_write (SWVoiceOut *sw, void *buf, int size)
{
int bytes;
@@ -1199,9 +1274,7 @@ int AUD_write (SWVoiceOut *sw, void *buf, int size)
return 0;
}
- BEGIN_NOSIGALRM
- bytes = sw->hw->pcm_ops->write (sw, buf, size);
- END_NOSIGALRM
+ bytes = sw->hw->pcm_ops->write (sw, buf, size);
return bytes;
}
@@ -1219,9 +1292,7 @@ int AUD_read (SWVoiceIn *sw, void *buf, int size)
return 0;
}
- BEGIN_NOSIGALRM
- bytes = sw->hw->pcm_ops->read (sw, buf, size);
- END_NOSIGALRM
+ bytes = sw->hw->pcm_ops->read (sw, buf, size);
return bytes;
}
@@ -1249,9 +1320,8 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
if (!hw->enabled) {
hw->enabled = 1;
if (s->vm_running) {
- BEGIN_NOSIGALRM
- hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
- END_NOSIGALRM
+ hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
+ audio_reset_timer ();
}
}
}
@@ -1295,9 +1365,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (!hw->enabled) {
hw->enabled = 1;
if (s->vm_running) {
- BEGIN_NOSIGALRM
- hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
- END_NOSIGALRM
+ hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
}
}
sw->total_hw_samples_acquired = hw->total_samples_captured;
@@ -1313,9 +1381,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (nb_active == 1) {
hw->enabled = 0;
- BEGIN_NOSIGALRM
- hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
- END_NOSIGALRM
+ hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
}
}
}
@@ -1418,7 +1484,7 @@ static void audio_run_out (AudioState *s)
int played;
int live, free, nb_live, cleanup_required, prev_rpos;
- live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+ live = audio_pcm_hw_get_live_out (hw, &nb_live);
if (!nb_live) {
live = 0;
}
@@ -1435,9 +1501,7 @@ static void audio_run_out (AudioState *s)
#endif
hw->enabled = 0;
hw->pending_disable = 0;
- BEGIN_NOSIGALRM
- hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
- END_NOSIGALRM
+ hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
sc->sw.active = 0;
audio_recalc_and_notify_capture (sc->cap);
@@ -1458,9 +1522,7 @@ static void audio_run_out (AudioState *s)
}
prev_rpos = hw->rpos;
- BEGIN_NOSIGALRM
- played = hw->pcm_ops->run_out (hw);
- END_NOSIGALRM
+ played = hw->pcm_ops->run_out (hw, live);
if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
hw->rpos, hw->samples, played);
@@ -1529,9 +1591,7 @@ static void audio_run_in (AudioState *s)
SWVoiceIn *sw;
int captured, min;
- BEGIN_NOSIGALRM
- captured = hw->pcm_ops->run_in (hw);
- END_NOSIGALRM
+ captured = hw->pcm_ops->run_in (hw);
min = audio_pcm_hw_find_min_in (hw);
hw->total_samples_captured += captured - min;
@@ -1561,7 +1621,7 @@ static void audio_run_capture (AudioState *s)
HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw;
- captured = live = audio_pcm_hw_get_live_out (hw);
+ captured = live = audio_pcm_hw_get_live_out (hw, NULL);
rpos = hw->rpos;
while (live) {
int left = hw->samples - rpos;
@@ -1599,89 +1659,126 @@ static void audio_run_capture (AudioState *s)
}
}
-static void audio_timer (void *opaque)
+void audio_run (const char *msg)
{
- AudioState *s = opaque;
-#if 0
-#define MAX_DIFFS 1000
- int64_t now = qemu_get_clock(vm_clock);
- static int64_t last = 0;
- static float diffs[MAX_DIFFS];
- static int num_diffs;
+ AudioState *s = &glob_audio_state;
- if (last == 0)
- last = now;
- else {
- diffs[num_diffs] = (float)((now-last)/1e6); /* last diff in ms */
- if (++num_diffs == MAX_DIFFS) {
- double min_diff = 1e6, max_diff = -1e6;
- double all_diff = 0.;
- int nn;
-
- for (nn = 0; nn < num_diffs; nn++) {
- if (diffs[nn] < min_diff) min_diff = diffs[nn];
- if (diffs[nn] > max_diff) max_diff = diffs[nn];
- all_diff += diffs[nn];
- }
- all_diff *= 1.0/num_diffs;
- printf("audio timer: min_diff=%6.2g max_diff=%6.2g avg_diff=%6.2g samples=%d\n",
- min_diff, max_diff, all_diff, num_diffs);
- num_diffs = 0;
- }
- }
- last = now;
-#endif
audio_run_out (s);
audio_run_in (s);
audio_run_capture (s);
+#ifdef DEBUG_POLL
+ {
+ static double prevtime;
+ double currtime;
+ struct timeval tv;
- qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+ if (gettimeofday (&tv, NULL)) {
+ perror ("audio_run: gettimeofday");
+ return;
+ }
+
+ currtime = tv.tv_sec + tv.tv_usec * 1e-6;
+ dolog ("Elapsed since last %s: %f\n", msg, currtime - prevtime);
+ prevtime = currtime;
+ }
+#endif
}
static struct audio_option audio_options[] = {
/* DAC */
- {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
- "Use fixed settings for host DAC", NULL, 0},
-
- {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq,
- "Frequency for fixed host DAC", NULL, 0},
-
- {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt,
- "Format for fixed host DAC", NULL, 0},
-
- {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels,
- "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
-
- {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices,
- "Number of voices for DAC", NULL, 0},
-
+ {
+ .name = "DAC_FIXED_SETTINGS",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.fixed_out.enabled,
+ .descr = "Use fixed settings for host DAC"
+ },
+ {
+ .name = "DAC_FIXED_FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_out.settings.freq,
+ .descr = "Frequency for fixed host DAC"
+ },
+ {
+ .name = "DAC_FIXED_FMT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.fixed_out.settings.fmt,
+ .descr = "Format for fixed host DAC"
+ },
+ {
+ .name = "DAC_FIXED_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_out.settings.nchannels,
+ .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "DAC_VOICES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_out.nb_voices,
+ .descr = "Number of voices for DAC"
+ },
+ {
+ .name = "DAC_TRY_POLL",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.try_poll_out,
+ .descr = "Attempt using poll mode for DAC"
+ },
/* ADC */
- {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled,
- "Use fixed settings for host ADC", NULL, 0},
-
- {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq,
- "Frequency for fixed host ADC", NULL, 0},
-
- {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt,
- "Format for fixed host ADC", NULL, 0},
-
- {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels,
- "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
-
- {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices,
- "Number of voices for ADC", NULL, 0},
-
+ {
+ .name = "ADC_FIXED_SETTINGS",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.fixed_in.enabled,
+ .descr = "Use fixed settings for host ADC"
+ },
+ {
+ .name = "ADC_FIXED_FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_in.settings.freq,
+ .descr = "Frequency for fixed host ADC"
+ },
+ {
+ .name = "ADC_FIXED_FMT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.fixed_in.settings.fmt,
+ .descr = "Format for fixed host ADC"
+ },
+ {
+ .name = "ADC_FIXED_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_in.settings.nchannels,
+ .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "ADC_VOICES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fixed_in.nb_voices,
+ .descr = "Number of voices for ADC"
+ },
+ {
+ .name = "ADC_TRY_POLL",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.try_poll_in,
+ .descr = "Attempt using poll mode for ADC"
+ },
/* Misc */
- {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hertz,
- "Timer period in HZ (0 - use lowest possible)", NULL, 0},
-
- {"PLIVE", AUD_OPT_BOOL, &conf.plive,
- "(undocumented)", NULL, 0},
-
- {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor,
- "print logging messages to monitor instead of stderr", NULL, 0},
-
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "TIMER_PERIOD",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.period.hertz,
+ .descr = "Timer period in HZ (0 - use lowest possible)"
+ },
+ {
+ .name = "PLIVE",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.plive,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "LOG_TO_MONITOR",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.log_to_monitor,
+ .descr = "Print logging messages to monitor instead of stderr"
+ },
+ { /* End of list */ }
};
static void audio_pp_nb_voices (const char *typ, int nb)
@@ -1755,43 +1852,17 @@ void AUD_help (void)
);
}
-static int audio_driver_init (AudioState *s, struct audio_driver *drv, int out)
+static int audio_driver_init (AudioState *s, struct audio_driver *drv)
{
- void* opaque;
-
if (drv->options) {
audio_process_options (drv->name, drv->options);
}
+ s->drv_opaque = drv->init ();
- /* is the driver already initialized ? */
- if (out) {
- if (drv == s->drv_in) {
- s->drv_out = drv;
- s->drv_out_opaque = s->drv_in_opaque;
- return 0;
- }
- } else {
- if (drv == s->drv_out) {
- s->drv_in = drv;
- s->drv_in_opaque = s->drv_out_opaque;
- return 0;
- }
- }
-
- BEGIN_NOSIGALRM
- opaque = drv->init();
- END_NOSIGALRM
-
- if (opaque != NULL) {
+ if (s->drv_opaque) {
audio_init_nb_voices_out (drv);
audio_init_nb_voices_in (drv);
- if (out) {
- s->drv_out = drv;
- s->drv_out_opaque = opaque;
- } else {
- s->drv_in = drv;
- s->drv_in_opaque = opaque;
- }
+ s->drv = drv;
return 0;
}
else {
@@ -1809,19 +1880,17 @@ static void audio_vm_change_state_handler (void *opaque, int running,
int op = running ? VOICE_ENABLE : VOICE_DISABLE;
s->vm_running = running;
- BEGIN_NOSIGALRM
- while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
- hwo->pcm_ops->ctl_out (hwo, op);
- }
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
+ hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out);
+ }
- while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
- hwi->pcm_ops->ctl_in (hwi, op);
- }
- END_NOSIGALRM
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
+ hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
+ }
+ audio_reset_timer ();
}
-// to make sure audio_atexit() is only called once
-static int initialized = 0;
+static int initialized;
static void audio_atexit (void)
{
@@ -1832,7 +1901,6 @@ static void audio_atexit (void)
if (!initialized) return;
initialized = 0;
- BEGIN_NOSIGALRM
while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) {
SWVoiceCap *sc;
@@ -1854,13 +1922,9 @@ static void audio_atexit (void)
hwi->pcm_ops->fini_in (hwi);
}
- if (s->drv_in) {
- s->drv_in->fini (s->drv_in_opaque);
+ if (s->drv) {
+ s->drv->fini (s->drv_opaque);
}
- if (s->drv_out) {
- s->drv_out->fini (s->drv_out_opaque);
- }
- END_NOSIGALRM
}
static void audio_save (QEMUFile *f, void *opaque)
@@ -1881,73 +1945,17 @@ static int audio_load (QEMUFile *f, void *opaque, int version_id)
return 0;
}
-static int
-find_audio_driver( AudioState* s, int out )
-{
- int i, done = 0, def;
- const char* envname;
- const char* drvname;
- struct audio_driver* drv = NULL;
- const char* drvtype = out ? "output" : "input";
-
- envname = out ? "QEMU_AUDIO_OUT_DRV" : "QEMU_AUDIO_IN_DRV";
- drvname = audio_get_conf_str(envname, NULL, &def);
- if (drvname == NULL) {
- drvname = audio_get_conf_str("QEMU_AUDIO_DRV", NULL, &def);
- }
-
- if (drvname != NULL) { /* look for a specific driver */
- for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
- if (!strcmp (drvname, drvtab[i]->name)) {
- drv = drvtab[i];
- break;
- }
- }
- }
-
- if (drv != NULL) {
- done = !audio_driver_init (s, drv, out);
- if (!done) {
- dolog ("Could not initialize '%s' %s audio backend, trying default one.\n",
- drvname, drvtype);
- dolog ("Run with -qemu -audio-help to list available backends\n");
- drv = NULL;
- }
- }
-
- if (!drv) {
- for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
- if (drvtab[i]->can_be_default) {
- drv = drvtab[i];
- done = !audio_driver_init (s, drv, out);
- if (done)
- break;
- }
- }
- }
-
- if (!done) {
- drv = &no_audio_driver;
- done = !audio_driver_init (s, drv, out);
- if (!done) {
- /* this should never happen */
- dolog ("Could not initialize audio subsystem\n");
- return -1;
- }
- dolog ("warning: Could not find suitable audio %s backend\n", drvtype);
- }
-
- if (VERBOSE_CHECK(init))
- dprint("using '%s' audio %s backend", drv->name, drvtype );
- return 0;
-}
static void audio_init (void)
{
+ size_t i;
+ int done = 0;
+ const char *drvname;
+ VMChangeStateEntry *e;
AudioState *s = &glob_audio_state;
- if (s->drv_out && s->drv_in) {
+ if (s->drv) {
return;
}
@@ -1979,13 +1987,45 @@ static void audio_init (void)
s->nb_hw_voices_in = 0;
}
- if ( find_audio_driver (s, 0) != 0 ||
- find_audio_driver (s, 1) != 0 ) {
- qemu_del_timer (s->ts);
- return;
+ {
+ int def;
+ drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
}
- VMChangeStateEntry *e;
+ if (drvname) {
+ int found = 0;
+
+ for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
+ if (!strcmp (drvname, drvtab[i]->name)) {
+ done = !audio_driver_init (s, drvtab[i]);
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ dolog ("Unknown audio driver `%s'\n", drvname);
+ dolog ("Run with -audio-help to list available drivers\n");
+ }
+ }
+
+ if (!done) {
+ for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
+ if (drvtab[i]->can_be_default) {
+ done = !audio_driver_init (s, drvtab[i]);
+ }
+ }
+ }
+
+ if (!done) {
+ done = !audio_driver_init (s, &no_audio_driver);
+ if (!done) {
+ hw_error("Could not initialize audio subsystem\n");
+ }
+ else {
+ dolog ("warning: Using timer based audio emulation\n");
+ }
+ }
if (conf.period.hertz <= 0) {
if (conf.period.hertz < 0) {
@@ -2004,12 +2044,11 @@ static void audio_init (void)
dolog ("warning: Could not register change state handler\n"
"(Audio can continue looping even after stopping the VM)\n");
}
-
initialized = 1;
QLIST_INIT (&s->card_head);
register_savevm ("audio", 0, 1, audio_save, audio_load, s);
- qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+ audio_reset_timer();
}
void AUD_register_card (const char *name, QEMUSoundCard *card)
@@ -2026,11 +2065,6 @@ void AUD_remove_card (QEMUSoundCard *card)
qemu_free (card->name);
}
-// this was added to work around a deadlock in SDL when quitting
-void AUD_cleanup()
-{
- audio_atexit();
-}
CaptureVoiceOut *AUD_add_capture (
struct audsettings *as,
diff --git a/audio/audio_int.h b/audio/audio_int.h
index a78f394..c6afe81 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -70,6 +70,7 @@ typedef struct SWVoiceCap SWVoiceCap;
typedef struct HWVoiceOut {
int enabled;
+ int poll_mode;
int pending_disable;
struct audio_pcm_info info;
@@ -89,6 +90,7 @@ typedef struct HWVoiceOut {
typedef struct HWVoiceIn {
int enabled;
+ int poll_mode;
struct audio_pcm_info info;
t_sample *conv;
@@ -155,7 +157,7 @@ struct audio_driver {
struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, struct audsettings *as);
void (*fini_out)(HWVoiceOut *hw);
- int (*run_out) (HWVoiceOut *hw);
+ int (*run_out) (HWVoiceOut *hw, int live);
int (*write) (SWVoiceOut *sw, void *buf, int size);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
@@ -186,10 +188,8 @@ struct SWVoiceCap {
};
struct AudioState {
- struct audio_driver* drv_in;
- void* drv_in_opaque;
- struct audio_driver* drv_out;
- void* drv_out_opaque;
+ struct audio_driver *drv;
+ void *drv_opaque;
QEMUTimer *ts;
QLIST_HEAD (card_listhead, QEMUSoundCard) card_head;
@@ -203,6 +203,7 @@ struct AudioState {
extern struct audio_driver no_audio_driver;
extern struct audio_driver oss_audio_driver;
+extern struct audio_driver sdl_audio_driver;
extern struct audio_driver win_audio_driver;
extern struct audio_driver wav_audio_driver;
extern struct audio_driver fmod_audio_driver;
@@ -211,6 +212,7 @@ extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
extern struct audio_driver esd_audio_driver;
extern struct audio_driver pa_audio_driver;
+extern struct audio_driver winwave_audio_driver;
extern struct mixeng_volume nominal_volume;
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
@@ -220,12 +222,15 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
-int audio_pcm_hw_get_live_out (HWVoiceOut *hw);
-int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
+
+int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
+ int live, int pending);
int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size);
+void audio_run (const char *msg);
+
#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
index e889a98..f15cc70 100644
--- a/audio/audio_pt_int.c
+++ b/audio/audio_pt_int.c
@@ -6,6 +6,8 @@
#include "audio_int.h"
#include "audio_pt_int.h"
+#include <signal.h>
+
static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
{
va_list ap;
@@ -23,9 +25,16 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
{
int err, err2;
const char *efunc;
+ sigset_t set, old_set;
p->drv = drv;
+ err = sigfillset (&set);
+ if (err) {
+ logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+
err = pthread_mutex_init (&p->mutex, NULL);
if (err) {
efunc = "pthread_mutex_init";
@@ -38,7 +47,23 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
goto err1;
}
+ err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+ if (err) {
+ efunc = "pthread_sigmask";
+ goto err2;
+ }
+
err = pthread_create (&p->thread, NULL, func, opaque);
+
+ err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed",
+ cap, AUDIO_FUNC);
+ /* We have failed to restore original signal mask, all bets are off,
+ so terminate the process */
+ exit (EXIT_FAILURE);
+ }
+
if (err) {
efunc = "pthread_create";
goto err2;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 9f75f19..d3764c8 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -245,8 +245,8 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
{
HW *hw;
AudioState *s = &glob_audio_state;
- struct audio_driver *drv = glue(s->drv_, TYPE);
- int err;
+ struct audio_driver *drv = s->drv;
+ int err;
if (!glue (s->nb_hw_voices_, TYPE)) {
return NULL;
@@ -433,7 +433,7 @@ SW *glue (AUD_open_, TYPE) (
goto fail;
}
- if (audio_bug (AUDIO_FUNC, !glue (s->drv_, TYPE))) {
+ if (audio_bug (AUDIO_FUNC, !s->drv)) {
dolog ("Can not open `%s' (no host audio driver)\n", name);
goto fail;
}
@@ -452,9 +452,9 @@ SW *glue (AUD_open_, TYPE) (
SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
dolog ("New %s freq %d, bits %d, channels %d\n",
name,
- freq,
- (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
- nchannels);
+ as->freq,
+ (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8,
+ as->nchannels);
#endif
if (live) {
@@ -548,7 +548,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
cur_ts = sw->hw->ts_helper;
old_ts = ts->old_ts;
- /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
+ /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */
if (cur_ts >= old_ts) {
delta = cur_ts - old_ts;
diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c
new file mode 100644
index 0000000..5869052
--- /dev/null
+++ b/audio/audio_win_int.c
@@ -0,0 +1,108 @@
+/* public domain */
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "win-int"
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "audio.h"
+#include "audio_int.h"
+#include "audio_win_int.h"
+
+int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as)
+{
+ memset (wfx, 0, sizeof (*wfx));
+
+ wfx->wFormatTag = WAVE_FORMAT_PCM;
+ wfx->nChannels = as->nchannels;
+ wfx->nSamplesPerSec = as->freq;
+ wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
+ wfx->nBlockAlign = 1 << (as->nchannels == 2);
+ wfx->cbSize = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ wfx->wBitsPerSample = 8;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ wfx->wBitsPerSample = 16;
+ wfx->nAvgBytesPerSec <<= 1;
+ wfx->nBlockAlign <<= 1;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ wfx->wBitsPerSample = 32;
+ wfx->nAvgBytesPerSec <<= 2;
+ wfx->nBlockAlign <<= 2;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", as->freq);
+ return -1;
+ }
+
+ return 0;
+}
+
+int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as)
+{
+ if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
+ dolog ("Invalid wave format, tag is not PCM, but %d\n",
+ wfx->wFormatTag);
+ return -1;
+ }
+
+ if (!wfx->nSamplesPerSec) {
+ dolog ("Invalid wave format, frequency is zero\n");
+ return -1;
+ }
+ as->freq = wfx->nSamplesPerSec;
+
+ switch (wfx->nChannels) {
+ case 1:
+ as->nchannels = 1;
+ break;
+
+ case 2:
+ as->nchannels = 2;
+ break;
+
+ default:
+ dolog (
+ "Invalid wave format, number of channels is not 1 or 2, but %d\n",
+ wfx->nChannels
+ );
+ return -1;
+ }
+
+ switch (wfx->wBitsPerSample) {
+ case 8:
+ as->fmt = AUD_FMT_U8;
+ break;
+
+ case 16:
+ as->fmt = AUD_FMT_S16;
+ break;
+
+ case 32:
+ as->fmt = AUD_FMT_S32;
+ break;
+
+ default:
+ dolog ("Invalid wave format, bits per sample is not "
+ "8, 16 or 32, but %d\n",
+ wfx->wBitsPerSample);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/audio/audio_win_int.h b/audio/audio_win_int.h
new file mode 100644
index 0000000..fa5b3cb
--- /dev/null
+++ b/audio/audio_win_int.h
@@ -0,0 +1,10 @@
+#ifndef AUDIO_WIN_INT_H
+#define AUDIO_WIN_INT_H
+
+int waveformat_from_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as);
+
+int waveformat_to_audio_settings (WAVEFORMATEX *wfx,
+ struct audsettings *as);
+
+#endif /* AUDIO_WIN_INT_H */
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 8abe0c4..26631df 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -27,13 +27,12 @@
#include <string.h> /* strerror */
#include <pthread.h> /* pthread_X */
+#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
-#define ENABLE_IN 1
-
#if 0
# define D(...) fprintf(stderr, __VA_ARGS__)
#else
@@ -152,11 +151,6 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
coreaudio_logstatus (status);
}
-static void coreaudio_atexit (void)
-{
- conf.isAtexit = 1;
-}
-
/***************************************************************************************/
/***************************************************************************************/
/*** ***/
@@ -177,9 +171,8 @@ typedef struct coreAudioVoice {
int pos;
} coreaudioVoice;
-
static inline UInt32
-coreaudio_voice_isPlaying (coreaudioVoice* core)
+coreaudio_voice_isPlaying (coreaudioVoice *core)
{
OSStatus status;
UInt32 result = 0;
@@ -194,8 +187,12 @@ coreaudio_voice_isPlaying (coreaudioVoice* core)
return result;
}
-static int
-coreaudio_voice_lock (coreaudioVoice* core, const char *fn_name)
+static void coreaudio_atexit (void)
+{
+ conf.isAtexit = 1;
+}
+
+static int coreaudio_voice_lock (coreaudioVoice *core, const char *fn_name)
{
int err;
@@ -209,7 +206,7 @@ coreaudio_voice_lock (coreaudioVoice* core, const char *fn_name)
}
static int
-coreaudio_voice_unlock (coreaudioVoice* core, const char *fn_name)
+coreaudio_voice_unlock (coreaudioVoice *core, const char *fn_name)
{
int err;
@@ -465,18 +462,15 @@ typedef struct coreaudioVoiceOut {
#define CORE_OUT(hw) ((coreaudioVoiceOut*)(hw))->core
-static int
-coreaudio_run_out (HWVoiceOut *hw)
+static int coreaudio_run_out (HWVoiceOut *hw, int live)
{
- int live, decr;
- coreaudioVoice *core = CORE_OUT(hw);
+ int decr;
+ coreaudioVoice *core = CORE_OUT(hw);
if (coreaudio_voice_lock (core, "coreaudio_run_out")) {
return 0;
}
- live = audio_pcm_hw_get_live_out (hw);
-
if (core->decr > live) {
ldebug ("core->decr %d live %d core->live %d\n",
core->decr,
@@ -484,19 +478,18 @@ coreaudio_run_out (HWVoiceOut *hw)
core->live);
}
- decr = audio_MIN (core->decr, live);
+ decr = audio_MIN (core->decr, live);
core->decr -= decr;
- core->live = live - decr;
- hw->rpos = core->pos;
+
+ core->live = live - decr;
+ hw->rpos = core->pos;
coreaudio_voice_unlock (core, "coreaudio_run_out");
return decr;
}
-
/* callback to feed audiooutput buffer */
-static OSStatus
-audioOutDeviceIOProc(
+static OSStatus audioOutDeviceIOProc(
AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
@@ -555,27 +548,25 @@ audioOutDeviceIOProc(
rpos = (rpos + frameCount) % hw->samples;
core->decr += frameCount;
- core->pos = rpos;
+ core->pos = rpos;
coreaudio_voice_unlock (core, "audioDeviceIOProc");
return 0;
}
-static int
-coreaudio_write (SWVoiceOut *sw, void *buf, int len)
+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)
{
coreaudioVoice* core = CORE_OUT(hw);
- int err;
+ int err;
audio_pcm_init_info (&hw->info, as);
- err = coreaudio_voice_init( core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0 );
+ err = coreaudio_voice_init (core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0);
if (err < 0)
return err;
@@ -583,21 +574,19 @@ coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
return 0;
}
-static void
-coreaudio_fini_out (HWVoiceOut *hw)
+static void coreaudio_fini_out (HWVoiceOut *hw)
{
+ coreaudioVoice *core = CORE_OUT(hw);
- coreaudioVoice* core = CORE_OUT(hw);
-
- coreaudio_voice_fini(core);
+ coreaudio_voice_fini (core);
}
static int
coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
- coreaudioVoice* core = CORE_OUT(hw);
+ coreaudioVoice *core = CORE_OUT(hw);
- return coreaudio_voice_ctl(core, cmd);
+ return coreaudio_voice_ctl (core, cmd);
}
/***************************************************************************************/
@@ -615,15 +604,14 @@ typedef struct coreaudioVoiceIn {
coreaudioVoice core[1];
} coreaudioVoiceIn;
-#define CORE_IN(hw) ((coreaudioVoiceIn*)(hw))->core
+#define CORE_IN(hw) ((coreaudioVoiceIn *) (hw))->core
-static int
-coreaudio_run_in (HWVoiceIn *hw)
+static int coreaudio_run_in (HWVoiceIn *hw, int live)
{
int decr;
- coreaudioVoice *core = CORE_IN(hw);
+ coreaudioVoice *core = CORE_IN(hw);
if (coreaudio_voice_lock (core, "coreaudio_run_in")) {
return 0;
@@ -639,8 +627,7 @@ coreaudio_run_in (HWVoiceIn *hw)
/* callback to feed audiooutput buffer */
-static OSStatus
-audioInDeviceIOProc(
+static OSStatus audioInDeviceIOProc(
AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
@@ -712,7 +699,7 @@ audioInDeviceIOProc(
static int
coreaudio_read (SWVoiceIn *sw, void *buf, int len)
{
- int result = audio_pcm_sw_read(sw, buf, len);
+ int result = audio_pcm_sw_read (sw, buf, len);
D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result);
return result;
}
@@ -725,7 +712,7 @@ coreaudio_init_in (HWVoiceIn *hw, struct audsettings *as)
audio_pcm_init_info (&hw->info, as);
- err = coreaudio_voice_init( core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1 );
+ err = coreaudio_voice_init (core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1);
if (err < 0) {
return err;
}
@@ -751,71 +738,69 @@ coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
return coreaudio_voice_ctl(core, cmd);
}
-static void*
-coreaudio_audio_init (void)
+static void *coreaudio_audio_init (void)
{
atexit(coreaudio_atexit);
return &coreaudio_audio_init;
}
-static void
-coreaudio_audio_fini (void *opaque)
+static void coreaudio_audio_fini (void *opaque)
{
(void) opaque;
}
static struct audio_option coreaudio_options[] = {
- {"OUT_BUFFER_SIZE", AUD_OPT_INT, &conf.out_buffer_frames,
- "Size of the output buffer in frames", NULL, 0},
- {"OUT_BUFFER_COUNT", AUD_OPT_INT, &conf.out_nbuffers,
- "Number of output buffers", NULL, 0},
- {"IN_BUFFER_SIZE", AUD_OPT_INT, &conf.in_buffer_frames,
- "Size of the input buffer in frames", NULL, 0},
- {"IN_BUFFER_COUNT", AUD_OPT_INT, &conf.in_nbuffers,
- "Number of input buffers", NULL, 0},
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "OUT_BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.out_buffer_frames,
+ .descr = "Size of the output buffer in frames"
+ },
+ {
+ .name = "OUT_BUFFER_COUNT",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.out_nbuffers,
+ .descr = "Number of output buffers"
+ },
+ {
+ .name = "IN_BUFFER_SIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.in_buffer_frames,
+ .descr = "Size of the input buffer in frames"
+ },
+ {
+ .name = "IN_BUFFER_COUNT",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.in_nbuffers,
+ .descr = "Number of input buffers"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops coreaudio_pcm_ops = {
- coreaudio_init_out,
- coreaudio_fini_out,
- coreaudio_run_out,
- coreaudio_write,
- coreaudio_ctl_out,
-
-#if ENABLE_IN
- coreaudio_init_in,
- coreaudio_fini_in,
- coreaudio_run_in,
- coreaudio_read,
- coreaudio_ctl_in
-#else
- NULL,
- NULL,
- NULL,
- NULL,
- NULL
-#endif
+ .init_out = coreaudio_init_out,
+ .fini_out = coreaudio_fini_out,
+ .run_out = coreaudio_run_out,
+ .write = coreaudio_write,
+ .ctl_out = coreaudio_ctl_out
+
+ .init_in = coreaudio_init_in,
+ .fini_in = coreaudio_fini_in,
+ .run_in = coreaudio_run_in,
+ .read = coreaudio_read,
+ .ctl_in = coreaudio_ctl_in
};
struct audio_driver coreaudio_audio_driver = {
- INIT_FIELD (name = ) "coreaudio",
- INIT_FIELD (descr = )
- "CoreAudio (developer.apple.com/audio/coreaudio.html)",
- INIT_FIELD (options = ) coreaudio_options,
- INIT_FIELD (init = ) coreaudio_audio_init,
- INIT_FIELD (fini = ) coreaudio_audio_fini,
- INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
-#if ENABLE_IN
- INIT_FIELD (max_voices_out = ) 1,
- INIT_FIELD (max_voices_in = ) 1,
- INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (coreaudioVoiceIn),
-#else
- INIT_FIELD (max_voices_out = ) 1,
- INIT_FIELD (max_voices_in = ) 0,
- INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
- INIT_FIELD (voice_size_in = ) 0,
-#endif
+ .name = "coreaudio",
+ .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
+ .options = coreaudio_options,
+ .init = coreaudio_audio_init,
+ .fini = coreaudio_audio_fini,
+ .pcm_ops = &coreaudio_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = 1,
+ .max_voices_in = 1,
+ .voice_size_out = sizeof (coreaudioVoiceOut),
+ .voice_size_in = sizeof (coreaudioVoiceIn),
};
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 9cc0b9d..8b37d16 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -174,16 +174,16 @@ static void dsound_fini_out (HWVoiceOut *hw)
}
#ifdef DSBTYPE_IN
-static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
+static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as)
#else
-static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
+static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as)
#endif
{
int err;
HRESULT hr;
dsound *s = &glob_dsound;
WAVEFORMATEX wfx;
- audsettings_t obt_as;
+ struct audsettings obt_as;
#ifdef DSBTYPE_IN
const char *typ = "ADC";
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
@@ -285,7 +285,9 @@ static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
}
#undef NAME
+#undef NAME2
#undef TYPE
#undef IFACE
#undef BUFPTR
#undef FIELD
+#undef FIELD2
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 8284067..e547955 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -26,17 +26,19 @@
* SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
*/
+#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "dsound"
#include "audio_int.h"
-#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>
#include <objbase.h>
#include <dsound.h>
+#include "audio_win_int.h"
+
/* #define DEBUG_DSOUND */
static struct {
@@ -46,28 +48,26 @@ static struct {
int set_primary;
int bufsize_in;
int bufsize_out;
- audsettings_t settings;
+ struct audsettings settings;
int latency_millis;
} conf = {
- 1,
- 1,
- 1,
- 0,
- 16384,
- 16384,
- {
- 44100,
- 2,
- AUD_FMT_S16
- },
- 10
+ .lock_retries = 1,
+ .restore_retries = 1,
+ .getstatus_retries = 1,
+ .set_primary = 0,
+ .bufsize_in = 16384,
+ .bufsize_out = 16384,
+ .settings.freq = 44100,
+ .settings.nchannels = 2,
+ .settings.fmt = AUD_FMT_S16,
+ .latency_millis = 10
};
typedef struct {
LPDIRECTSOUND dsound;
LPDIRECTSOUNDCAPTURE dsound_capture;
LPDIRECTSOUNDBUFFER dsound_primary_buffer;
- audsettings_t settings;
+ struct audsettings settings;
} dsound;
static dsound glob_dsound;
@@ -306,99 +306,6 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
return -1;
}
-static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
-{
- memset (wfx, 0, sizeof (*wfx));
-
- wfx->wFormatTag = WAVE_FORMAT_PCM;
- wfx->nChannels = as->nchannels;
- wfx->nSamplesPerSec = as->freq;
- wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
- wfx->nBlockAlign = 1 << (as->nchannels == 2);
- wfx->cbSize = 0;
-
- switch (as->fmt) {
- case AUD_FMT_S8:
- case AUD_FMT_U8:
- wfx->wBitsPerSample = 8;
- break;
-
- case AUD_FMT_S16:
- case AUD_FMT_U16:
- wfx->wBitsPerSample = 16;
- wfx->nAvgBytesPerSec <<= 1;
- wfx->nBlockAlign <<= 1;
- break;
-
- case AUD_FMT_S32:
- case AUD_FMT_U32:
- wfx->wBitsPerSample = 32;
- wfx->nAvgBytesPerSec <<= 2;
- wfx->nBlockAlign <<= 2;
- break;
-
- default:
- dolog ("Internal logic error: Bad audio format %d\n", as->freq);
- return -1;
- }
-
- return 0;
-}
-
-static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
-{
- if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
- dolog ("Invalid wave format, tag is not PCM, but %d\n",
- wfx->wFormatTag);
- return -1;
- }
-
- if (!wfx->nSamplesPerSec) {
- dolog ("Invalid wave format, frequency is zero\n");
- return -1;
- }
- as->freq = wfx->nSamplesPerSec;
-
- switch (wfx->nChannels) {
- case 1:
- as->nchannels = 1;
- break;
-
- case 2:
- as->nchannels = 2;
- break;
-
- default:
- dolog (
- "Invalid wave format, number of channels is not 1 or 2, but %d\n",
- wfx->nChannels
- );
- return -1;
- }
-
- switch (wfx->wBitsPerSample) {
- case 8:
- as->fmt = AUD_FMT_U8;
- break;
-
- case 16:
- as->fmt = AUD_FMT_S16;
- break;
-
- case 32:
- as->fmt = AUD_FMT_S32;
- break;
-
- default:
- dolog ("Invalid wave format, bits per sample is not "
- "8, 16 or 32, but %d\n",
- wfx->wBitsPerSample);
- return -1;
- }
-
- return 0;
-}
-
#include "dsound_template.h"
#define DSBTYPE_IN
#include "dsound_template.h"
@@ -447,8 +354,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
int src_len1 = dst_len;
int src_len2 = 0;
int pos = hw->rpos + dst_len;
- st_sample_t *src1 = hw->mix_buf + hw->rpos;
- st_sample_t *src2 = NULL;
+ struct st_sample *src1 = hw->mix_buf + hw->rpos;
+ struct st_sample *src2 = NULL;
if (pos > hw->samples) {
src_len1 = hw->samples - hw->rpos;
@@ -658,13 +565,13 @@ static int dsound_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int dsound_run_out (HWVoiceOut *hw)
+static int dsound_run_out (HWVoiceOut *hw, int live)
{
int err;
HRESULT hr;
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
- int live, len, hwshift;
+ int len, hwshift;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
@@ -680,8 +587,6 @@ static int dsound_run_out (HWVoiceOut *hw)
hwshift = hw->info.shift;
bufsize = hw->samples << hwshift;
- live = audio_pcm_hw_get_live_out (hw);
-
hr = IDirectSoundBuffer_GetCurrentPosition (
dsb,
&ppos,
@@ -1033,54 +938,93 @@ static void *dsound_audio_init (void)
}
static struct audio_option dsound_options[] = {
- {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
- "Number of times to attempt locking the buffer", NULL, 0},
- {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
- "Number of times to attempt restoring the buffer", NULL, 0},
- {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
- "Number of times to attempt getting status of the buffer", NULL, 0},
- {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
- "Set the parameters of primary buffer", NULL, 0},
- {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
- "(undocumented)", NULL, 0},
- {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
- "Primary buffer frequency", NULL, 0},
- {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
- "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
- {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
- "Primary buffer format", NULL, 0},
- {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
- "(undocumented)", NULL, 0},
- {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
- "(undocumented)", NULL, 0},
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "LOCK_RETRIES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.lock_retries,
+ .descr = "Number of times to attempt locking the buffer"
+ },
+ {
+ .name = "RESTOURE_RETRIES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.restore_retries,
+ .descr = "Number of times to attempt restoring the buffer"
+ },
+ {
+ .name = "GETSTATUS_RETRIES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.getstatus_retries,
+ .descr = "Number of times to attempt getting status of the buffer"
+ },
+ {
+ .name = "SET_PRIMARY",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.set_primary,
+ .descr = "Set the parameters of primary buffer"
+ },
+ {
+ .name = "LATENCY_MILLIS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.latency_millis,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "PRIMARY_FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.settings.freq,
+ .descr = "Primary buffer frequency"
+ },
+ {
+ .name = "PRIMARY_CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.settings.nchannels,
+ .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "PRIMARY_FMT",
+ .tag = AUD_OPT_FMT,
+ .valp = &conf.settings.fmt,
+ .descr = "Primary buffer format"
+ },
+ {
+ .name = "BUFSIZE_OUT",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.bufsize_out,
+ .descr = "(undocumented)"
+ },
+ {
+ .name = "BUFSIZE_IN",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.bufsize_in,
+ .descr = "(undocumented)"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops dsound_pcm_ops = {
- dsound_init_out,
- dsound_fini_out,
- dsound_run_out,
- dsound_write,
- dsound_ctl_out,
-
- dsound_init_in,
- dsound_fini_in,
- dsound_run_in,
- dsound_read,
- dsound_ctl_in
+ .init_out = dsound_init_out,
+ .fini_out = dsound_fini_out,
+ .run_out = dsound_run_out,
+ .write = dsound_write,
+ .ctl_out = dsound_ctl_out,
+
+ .init_in = dsound_init_in,
+ .fini_in = dsound_fini_in,
+ .run_in = dsound_run_in,
+ .read = dsound_read,
+ .ctl_in = dsound_ctl_in
};
struct audio_driver dsound_audio_driver = {
- INIT_FIELD (name = ) "dsound",
- INIT_FIELD (descr = )
- "DirectSound audio (www.wikipedia.org/wiki/DirectSound)",
- INIT_FIELD (options = ) dsound_options,
- INIT_FIELD (init = ) dsound_audio_init,
- INIT_FIELD (fini = ) dsound_audio_fini,
- INIT_FIELD (pcm_ops = ) &dsound_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) INT_MAX,
- INIT_FIELD (max_voices_in = ) 1,
- INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn)
+ .name = "dsound",
+ .descr = "DirectSound http://wikipedia.org/wiki/DirectSound",
+ .options = dsound_options,
+ .init = dsound_audio_init,
+ .fini = dsound_audio_fini,
+ .pcm_ops = &dsound_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = 1,
+ .voice_size_out = sizeof (DSoundVoiceOut),
+ .voice_size_in = sizeof (DSoundVoiceIn)
};
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
index 1d72125..84cbcb4 100644
--- a/audio/esdaudio.c
+++ b/audio/esdaudio.c
@@ -25,11 +25,10 @@
#include <esd.h>
#include "qemu-common.h"
#include "audio.h"
-#include <signal.h>
#define AUDIO_CAP "esd"
#include "audio_int.h"
-#include <dlfcn.h>
+#include "audio_pt_int.h"
#include "qemu_debug.h"
@@ -51,6 +50,23 @@
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
+#include <dlfcn.h>
+/* link dynamically to the libesd.so */
+
+#define DYNLINK_FUNCTIONS \
+ DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*)) \
+ DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
+ DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \
+ DYNLINK_FUNC(int,esd_close,(int)) \
+
+#define DYNLINK_FUNCTIONS_INIT \
+ esd_dynlink_init
+
+#include "dynlink.h"
+
+static void* esd_lib;
+
+
typedef struct {
HWVoiceOut hw;
int done;
@@ -59,6 +75,7 @@ typedef struct {
int rpos;
void *pcm_buf;
int fd;
+ struct audio_pt pt;
} ESDVoiceOut;
typedef struct {
@@ -69,6 +86,7 @@ typedef struct {
int wpos;
void *pcm_buf;
int fd;
+ struct audio_pt pt;
} ESDVoiceIn;
static struct {
@@ -77,27 +95,10 @@ static struct {
char *dac_host;
char *adc_host;
} conf = {
- 1024,
- 2,
- NULL,
- NULL
+ .samples = 1024,
+ .divisor = 2,
};
-/* link dynamically to the libesd.so */
-
-#define DYNLINK_FUNCTIONS \
- DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*)) \
- DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
- DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \
- DYNLINK_FUNC(int,esd_close,(int)) \
-
-#define DYNLINK_FUNCTIONS_INIT \
- esd_dynlink_init
-
-#include "dynlink.h"
-
-static void* esd_lib;
-
static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
{
va_list ap;
@@ -109,50 +110,111 @@ static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
}
-static int qesd_run_out (HWVoiceOut *hw)
+/* playback */
+static void *qesd_thread_out (void *arg)
{
- ESDVoiceOut *esd = (ESDVoiceOut *) hw;
- int liveSamples, totalSamples;
- int rpos, nwrite, writeSamples, writeBytes;
-
- liveSamples = audio_pcm_hw_get_live_out (hw);
- rpos = hw->rpos;
- totalSamples = 0;
-
- while (liveSamples > 0) {
- int chunkSamples = audio_MIN (liveSamples, hw->samples - rpos);
- int chunkBytes = chunkSamples << hw->info.shift;
- struct st_sample *src = hw->mix_buf + rpos;
-
- hw->clip (esd->pcm_buf, src, chunkSamples);
-
- AGAIN:
- nwrite = write (esd->fd, esd->pcm_buf, chunkBytes);
- if (nwrite == -1) {
- if (errno == EINTR)
- goto AGAIN;
- if (errno == EAGAIN || errno == EWOULDBLOCK)
+ ESDVoiceOut *esd = arg;
+ HWVoiceOut *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ for (;;) {
+ int decr, to_mix, rpos;
+
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->live > threshold) {
+ break;
+ }
+
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ decr = to_mix = esd->live;
+ rpos = hw->rpos;
+
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ while (to_mix) {
+ ssize_t written;
+ int chunk = audio_MIN (to_mix, hw->samples - rpos);
+ struct st_sample *src = hw->mix_buf + rpos;
+
+ hw->clip (esd->pcm_buf, src, chunk);
+
+ again:
+ written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
+ if (written == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ qesd_logerr (errno, "write failed\n");
+ return NULL;
+ }
+
+ if (written != chunk << hw->info.shift) {
+ int wsamples = written >> hw->info.shift;
+ int wbytes = wsamples << hw->info.shift;
+ if (wbytes != written) {
+ dolog ("warning: Misaligned write %d (requested %zd), "
+ "alignment %d\n",
+ wbytes, written, hw->info.align + 1);
+ }
+ to_mix -= wsamples;
+ rpos = (rpos + wsamples) % hw->samples;
break;
- qesd_logerr (errno, "write failed: %s\n", strerror(errno));
- O("EsounD output thread write error: %s", strerror(errno));
- break;
+ }
+
+ rpos = (rpos + chunk) % hw->samples;
+ to_mix -= chunk;
}
- if (nwrite == 0)
- break;
-
- writeSamples = nwrite >> hw->info.shift;
- writeBytes = writeSamples << hw->info.shift;
- if (writeBytes != nwrite) {
- dolog ("warning: Misaligned write %d (requested %d), "
- "alignment %d\n",
- nwrite, writeBytes, hw->info.align + 1);
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
}
- rpos = (rpos + writeSamples) % hw->samples;
- totalSamples += writeSamples;
- liveSamples -= writeSamples;
+
+ esd->rpos = rpos;
+ esd->live -= decr;
+ esd->decr += decr;
+ }
+
+ exit:
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qesd_run_out (HWVoiceOut *hw, int live)
+{
+ int decr;
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ decr = audio_MIN (live, esd->decr);
+ esd->decr -= decr;
+ esd->live = live - decr;
+ hw->rpos = esd->rpos;
+ if (esd->live > 0) {
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
}
- hw->rpos = rpos;
- return totalSamples;
+ return decr;
}
static int qesd_write (SWVoiceOut *sw, void *buf, int len)
@@ -165,13 +227,7 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
struct audsettings obt_as = *as;
int esdfmt = ESD_STREAM | ESD_PLAY;
- int result = -1;
- /* shut down verbose debug spew */
- if (!D_ACTIVE)
- stdio_disable();
-
- O("initializing EsoundD audio output");
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
switch (as->fmt) {
case AUD_FMT_S8:
@@ -179,11 +235,11 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
esdfmt |= ESD_BITS8;
obt_as.fmt = AUD_FMT_U8;
break;
-#if 0
+
case AUD_FMT_S32:
case AUD_FMT_U32:
dolog ("Will use 16 instead of 32 bit samples\n");
-#endif
+
case AUD_FMT_S16:
case AUD_FMT_U16:
deffmt:
@@ -205,50 +261,53 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
if (!esd->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
- goto exit;
+ return -1;
}
esd->fd = FF(esd_play_stream) (esdfmt, as->freq, conf.dac_host, NULL);
if (esd->fd < 0) {
- if (conf.dac_host == NULL) {
- esd->fd = FF(esd_play_stream) (esdfmt, as->freq, "localhost", NULL);
- }
- if (esd->fd < 0) {
- qesd_logerr (errno, "esd_play_stream failed\n");
- goto fail2;
- }
+ qesd_logerr (errno, "esd_play_stream failed\n");
+ goto fail1;
}
- {
- int flags;
- flags = fcntl(esd->fd, F_GETFL);
- fcntl(esd->fd, F_SETFL, flags | O_NONBLOCK);
+ if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail2;
}
- result = 0; /* success */
- goto exit;
+ return 0;
fail2:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail1:
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
-
- exit:
- if (!D_ACTIVE)
- stdio_enable();
-
- return result;
+ return -1;
}
static void qesd_fini_out (HWVoiceOut *hw)
{
+ void *ret;
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+ audio_pt_lock (&esd->pt, AUDIO_FUNC);
+ esd->done = 1;
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
if (esd->fd >= 0) {
if (close (esd->fd)) {
qesd_logerr (errno, "failed to close esd socket\n");
}
esd->fd = -1;
}
+
+ audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
}
@@ -261,56 +320,112 @@ static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
}
/* capture */
-static int qesd_run_in (HWVoiceIn *hw)
+static void *qesd_thread_in (void *arg)
{
- int wpos, liveSamples, totalSamples;
- int grabSamples;
- ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+ ESDVoiceIn *esd = arg;
+ HWVoiceIn *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ for (;;) {
+ int incr, to_grab, wpos;
- wpos = hw->wpos;
- liveSamples = audio_pcm_hw_get_live_in (hw);
- grabSamples = hw->samples - liveSamples;
- totalSamples = 0;
-
- while (grabSamples > 0) {
- ssize_t nread;
- int chunkSamples = audio_MIN (grabSamples, hw->samples - wpos);
- int chunkBytes = chunkSamples << hw->info.shift;
- int readSamples, readBytes;
- void* buf = advance (esd->pcm_buf, wpos);
-
- AGAIN:
- nread = read (esd->fd, buf, chunkBytes);
- if (nread == -1) {
- if (errno == EINTR)
- goto AGAIN;
- if (errno == EAGAIN || errno == EWOULDBLOCK)
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->dead > threshold) {
break;
+ }
- qesd_logerr (errno, "read failed: %s\n", strerror(errno));
- break;
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
}
- if (nread == 0)
- break;
- readSamples = nread >> hw->info.shift;
- readBytes = readSamples << hw->info.shift;
+ incr = to_grab = esd->dead;
+ wpos = hw->wpos;
- if (readBytes != nread) {
- dolog ("warning: Misaligned read %d (requested %d), "
- "alignment %d\n",
- nread, readBytes, hw->info.align + 1);
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
}
- hw->conv (hw->conv_buf + wpos, buf, readSamples,
- &nominal_volume);
+ while (to_grab) {
+ ssize_t nread;
+ int chunk = audio_MIN (to_grab, hw->samples - wpos);
+ void *buf = advance (esd->pcm_buf, wpos);
+
+ again:
+ nread = read (esd->fd, buf, chunk << hw->info.shift);
+ if (nread == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ qesd_logerr (errno, "read failed\n");
+ return NULL;
+ }
+
+ if (nread != chunk << hw->info.shift) {
+ int rsamples = nread >> hw->info.shift;
+ int rbytes = rsamples << hw->info.shift;
+ if (rbytes != nread) {
+ dolog ("warning: Misaligned write %d (requested %zd), "
+ "alignment %d\n",
+ rbytes, nread, hw->info.align + 1);
+ }
+ to_grab -= rsamples;
+ wpos = (wpos + rsamples) % hw->samples;
+ break;
+ }
+
+ hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
+ &nominal_volume);
+ wpos = (wpos + chunk) % hw->samples;
+ to_grab -= chunk;
+ }
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
- wpos = (wpos + readSamples) % hw->samples;
- grabSamples -= readSamples;
- totalSamples += readSamples;
+ esd->wpos = wpos;
+ esd->dead -= incr;
+ esd->incr += incr;
}
- hw->wpos = wpos;
- return totalSamples;
+
+ exit:
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qesd_run_in (HWVoiceIn *hw)
+{
+ int live, incr, dead;
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ incr = audio_MIN (dead, esd->incr);
+ esd->incr -= incr;
+ esd->dead = dead - incr;
+ hw->wpos = esd->wpos;
+ if (esd->dead > 0) {
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ }
+ return incr;
}
static int qesd_read (SWVoiceIn *sw, void *buf, int len)
@@ -323,11 +438,6 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
struct audsettings obt_as = *as;
int esdfmt = ESD_STREAM | ESD_RECORD;
- int result = -1;
-
- /* shut down verbose debug spew */
- if (!D_ACTIVE)
- stdio_disable();
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
switch (as->fmt) {
@@ -359,50 +469,53 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
if (!esd->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
- goto exit;
+ return -1;
}
esd->fd = FF(esd_record_stream) (esdfmt, as->freq, conf.adc_host, NULL);
if (esd->fd < 0) {
- if (conf.adc_host == NULL) {
- esd->fd = FF(esd_record_stream) (esdfmt, as->freq, "localhost", NULL);
- }
- if (esd->fd < 0) {
- qesd_logerr (errno, "esd_record_stream failed\n");
- goto fail2;
- }
+ qesd_logerr (errno, "esd_record_stream failed\n");
+ goto fail1;
}
- {
- int flags;
- flags = fcntl(esd->fd, F_GETFL);
- fcntl(esd->fd, F_SETFL, flags | O_NONBLOCK);
+ if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail2;
}
- result = 0; /* success */
- goto exit;
+ return 0;
fail2:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail1:
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
-
- exit:
- if (!D_ACTIVE)
- stdio_enable();
-
- return result;
+ return -1;
}
static void qesd_fini_in (HWVoiceIn *hw)
{
+ void *ret;
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+ audio_pt_lock (&esd->pt, AUDIO_FUNC);
+ esd->done = 1;
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
if (esd->fd >= 0) {
if (close (esd->fd)) {
qesd_logerr (errno, "failed to close esd socket\n");
}
esd->fd = -1;
}
+
+ audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
}
@@ -473,46 +586,57 @@ static void qesd_audio_fini (void *opaque)
}
struct audio_option qesd_options[] = {
- {"SAMPLES", AUD_OPT_INT, &conf.samples,
- "buffer size in samples", NULL, 0},
-
- {"DIVISOR", AUD_OPT_INT, &conf.divisor,
- "threshold divisor", NULL, 0},
-
- {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
- "playback host", NULL, 0},
-
- {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
- "capture host", NULL, 0},
-
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.samples,
+ .descr = "buffer size in samples"
+ },
+ {
+ .name = "DIVISOR",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.divisor,
+ .descr = "threshold divisor"
+ },
+ {
+ .name = "DAC_HOST",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.dac_host,
+ .descr = "playback host"
+ },
+ {
+ .name = "ADC_HOST",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.adc_host,
+ .descr = "capture host"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops qesd_pcm_ops = {
- qesd_init_out,
- qesd_fini_out,
- qesd_run_out,
- qesd_write,
- qesd_ctl_out,
-
- qesd_init_in,
- qesd_fini_in,
- qesd_run_in,
- qesd_read,
- qesd_ctl_in,
+ .init_out = qesd_init_out,
+ .fini_out = qesd_fini_out,
+ .run_out = qesd_run_out,
+ .write = qesd_write,
+ .ctl_out = qesd_ctl_out,
+
+ .init_in = qesd_init_in,
+ .fini_in = qesd_fini_in,
+ .run_in = qesd_run_in,
+ .read = qesd_read,
+ .ctl_in = qesd_ctl_in,
};
struct audio_driver esd_audio_driver = {
- INIT_FIELD (name = ) "esd",
- INIT_FIELD (descr = )
- "EsounD audio (en.wikipedia.org/wiki/Esound)",
- INIT_FIELD (options = ) qesd_options,
- INIT_FIELD (init = ) qesd_audio_init,
- INIT_FIELD (fini = ) qesd_audio_fini,
- INIT_FIELD (pcm_ops = ) &qesd_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) INT_MAX,
- INIT_FIELD (max_voices_in = ) 1,
- INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn)
+ .name = "esd",
+ .descr = "http://en.wikipedia.org/wiki/Esound",
+ .options = qesd_options,
+ .init = qesd_audio_init,
+ .fini = qesd_audio_fini,
+ .pcm_ops = &qesd_pcm_ops,
+ .can_be_default = 0,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (ESDVoiceOut),
+ .voice_size_in = sizeof (ESDVoiceIn)
};
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
index 0becd3b..7f08e14 100644
--- a/audio/fmodaudio.c
+++ b/audio/fmodaudio.c
@@ -47,16 +47,11 @@ static struct {
int freq;
int nb_channels;
int bufsize;
- int threshold;
int broken_adc;
} conf = {
- NULL,
- 2048 * 2,
- 44100,
- 2,
- 0,
- 0,
- 0
+ .nb_samples = 2048 * 2,
+ .freq = 44100,
+ .nb_channels = 2,
};
static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
@@ -229,24 +224,15 @@ static int fmod_lock_sample (
return 0;
}
-static int fmod_run_out (HWVoiceOut *hw)
+static int fmod_run_out (HWVoiceOut *hw, int live)
{
FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
- int live, decr;
+ int decr;
void *p1 = 0, *p2 = 0;
unsigned int blen1 = 0, blen2 = 0;
unsigned int len1 = 0, len2 = 0;
- int nb_live;
- live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
- if (!live) {
- return 0;
- }
-
- if (!hw->pending_disable
- && nb_live
- && (conf.threshold && live <= conf.threshold)) {
- ldebug ("live=%d nb_live=%d\n", live, nb_live);
+ if (!hw->pending_disable) {
return 0;
}
@@ -517,27 +503,27 @@ static struct {
const char *name;
int type;
} drvtab[] = {
- {"none", FSOUND_OUTPUT_NOSOUND},
+ { .name = "none", .type = FSOUND_OUTPUT_NOSOUND },
#ifdef _WIN32
- {"winmm", FSOUND_OUTPUT_WINMM},
- {"dsound", FSOUND_OUTPUT_DSOUND},
- {"a3d", FSOUND_OUTPUT_A3D},
- {"asio", FSOUND_OUTPUT_ASIO},
+ { .name = "winmm", .type = FSOUND_OUTPUT_WINMM },
+ { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND },
+ { .name = "a3d", .type = FSOUND_OUTPUT_A3D },
+ { .name = "asio", .type = FSOUND_OUTPUT_ASIO },
#endif
#ifdef __linux__
- {"oss", FSOUND_OUTPUT_OSS},
- {"alsa", FSOUND_OUTPUT_ALSA},
- {"esd", FSOUND_OUTPUT_ESD},
+ { .name = "oss", .type = FSOUND_OUTPUT_OSS },
+ { .name = "alsa", .type = FSOUND_OUTPUT_ALSA },
+ { .name = "esd", .type = FSOUND_OUTPUT_ESD },
#endif
#ifdef __APPLE__
- {"mac", FSOUND_OUTPUT_MAC},
+ { .name = "mac", .type = FSOUND_OUTPUT_MAC },
#endif
#if 0
- {"xbox", FSOUND_OUTPUT_XBOX},
- {"ps2", FSOUND_OUTPUT_PS2},
- {"gcube", FSOUND_OUTPUT_GC},
+ { .name = "xbox", .type = FSOUND_OUTPUT_XBOX },
+ { .name = "ps2", .type = FSOUND_OUTPUT_PS2 },
+ { .name = "gcube", .type = FSOUND_OUTPUT_GC },
#endif
- {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
+ { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME }
};
static void *fmod_audio_init (void)
@@ -639,48 +625,63 @@ static void fmod_audio_fini (void *opaque)
}
static struct audio_option fmod_options[] = {
- {"DRV", AUD_OPT_STR, &conf.drvname,
- "FMOD driver", NULL, 0},
- {"FREQ", AUD_OPT_INT, &conf.freq,
- "Default frequency", NULL, 0},
- {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
- "Buffer size in samples", NULL, 0},
- {"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
- "Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
- {"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
- "(undocumented)", NULL, 0},
-#if 0
- {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
- "(undocumented)"},
-#endif
-
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "DRV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.drvname,
+ .descr = "FMOD driver"
+ },
+ {
+ .name = "FREQ",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.freq,
+ .descr = "Default frequency"
+ },
+ {
+ .name = "SAMPLES",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nb_samples,
+ .descr = "Buffer size in samples"
+ },
+ {
+ .name = "CHANNELS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nb_channels,
+ .descr = "Number of default channels (1 - mono, 2 - stereo)"
+ },
+ {
+ .name = "BUFSIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.bufsize,
+ .descr = "(undocumented)"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops fmod_pcm_ops = {
- fmod_init_out,
- fmod_fini_out,
- fmod_run_out,
- fmod_write,
- fmod_ctl_out,
-
- fmod_init_in,
- fmod_fini_in,
- fmod_run_in,
- fmod_read,
- fmod_ctl_in
+ .init_out = fmod_init_out,
+ .fini_out = fmod_fini_out,
+ .run_out = fmod_run_out,
+ .write = fmod_write,
+ .ctl_out = fmod_ctl_out,
+
+ .init_in = fmod_init_in,
+ .fini_in = fmod_fini_in,
+ .run_in = fmod_run_in,
+ .read = fmod_read,
+ .ctl_in = fmod_ctl_in
};
struct audio_driver fmod_audio_driver = {
- INIT_FIELD (name = ) "fmod",
- INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
- INIT_FIELD (options = ) fmod_options,
- INIT_FIELD (init = ) fmod_audio_init,
- INIT_FIELD (fini = ) fmod_audio_fini,
- INIT_FIELD (pcm_ops = ) &fmod_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) INT_MAX,
- INIT_FIELD (max_voices_in = ) INT_MAX,
- INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
+ .name = "fmod",
+ .descr = "FMOD 3.xx http://www.fmod.org",
+ .options = fmod_options,
+ .init = fmod_audio_init,
+ .fini = fmod_audio_fini,
+ .pcm_ops = &fmod_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (FMODVoiceOut),
+ .voice_size_in = sizeof (FMODVoiceIn)
};
diff --git a/audio/mixeng.c b/audio/mixeng.c
index 8ce942e..9f1d93f 100644
--- a/audio/mixeng.c
+++ b/audio/mixeng.c
@@ -123,7 +123,7 @@
#undef IN_T
#undef SHIFT
-/* Unsigned 16 bit */
+/* Unsigned 32 bit */
#define IN_T uint32_t
#define IN_MIN 0
#define IN_MAX UINT32_MAX
diff --git a/audio/noaudio.c b/audio/noaudio.c
index 0209edb..4925234 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -38,19 +38,14 @@ typedef struct NoVoiceIn {
int64_t old_ticks;
} NoVoiceIn;
-static int no_run_out (HWVoiceOut *hw)
+static int no_run_out (HWVoiceOut *hw, int live)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
- int live, decr, samples;
+ int decr, samples;
int64_t now;
int64_t ticks;
int64_t bytes;
- live = audio_pcm_hw_get_live_out (&no->hw);
- if (!live) {
- return 0;
- }
-
now = qemu_get_clock (vm_clock);
ticks = now - no->old_ticks;
bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
@@ -147,29 +142,29 @@ static void no_audio_fini (void *opaque)
}
static struct audio_pcm_ops no_pcm_ops = {
- no_init_out,
- no_fini_out,
- no_run_out,
- no_write,
- no_ctl_out,
-
- no_init_in,
- no_fini_in,
- no_run_in,
- no_read,
- no_ctl_in
+ .init_out = no_init_out,
+ .fini_out = no_fini_out,
+ .run_out = no_run_out,
+ .write = no_write,
+ .ctl_out = no_ctl_out,
+
+ .init_in = no_init_in,
+ .fini_in = no_fini_in,
+ .run_in = no_run_in,
+ .read = no_read,
+ .ctl_in = no_ctl_in
};
struct audio_driver no_audio_driver = {
- INIT_FIELD (name = ) "none",
- INIT_FIELD (descr = ) "disabled audio",
- INIT_FIELD (options = ) NULL,
- INIT_FIELD (init = ) no_audio_init,
- INIT_FIELD (fini = ) no_audio_fini,
- INIT_FIELD (pcm_ops = ) &no_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) INT_MAX,
- INIT_FIELD (max_voices_in = ) INT_MAX,
- INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn)
+ .name = "none",
+ .descr = "Timer based audio emulation",
+ .options = NULL,
+ .init = no_audio_init,
+ .fini = no_audio_fini,
+ .pcm_ops = &no_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (NoVoiceOut),
+ .voice_size_in = sizeof (NoVoiceIn)
};
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index ceb81ce..42bffae 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -31,36 +31,26 @@
#include <sys/soundcard.h>
#endif
#include "qemu-common.h"
+#include "host-utils.h"
+#include "qemu-char.h"
#include "audio.h"
#define AUDIO_CAP "oss"
#include "audio_int.h"
-/* http://www.df.lth.se/~john_e/gems/gem002d.html */
-/* http://www.multi-platforms.com/Tips/PopCount.htm */
-uint32_t popcount (uint32_t u)
-{
- u = ((u&0x55555555) + ((u>>1)&0x55555555));
- u = ((u&0x33333333) + ((u>>2)&0x33333333));
- u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
- u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
- u = ( u&0x0000ffff) + (u>>16);
- return u;
-}
-
-inline uint32_t lsbindex (uint32_t u)
-{
- return popcount ((u&-u)-1);
-}
+#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
+#define USE_DSP_POLICY
+#endif
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
int fd;
+ int wpos;
int nfrags;
int fragsize;
int mmapped;
- int old_optr;
+ int pending;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
@@ -69,7 +59,6 @@ typedef struct OSSVoiceIn {
int fd;
int nfrags;
int fragsize;
- int old_optr;
} OSSVoiceIn;
static struct {
@@ -79,13 +68,17 @@ static struct {
const char *devpath_out;
const char *devpath_in;
int debug;
+ int exclusive;
+ int policy;
} conf = {
.try_mmap = 0,
.nfrags = 4,
.fragsize = 4096,
.devpath_out = "/dev/dsp",
.devpath_in = "/dev/dsp",
- .debug = 0
+ .debug = 0,
+ .exclusive = 0,
+ .policy = 5
};
struct oss_params {
@@ -127,13 +120,42 @@ static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
static void oss_anal_close (int *fdp)
{
- int err = close (*fdp);
+ int err;
+
+ qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
+ err = close (*fdp);
if (err) {
oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
}
*fdp = -1;
}
+static void oss_helper_poll_out (void *opaque)
+{
+ (void) opaque;
+ audio_run ("oss_poll_out");
+}
+
+static void oss_helper_poll_in (void *opaque)
+{
+ (void) opaque;
+ audio_run ("oss_poll_in");
+}
+
+static int oss_poll_out (HWVoiceOut *hw)
+{
+ OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+ return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
+}
+
+static int oss_poll_in (HWVoiceIn *hw)
+{
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+ return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
+}
+
static int oss_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
@@ -218,17 +240,46 @@ static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
}
#endif
+#ifdef USE_DSP_POLICY
+static int oss_get_version (int fd, int *version, const char *typ)
+{
+ if (ioctl (fd, OSS_GETVERSION, &version)) {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /*
+ * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
+ * since 7.x, but currently only on the mixer device (or in
+ * the Linuxolator), and in the native version that part of
+ * the code is in fact never reached so the ioctl fails anyway.
+ * Until this is fixed, just check the errno and if its what
+ * FreeBSD's sound drivers return atm assume they are new enough.
+ */
+ if (errno == EINVAL) {
+ *version = 0x040000;
+ return 0;
+ }
+#endif
+ oss_logerr2 (errno, typ, "Failed to get OSS version\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd)
{
int fd;
- int mmmmssss;
+ int oflags = conf.exclusive ? O_EXCL : 0;
audio_buf_info abinfo;
int fmt, freq, nchannels;
+ int setfragment = 1;
const char *dspname = in ? conf.devpath_in : conf.devpath_out;
const char *typ = in ? "ADC" : "DAC";
- fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
+ /* Kludge needed to have working mmap on Linux */
+ oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY);
+
+ fd = open (dspname, oflags | O_NONBLOCK);
if (-1 == fd) {
oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
return -1;
@@ -259,11 +310,36 @@ static int oss_open (int in, struct oss_params *req,
goto err;
}
- mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
- if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
- oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
- req->nfrags, req->fragsize);
- goto err;
+#ifdef USE_DSP_POLICY
+ if (conf.policy >= 0) {
+ int version;
+
+ if (!oss_get_version (fd, &version, typ)) {
+ if (conf.debug) {
+ dolog ("OSS version = %#x\n", version);
+ }
+
+ if (version >= 0x040000) {
+ int policy = conf.policy;
+ if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+ oss_logerr2 (errno, typ,
+ "Failed to set timing policy to %d\n",
+ conf.policy);
+ goto err;
+ }
+ setfragment = 0;
+ }
+ }
+ }
+#endif
+
+ if (setfragment) {
+ int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
+ if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
+ oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
+ req->nfrags, req->fragsize);
+ goto err;
+ }
}
if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
@@ -305,26 +381,58 @@ static int oss_open (int in, struct oss_params *req,
return -1;
}
-static int oss_run_out (HWVoiceOut *hw)
+static void oss_write_pending (OSSVoiceOut *oss)
+{
+ HWVoiceOut *hw = &oss->hw;
+
+ if (oss->mmapped) {
+ return;
+ }
+
+ while (oss->pending) {
+ int samples_written;
+ ssize_t bytes_written;
+ int samples_till_end = hw->samples - oss->wpos;
+ int samples_to_write = audio_MIN (oss->pending, samples_till_end);
+ int bytes_to_write = samples_to_write << hw->info.shift;
+ void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
+
+ bytes_written = write (oss->fd, pcm, bytes_to_write);
+ if (bytes_written < 0) {
+ if (errno != EAGAIN) {
+ oss_logerr (errno, "failed to write %d bytes\n",
+ bytes_to_write);
+ }
+ break;
+ }
+
+ if (bytes_written & hw->info.align) {
+ dolog ("misaligned write asked for %d, but got %zd\n",
+ bytes_to_write, bytes_written);
+ return;
+ }
+
+ samples_written = bytes_written >> hw->info.shift;
+ oss->pending -= samples_written;
+ oss->wpos = (oss->wpos + samples_written) % hw->samples;
+ if (bytes_written - bytes_to_write) {
+ break;
+ }
+ }
+}
+
+static int oss_run_out (HWVoiceOut *hw, int live)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
- int err, rpos, live, decr;
- int samples;
- uint8_t *dst;
- struct st_sample *src;
+ int err, decr;
struct audio_buf_info abinfo;
struct count_info cntinfo;
int bufsize;
- live = audio_pcm_hw_get_live_out (hw);
- if (!live) {
- return 0;
- }
-
bufsize = hw->samples << hw->info.shift;
if (oss->mmapped) {
- int bytes;
+ int bytes, pos;
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
@@ -332,20 +440,8 @@ static int oss_run_out (HWVoiceOut *hw)
return 0;
}
- if (cntinfo.ptr == oss->old_optr) {
- if (abs (hw->samples - live) < 64) {
- dolog ("warning: Overrun\n");
- }
- return 0;
- }
-
- if (cntinfo.ptr > oss->old_optr) {
- bytes = cntinfo.ptr - oss->old_optr;
- }
- else {
- bytes = bufsize + cntinfo.ptr - oss->old_optr;
- }
-
+ pos = hw->rpos << hw->info.shift;
+ bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
decr = audio_MIN (bytes >> hw->info.shift, live);
}
else {
@@ -358,7 +454,7 @@ static int oss_run_out (HWVoiceOut *hw)
if (abinfo.bytes > bufsize) {
if (conf.debug) {
dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
- "please report your OS/audio hw to malc@pulsesoft.com\n",
+ "please report your OS/audio hw to av1474@comtv.ru\n",
abinfo.bytes, bufsize);
}
abinfo.bytes = bufsize;
@@ -378,53 +474,10 @@ static int oss_run_out (HWVoiceOut *hw)
}
}
- samples = decr;
- rpos = hw->rpos;
- while (samples) {
- int left_till_end_samples = hw->samples - rpos;
- int convert_samples = audio_MIN (samples, left_till_end_samples);
-
- src = hw->mix_buf + rpos;
- dst = advance (oss->pcm_buf, rpos << hw->info.shift);
-
- hw->clip (dst, src, convert_samples);
- if (!oss->mmapped) {
- int written;
+ decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
+ oss->pending += decr;
+ oss_write_pending (oss);
- written = write (oss->fd, dst, convert_samples << hw->info.shift);
- /* XXX: follow errno recommendations ? */
- if (written == -1) {
- oss_logerr (
- errno,
- "Failed to write %d bytes of audio data from %p\n",
- convert_samples << hw->info.shift,
- dst
- );
- continue;
- }
-
- if (written != convert_samples << hw->info.shift) {
- int wsamples = written >> hw->info.shift;
- int wbytes = wsamples << hw->info.shift;
- if (wbytes != written) {
- dolog ("warning: Misaligned write %d (requested %d), "
- "alignment %d\n",
- wbytes, written, hw->info.align + 1);
- }
- decr -= wsamples;
- rpos = (rpos + wsamples) % hw->samples;
- break;
- }
- }
-
- rpos = (rpos + convert_samples) % hw->samples;
- samples -= convert_samples;
- }
- if (oss->mmapped) {
- oss->old_optr = cntinfo.ptr;
- }
-
- hw->rpos = rpos;
return decr;
}
@@ -498,7 +551,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
oss->mmapped = 0;
if (conf.try_mmap) {
oss->pcm_buf = mmap (
- 0,
+ NULL,
hw->samples << hw->info.shift,
PROT_READ | PROT_WRITE,
MAP_SHARED,
@@ -508,7 +561,8 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
if (oss->pcm_buf == MAP_FAILED) {
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
hw->samples << hw->info.shift);
- } else {
+ }
+ else {
int err;
int trig = 0;
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@@ -563,25 +617,48 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
- if (!oss->mmapped) {
- return 0;
- }
-
switch (cmd) {
case VOICE_ENABLE:
- ldebug ("enabling voice\n");
- audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
- trig = PCM_ENABLE_OUTPUT;
- if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
- oss_logerr (
- errno,
- "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
- );
- return -1;
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ ldebug ("enabling voice\n");
+ if (poll_mode && oss_poll_out (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+
+ if (!oss->mmapped) {
+ return 0;
+ }
+
+ audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+ trig = PCM_ENABLE_OUTPUT;
+ if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (
+ errno,
+ "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+ );
+ return -1;
+ }
}
break;
case VOICE_DISABLE:
+ if (hw->poll_mode) {
+ qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
+ hw->poll_mode = 0;
+ }
+
+ if (!oss->mmapped) {
+ return 0;
+ }
+
ldebug ("disabling voice\n");
trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@@ -671,8 +748,8 @@ static int oss_run_in (HWVoiceIn *hw)
int add;
int len;
} bufs[2] = {
- { hw->wpos, 0 },
- { 0, 0 }
+ { .add = hw->wpos, .len = 0 },
+ { .add = 0, .len = 0 }
};
if (!dead) {
@@ -687,7 +764,6 @@ static int oss_run_in (HWVoiceIn *hw)
bufs[0].len = dead << hwshift;
}
-
for (i = 0; i < 2; ++i) {
ssize_t nread;
@@ -737,8 +813,32 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
- (void) hw;
- (void) cmd;
+ OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ if (poll_mode && oss_poll_in (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+ }
+ break;
+
+ case VOICE_DISABLE:
+ if (hw->poll_mode) {
+ hw->poll_mode = 0;
+ qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
+ }
+ break;
+ }
return 0;
}
@@ -753,45 +853,83 @@ static void oss_audio_fini (void *opaque)
}
static struct audio_option oss_options[] = {
- {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
- "Fragment size in bytes", NULL, 0},
- {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
- "Number of fragments", NULL, 0},
- {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
- "Try using memory mapped access", NULL, 0},
- {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
- "Path to DAC device", NULL, 0},
- {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
- "Path to ADC device", NULL, 0},
- {"DEBUG", AUD_OPT_BOOL, &conf.debug,
- "Turn on some debugging messages", NULL, 0},
- {NULL, 0, NULL, NULL, NULL, 0}
+ {
+ .name = "FRAGSIZE",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.fragsize,
+ .descr = "Fragment size in bytes"
+ },
+ {
+ .name = "NFRAGS",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.nfrags,
+ .descr = "Number of fragments"
+ },
+ {
+ .name = "MMAP",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.try_mmap,
+ .descr = "Try using memory mapped access"
+ },
+ {
+ .name = "DAC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.devpath_out,
+ .descr = "Path to DAC device"
+ },
+ {
+ .name = "ADC_DEV",
+ .tag = AUD_OPT_STR,
+ .valp = &conf.devpath_in,
+ .descr = "Path to ADC device"
+ },
+ {
+ .name = "EXCLUSIVE",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.exclusive,
+ .descr = "Open device in exclusive mode (vmix wont work)"
+ },
+#ifdef USE_DSP_POLICY
+ {
+ .name = "POLICY",
+ .tag = AUD_OPT_INT,
+ .valp = &conf.policy,
+ .descr = "Set the timing policy of the device, -1 to use fragment mode",
+ },
+#endif
+ {
+ .name = "DEBUG",
+ .tag = AUD_OPT_BOOL,
+ .valp = &conf.debug,
+ .descr = "Turn on some debugging messages"
+ },
+ { /* End of list */ }
};
static struct audio_pcm_ops oss_pcm_ops = {
- oss_init_out,
- oss_fini_out,
- oss_run_out,
- oss_write,
- oss_ctl_out,
-
- oss_init_in,
- oss_fini_in,
- oss_run_in,
- oss_read,
- oss_ctl_in
+ .init_out = oss_init_out,
+ .fini_out = oss_fini_out,
+ .run_out = oss_run_out,
+ .write = oss_write,
+ .ctl_out = oss_ctl_out,
+
+ .init_in = oss_init_in,
+ .fini_in = oss_fini_in,
+ .run_in = oss_run_in,
+ .read = oss_read,
+ .ctl_in = oss_ctl_in
};
struct audio_driver oss_audio_driver = {
- INIT_FIELD (name = ) "oss",
- INIT_FIELD (descr = ) "OSS audio (www.opensound.com)",
- INIT_FIELD (options = ) oss_options,
- INIT_FIELD (init = ) oss_audio_init,
- INIT_FIELD (fini = ) oss_audio_fini,
- INIT_FIELD (pcm_ops = ) &oss_pcm_ops,
- INIT_FIELD (can_be_default = ) 1,
- INIT_FIELD (max_voices_out = ) INT_MAX,
- INIT_FIELD (max_voices_in = ) INT_MAX,
- INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
- INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn)
+ .name = "oss",
+ .descr = "OSS http://www.opensound.com",
+ .options = oss_options,
+ .init = oss_audio_init,
+ .fini = oss_audio_fini,
+ .pcm_ops = &oss_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = INT_MAX,
+ .max_voices_in = INT_MAX,
+ .voice_size_out = sizeof (OSSVoiceOut),
+ .voice_size_in = sizeof (OSSVoiceIn)
};
diff --git a/audio/paaudio.c b/audio/paaudio.c
index ed2c58c..c78353b 100644
--- a/audio/paaudio.c
+++ b/audio/paaudio.c
@@ -151,17 +151,11 @@ static void *qpa_thread_out (void *arg)
return NULL;
}
-static int qpa_run_out (HWVoiceOut *hw)
+static int qpa_run_out (HWVoiceOut *hw, int live)
{
int decr;
- int live;
PAVoiceOut *pa = (PAVoiceOut *) hw;
- live = audio_pcm_hw_get_live_out (hw);
- if (!live) {
- return 0;
- }
-
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return 0;
}
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index a82997e..62ba42d 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -55,10 +55,10 @@ static struct {
"qemu.wav"
};
-static int wav_out_run (HWVoiceOut *hw)
+static int wav_out_run (HWVoiceOut *hw, int live)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
- int rpos, live, decr, samples;
+ int rpos, decr, samples;
uint8_t *dst;
struct st_sample *src;
int64_t now = qemu_get_clock (vm_clock);
@@ -73,11 +73,6 @@ static int wav_out_run (HWVoiceOut *hw)
samples = bytes >> hw->info.shift;
}
- live = audio_pcm_hw_get_live_out (hw);
- if (!live) {
- return 0;
- }
-
wav->old_ticks = now;
decr = audio_MIN (live, samples);
samples = decr;
diff --git a/audio/winaudio.c b/audio/winaudio.c
index ca6c487..75f6af2 100644
--- a/audio/winaudio.c
+++ b/audio/winaudio.c
@@ -251,12 +251,11 @@ winaudio_out_init (HWVoiceOut *hw, struct audsettings *as)
static int
-winaudio_out_run (HWVoiceOut *hw)
+winaudio_out_run (HWVoiceOut *hw, int live)
{
WinAudioOut* s = (WinAudioOut*) hw;
int played = 0;
int has_buffer;
- int live = audio_pcm_hw_get_live_out (hw);
if (!live) {
return 0;