audio: documentation fixes.

audio: new backend api (first part of the surround sound patch series).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJdibVzAAoJEEy22O7T6HE4FzIQAKhh+spepRDCWHr5Fp0Ur3pc
 jo4ewzU7NJcth6lwWElpCt0P3rdscFBZefz0VeZ7alzYFD3lNryO9L6HBu5TvkgK
 07KJfi6BJNo+rJfqF9+n+IP6eECKDeKgubZzcTQ8q4cu7BDSu8LiM1lds70HgcSN
 1R/ddnwI0Xoo053M7CFVUEoux15wo5/SxP9atZz3ZkTBru2ZoHLHM9ZrQ2g0WOVs
 ewBCOGANhEudbq3QgYHVhuYDXy5SY4Mew9E3hbXOv0WdOV+AJ96A94oAyPC5aGdZ
 k/U8PhyNRnFzFsyyWHt/Cuxg1ArrYb0JEOJKTrcOsZVjX3QCTb8fyV9MVU80wym2
 PU8if5UGxz1wS+JPLOaaoLXNo3Drjbmk4c1cZRUU2d/Exv9DTiV34DB1fqkq4WOQ
 XEzVmnSYjjq4Rzfed9shPQpeKWW0CmNn6rKheIYjsqDJvF86VkCOVZ26G2GJf+KI
 g15UhncidNtmFncP3LzE84+SElE89bSu0pvt0vkXv1SexVVopJI/LuT02uZL/Ok9
 Y4+uZURUPhOm8XA93gczSPY3WJgQ6ljRGUZlWFQNMsurBfJQGlDSSVKCxCmD8Mve
 RM+bljni+sVKY4V6HODFS5rkEcgb5eHoFZEH6UdIyecGBFlhfMsdE53Nt/+Pyupc
 Zft180C00DMD2NxwNS1k
 =w6YQ
 -----END PGP SIGNATURE-----

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

audio: documentation fixes.
audio: new backend api (first part of the surround sound patch series).

# gpg: Signature made Tue 24 Sep 2019 07:19:31 BST
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/audio-20190924-pull-request:
  audio: split ctl_* functions into enable_* and volume_*
  audio: common rate control code for timer based outputs
  audio: unify input and output mixeng buffer management
  audio: remove remains of the old backend api
  wavaudio: port to the new audio backend api
  spiceaudio: port to the new audio backend api
  sdlaudio: port to the new audio backend api
  paaudio: port to the new audio backend api
  ossaudio: port to the new audio backend api
  noaudio: port to the new audio backend api
  dsoundaudio: port to the new audio backend api
  coreaudio: port to the new audio backend api
  alsaaudio: port to the new audio backend api
  audio: api for mixeng code free backends
  audio: fix ALSA period-length typo in documentation
  audio: fix buffer-length typo in documentation

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-09-24 13:51:51 +01:00
commit 860d9048c7
18 changed files with 1077 additions and 1915 deletions

View File

@ -2,7 +2,6 @@ common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o

View File

@ -44,9 +44,6 @@ struct pollhlp {
typedef struct ALSAVoiceOut {
HWVoiceOut hw;
int wpos;
int pending;
void *pcm_buf;
snd_pcm_t *handle;
struct pollhlp pollhlp;
Audiodev *dev;
@ -55,7 +52,6 @@ typedef struct ALSAVoiceOut {
typedef struct ALSAVoiceIn {
HWVoiceIn hw;
snd_pcm_t *handle;
void *pcm_buf;
struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceIn;
@ -602,102 +598,64 @@ static int alsa_open(bool in, struct alsa_params_req *req,
return -1;
}
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
{
snd_pcm_sframes_t avail;
avail = snd_pcm_avail_update (handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover (handle)) {
avail = snd_pcm_avail_update (handle);
}
}
if (avail < 0) {
alsa_logerr (avail,
"Could not obtain number of available frames\n");
return -1;
}
}
return avail;
}
static void alsa_write_pending (ALSAVoiceOut *alsa)
{
HWVoiceOut *hw = &alsa->hw;
while (alsa->pending) {
int left_till_end_samples = hw->samples - alsa->wpos;
int len = MIN (alsa->pending, left_till_end_samples);
char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
while (len) {
snd_pcm_sframes_t written;
written = snd_pcm_writei (alsa->handle, src, len);
if (written <= 0) {
switch (written) {
case 0:
trace_alsa_wrote_zero(len);
return;
case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (written, "Failed to write %d frames\n",
len);
return;
}
trace_alsa_xrun_out();
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;
}
trace_alsa_resume_out();
continue;
case -EAGAIN:
return;
default:
alsa_logerr (written, "Failed to write %d frames from %p\n",
len, src);
return;
}
}
alsa->wpos = (alsa->wpos + written) % hw->samples;
alsa->pending -= written;
len -= written;
}
}
}
static size_t alsa_run_out(HWVoiceOut *hw, size_t live)
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
size_t decr;
snd_pcm_sframes_t avail;
size_t pos = 0;
size_t len_frames = len >> hw->info.shift;
avail = alsa_get_avail (alsa->handle);
if (avail < 0) {
dolog ("Could not get number of available playback frames\n");
return 0;
while (len_frames) {
char *src = advance(buf, pos);
snd_pcm_sframes_t written;
written = snd_pcm_writei(alsa->handle, src, len_frames);
if (written <= 0) {
switch (written) {
case 0:
trace_alsa_wrote_zero(len_frames);
return pos;
case -EPIPE:
if (alsa_recover(alsa->handle)) {
alsa_logerr(written, "Failed to write %zu frames\n",
len_frames);
return pos;
}
trace_alsa_xrun_out();
continue;
case -ESTRPIPE:
/*
* stream is suspended and waiting for an application
* recovery
*/
if (alsa_resume(alsa->handle)) {
alsa_logerr(written, "Failed to write %zu frames\n",
len_frames);
return pos;
}
trace_alsa_resume_out();
continue;
case -EAGAIN:
return pos;
default:
alsa_logerr(written, "Failed to write %zu frames from %p\n",
len, src);
return pos;
}
}
pos += written << hw->info.shift;
if (written < len_frames) {
break;
}
len_frames -= written;
}
decr = 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;
return pos;
}
static void alsa_fini_out (HWVoiceOut *hw)
@ -706,9 +664,6 @@ static void alsa_fini_out (HWVoiceOut *hw)
ldebug ("alsa_fini\n");
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
g_free(alsa->pcm_buf);
alsa->pcm_buf = NULL;
}
static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
@ -737,14 +692,6 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) {
dolog("Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
alsa_anal_close1 (&handle);
return -1;
}
alsa->pollhlp.s = hw->s;
alsa->handle = handle;
alsa->dev = dev;
@ -784,34 +731,28 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
return 0;
}
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void alsa_enable_out(HWVoiceOut *hw, bool enable)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = apdo->try_poll;
if (enable) {
bool poll_mode = apdo->try_poll;
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", VOICE_CTL_PREPARE);
ldebug("enabling voice\n");
if (poll_mode && alsa_poll_out(hw)) {
poll_mode = 0;
}
case VOICE_DISABLE:
ldebug ("disabling voice\n");
hw->poll_mode = poll_mode;
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
} else {
ldebug("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
alsa_fini_poll (&alsa->pollhlp);
alsa_fini_poll(&alsa->pollhlp);
}
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
}
return -1;
}
static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@ -839,14 +780,6 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) {
dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
alsa_anal_close1 (&handle);
return -1;
}
alsa->pollhlp.s = hw->s;
alsa->handle = handle;
alsa->dev = dev;
@ -858,160 +791,73 @@ static void alsa_fini_in (HWVoiceIn *hw)
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
g_free(alsa->pcm_buf);
alsa->pcm_buf = NULL;
}
static size_t alsa_run_in(HWVoiceIn *hw)
static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
int hwshift = hw->info.shift;
int i;
size_t live = audio_pcm_hw_get_live_in (hw);
size_t dead = hw->samples - live;
size_t decr;
struct {
size_t add;
size_t len;
} bufs[2] = {
{ .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 }
};
snd_pcm_sframes_t avail;
snd_pcm_uframes_t read_samples = 0;
size_t pos = 0;
if (!dead) {
return 0;
}
avail = alsa_get_avail (alsa->handle);
if (avail < 0) {
dolog ("Could not get number of captured frames\n");
return 0;
}
if (!avail) {
snd_pcm_state_t state;
state = 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;
}
trace_alsa_resume_in();
break;
default:
trace_alsa_no_frames(state);
return 0;
}
}
decr = MIN(dead, avail);
if (!decr) {
return 0;
}
if (hw->wpos + decr > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos);
bufs[1].len = (decr - (hw->samples - hw->wpos));
}
else {
bufs[0].len = decr;
}
for (i = 0; i < 2; ++i) {
void *src;
struct st_sample *dst;
while (len) {
void *dst = advance(buf, pos);
snd_pcm_sframes_t nread;
snd_pcm_uframes_t len;
len = bufs[i].len;
nread = snd_pcm_readi(alsa->handle, dst, len >> hw->info.shift);
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
dst = hw->conv_buf + bufs[i].add;
if (nread <= 0) {
switch (nread) {
case 0:
trace_alsa_read_zero(len);
return pos;;
while (len) {
nread = snd_pcm_readi (alsa->handle, src, len);
if (nread <= 0) {
switch (nread) {
case 0:
trace_alsa_read_zero(len);
goto exit;
case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (nread, "Failed to read %ld frames\n", len);
goto exit;
}
trace_alsa_xrun_in();
continue;
case -EAGAIN:
goto exit;
default:
alsa_logerr (
nread,
"Failed to read %ld frames from %p\n",
len,
src
);
goto exit;
case -EPIPE:
if (alsa_recover(alsa->handle)) {
alsa_logerr(nread, "Failed to read %zu frames\n", len);
return pos;
}
trace_alsa_xrun_in();
continue;
case -EAGAIN:
return pos;
default:
alsa_logerr(nread, "Failed to read %zu frames to %p\n",
len, dst);
return pos;;
}
hw->conv (dst, src, nread);
src = advance (src, nread << hwshift);
dst += nread;
read_samples += nread;
len -= nread;
}
pos += nread << hw->info.shift;
len -= nread << hw->info.shift;
}
exit:
hw->wpos = (hw->wpos + read_samples) % hw->samples;
return read_samples;
return pos;
}
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
static void alsa_enable_in(HWVoiceIn *hw, bool enable)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = apdo->try_poll;
if (enable) {
bool poll_mode = apdo->try_poll;
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", VOICE_CTL_START);
ldebug("enabling voice\n");
if (poll_mode && alsa_poll_in(hw)) {
poll_mode = 0;
}
hw->poll_mode = poll_mode;
case VOICE_DISABLE:
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
} else {
ldebug ("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
alsa_fini_poll (&alsa->pollhlp);
alsa_fini_poll(&alsa->pollhlp);
}
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
}
return -1;
}
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
@ -1065,13 +911,13 @@ static void alsa_audio_fini (void *opaque)
static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
.run_out = alsa_run_out,
.ctl_out = alsa_ctl_out,
.write = alsa_write,
.enable_out = alsa_enable_out,
.init_in = alsa_init_in,
.fini_in = alsa_fini_in,
.run_in = alsa_run_in,
.ctl_in = alsa_ctl_in,
.read = alsa_read,
.enable_in = alsa_enable_in,
};
static struct audio_driver alsa_audio_driver = {

View File

@ -541,36 +541,33 @@ static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
return m;
}
size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
{
size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
if (audio_bug(__func__, live > hw->samples)) {
dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
if (audio_bug(__func__, live > hw->conv_buf->size)) {
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
return 0;
}
return live;
}
size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
size_t live, size_t pending)
static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
{
size_t left = hw->samples - pending;
size_t len = MIN (left, live);
size_t clipped = 0;
size_t pos = hw->mix_buf->pos;
while (len) {
struct st_sample *src = hw->mix_buf + hw->rpos;
uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift);
size_t samples_till_end_of_buf = hw->samples - hw->rpos;
size_t samples_to_clip = MIN (len, samples_till_end_of_buf);
st_sample *src = hw->mix_buf->samples + pos;
uint8_t *dst = advance(pcm_buf, clipped << hw->info.shift);
size_t samples_till_end_of_buf = hw->mix_buf->size - pos;
size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
hw->clip (dst, src, samples_to_clip);
hw->clip(dst, src, samples_to_clip);
hw->rpos = (hw->rpos + samples_to_clip) % hw->samples;
pos = (pos + samples_to_clip) % hw->mix_buf->size;
len -= samples_to_clip;
clipped += samples_to_clip;
}
return clipped;
}
/*
@ -582,17 +579,17 @@ static size_t audio_pcm_sw_get_rpos_in(SWVoiceIn *sw)
ssize_t live = hw->total_samples_captured - sw->total_hw_samples_acquired;
ssize_t rpos;
if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
if (audio_bug(__func__, live < 0 || live > hw->conv_buf->size)) {
dolog("live=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
return 0;
}
rpos = hw->wpos - live;
rpos = hw->conv_buf->pos - live;
if (rpos >= 0) {
return rpos;
}
else {
return hw->samples + rpos;
return hw->conv_buf->size + rpos;
}
}
@ -602,11 +599,11 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
size_t samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
struct st_sample *src, *dst = sw->buf;
rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
rpos = audio_pcm_sw_get_rpos_in(sw) % hw->conv_buf->size;
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
if (audio_bug(__func__, live > hw->samples)) {
dolog("live_in=%zu hw->samples=%zu\n", live, hw->samples);
if (audio_bug(__func__, live > hw->conv_buf->size)) {
dolog("live_in=%zu hw->conv_buf->size=%zu\n", live, hw->conv_buf->size);
return 0;
}
@ -619,11 +616,11 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
swlim = MIN (swlim, samples);
while (swlim) {
src = hw->conv_buf + rpos;
if (hw->wpos > rpos) {
isamp = hw->wpos - rpos;
src = hw->conv_buf->samples + rpos;
if (hw->conv_buf->pos > rpos) {
isamp = hw->conv_buf->pos - rpos;
} else {
isamp = hw->samples - rpos;
isamp = hw->conv_buf->size - rpos;
}
if (!isamp) {
@ -633,13 +630,13 @@ static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t size)
st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
swlim -= osamp;
rpos = (rpos + isamp) % hw->samples;
rpos = (rpos + isamp) % hw->conv_buf->size;
dst += osamp;
ret += osamp;
total += isamp;
}
if (!(hw->ctl_caps & VOICE_VOLUME_CAP)) {
if (!hw->pcm_ops->volume_in) {
mixeng_volume (sw->buf, ret, &sw->vol);
}
@ -681,8 +678,8 @@ static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
if (nb_live1) {
size_t live = smin;
if (audio_bug(__func__, live > hw->samples)) {
dolog("live=%zu hw->samples=%zu\n", live, hw->samples);
if (audio_bug(__func__, live > hw->mix_buf->size)) {
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
return 0;
}
return live;
@ -702,11 +699,11 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
return size;
}
hwsamples = sw->hw->samples;
hwsamples = sw->hw->mix_buf->size;
live = sw->total_hw_samples_mixed;
if (audio_bug(__func__, live > hwsamples)) {
dolog("live=%zu hw->samples=%zu\n", live, hwsamples);
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hwsamples);
return 0;
}
@ -717,7 +714,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
return 0;
}
wpos = (sw->hw->rpos + live) % hwsamples;
wpos = (sw->hw->mix_buf->pos + live) % hwsamples;
samples = size >> sw->info.shift;
dead = hwsamples - live;
@ -726,7 +723,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
if (swlim) {
sw->conv (sw->buf, buf, swlim);
if (!(sw->hw->ctl_caps & VOICE_VOLUME_CAP)) {
if (!sw->hw->pcm_ops->volume_out) {
mixeng_volume (sw->buf, swlim, &sw->vol);
}
}
@ -743,7 +740,7 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t size)
st_rate_flow_mix (
sw->rate,
sw->buf + pos,
sw->hw->mix_buf + wpos,
sw->hw->mix_buf->samples + wpos,
&isamp,
&osamp
);
@ -871,7 +868,7 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
int AUD_get_buffer_size_out (SWVoiceOut *sw)
{
return sw->hw->samples << sw->hw->info.shift;
return sw->hw->mix_buf->size << sw->hw->info.shift;
}
void AUD_set_active_out (SWVoiceOut *sw, int on)
@ -893,7 +890,9 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
if (!hw->enabled) {
hw->enabled = 1;
if (s->vm_running) {
hw->pcm_ops->ctl_out(hw, VOICE_ENABLE);
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, true);
}
audio_reset_timer (s);
}
}
@ -938,7 +937,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (!hw->enabled) {
hw->enabled = 1;
if (s->vm_running) {
hw->pcm_ops->ctl_in(hw, VOICE_ENABLE);
if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, true);
}
audio_reset_timer (s);
}
}
@ -955,7 +956,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (nb_active == 1) {
hw->enabled = 0;
hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
if (hw->pcm_ops->enable_in) {
hw->pcm_ops->enable_in(hw, false);
}
}
}
}
@ -972,8 +975,9 @@ static size_t audio_get_avail (SWVoiceIn *sw)
}
live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
if (audio_bug(__func__, live > sw->hw->samples)) {
dolog("live=%zu sw->hw->samples=%zu\n", live, sw->hw->samples);
if (audio_bug(__func__, live > sw->hw->conv_buf->size)) {
dolog("live=%zu sw->hw->conv_buf->size=%zu\n", live,
sw->hw->conv_buf->size);
return 0;
}
@ -996,12 +1000,13 @@ static size_t audio_get_free(SWVoiceOut *sw)
live = sw->total_hw_samples_mixed;
if (audio_bug(__func__, live > sw->hw->samples)) {
dolog("live=%zu sw->hw->samples=%zu\n", live, sw->hw->samples);
if (audio_bug(__func__, live > sw->hw->mix_buf->size)) {
dolog("live=%zu sw->hw->mix_buf->size=%zu\n", live,
sw->hw->mix_buf->size);
return 0;
}
dead = sw->hw->samples - live;
dead = sw->hw->mix_buf->size - live;
#ifdef DEBUG_OUT
dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
@ -1026,12 +1031,12 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
n = samples;
while (n) {
size_t till_end_of_hw = hw->samples - rpos2;
size_t till_end_of_hw = hw->mix_buf->size - rpos2;
size_t to_write = MIN(till_end_of_hw, n);
size_t bytes = to_write << hw->info.shift;
size_t written;
sw->buf = hw->mix_buf + rpos2;
sw->buf = hw->mix_buf->samples + rpos2;
written = audio_pcm_sw_write (sw, NULL, bytes);
if (written - bytes) {
dolog("Could not mix %zu bytes into a capture "
@ -1040,14 +1045,44 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
break;
}
n -= to_write;
rpos2 = (rpos2 + to_write) % hw->samples;
rpos2 = (rpos2 + to_write) % hw->mix_buf->size;
}
}
}
n = MIN(samples, hw->samples - rpos);
mixeng_clear(hw->mix_buf + rpos, n);
mixeng_clear(hw->mix_buf, samples - n);
n = MIN(samples, hw->mix_buf->size - rpos);
mixeng_clear(hw->mix_buf->samples + rpos, n);
mixeng_clear(hw->mix_buf->samples, samples - n);
}
static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
{
size_t clipped = 0;
while (live) {
size_t size, decr, proc;
void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
if (!buf) {
/* retrying will likely won't help, drop everything. */
hw->mix_buf->pos = (hw->mix_buf->pos + live) % hw->mix_buf->size;
return clipped + live;
}
decr = MIN(size >> hw->info.shift, live);
audio_pcm_hw_clip_out(hw, buf, decr);
proc = hw->pcm_ops->put_buffer_out(hw, buf, decr << hw->info.shift) >>
hw->info.shift;
live -= proc;
clipped += proc;
hw->mix_buf->pos = (hw->mix_buf->pos + proc) % hw->mix_buf->size;
if (proc == 0 || proc < decr) {
break;
}
}
return clipped;
}
static void audio_run_out (AudioState *s)
@ -1064,8 +1099,8 @@ static void audio_run_out (AudioState *s)
live = 0;
}
if (audio_bug(__func__, live > hw->samples)) {
dolog ("live=%zu hw->samples=%zu\n", live, hw->samples);
if (audio_bug(__func__, live > hw->mix_buf->size)) {
dolog("live=%zu hw->mix_buf->size=%zu\n", live, hw->mix_buf->size);
continue;
}
@ -1076,7 +1111,9 @@ static void audio_run_out (AudioState *s)
#endif
hw->enabled = 0;
hw->pending_disable = 0;
hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
if (hw->pcm_ops->enable_out) {
hw->pcm_ops->enable_out(hw, false);
}
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
sc->sw.active = 0;
audio_recalc_and_notify_capture (sc->cap);
@ -1096,13 +1133,13 @@ static void audio_run_out (AudioState *s)
continue;
}
prev_rpos = hw->rpos;
played = hw->pcm_ops->run_out (hw, live);
prev_rpos = hw->mix_buf->pos;
played = audio_pcm_hw_run_out(hw, live);
replay_audio_out(&played);
if (audio_bug(__func__, hw->rpos >= hw->samples)) {
dolog("hw->rpos=%zu hw->samples=%zu played=%zu\n",
hw->rpos, hw->samples, played);
hw->rpos = 0;
if (audio_bug(__func__, hw->mix_buf->pos >= hw->mix_buf->size)) {
dolog("hw->mix_buf->pos=%zu hw->mix_buf->size=%zu played=%zu\n",
hw->mix_buf->pos, hw->mix_buf->size, played);
hw->mix_buf->pos = 0;
}
#ifdef DEBUG_OUT
@ -1156,6 +1193,36 @@ static void audio_run_out (AudioState *s)
}
}
static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
{
size_t conv = 0;
STSampleBuffer *conv_buf = hw->conv_buf;
while (samples) {
size_t proc;
size_t size = samples << hw->info.shift;
void *buf = hw->pcm_ops->get_buffer_in(hw, &size);
assert((size & hw->info.align) == 0);
if (size == 0) {
hw->pcm_ops->put_buffer_in(hw, buf, size);
break;
}
proc = MIN(size >> hw->info.shift,
conv_buf->size - conv_buf->pos);
hw->conv(conv_buf->samples + conv_buf->pos, buf, proc);
conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
samples -= proc;
conv += proc;
hw->pcm_ops->put_buffer_in(hw, buf, proc << hw->info.shift);
}
return conv;
}
static void audio_run_in (AudioState *s)
{
HWVoiceIn *hw = NULL;
@ -1165,9 +1232,11 @@ static void audio_run_in (AudioState *s)
size_t captured = 0, min;
if (replay_mode != REPLAY_MODE_PLAY) {
captured = hw->pcm_ops->run_in(hw);
captured = audio_pcm_hw_run_in(
hw, hw->conv_buf->size - audio_pcm_hw_get_live_in(hw));
}
replay_audio_in(&captured, hw->conv_buf, &hw->wpos, hw->samples);
replay_audio_in(&captured, hw->conv_buf->samples, &hw->conv_buf->pos,
hw->conv_buf->size);
min = audio_pcm_hw_find_min_in (hw);
hw->total_samples_captured += captured - min;
@ -1198,14 +1267,14 @@ static void audio_run_capture (AudioState *s)
SWVoiceOut *sw;
captured = live = audio_pcm_hw_get_live_out (hw, NULL);
rpos = hw->rpos;
rpos = hw->mix_buf->pos;
while (live) {
size_t left = hw->samples - rpos;
size_t left = hw->mix_buf->size - rpos;
size_t to_capture = MIN(live, left);
struct st_sample *src;
struct capture_callback *cb;
src = hw->mix_buf + rpos;
src = hw->mix_buf->samples + rpos;
hw->clip (cap->buf, src, to_capture);
mixeng_clear (src, to_capture);
@ -1213,10 +1282,10 @@ static void audio_run_capture (AudioState *s)
cb->ops.capture (cb->opaque, cap->buf,
to_capture << hw->info.shift);
}
rpos = (rpos + to_capture) % hw->samples;
rpos = (rpos + to_capture) % hw->mix_buf->size;
live -= to_capture;
}
hw->rpos = rpos;
hw->mix_buf->pos = rpos;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (!sw->active && sw->empty) {
@ -1259,12 +1328,137 @@ void audio_run(AudioState *s, const char *msg)
#endif
}
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
{
ssize_t start;
if (unlikely(!hw->buf_emul)) {
size_t calc_size = hw->conv_buf->size << hw->info.shift;
hw->buf_emul = g_malloc(calc_size);
hw->size_emul = calc_size;
hw->pos_emul = hw->pending_emul = 0;
}
while (hw->pending_emul < hw->size_emul) {
size_t read_len = MIN(hw->size_emul - hw->pos_emul,
hw->size_emul - hw->pending_emul);
size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,
read_len);
hw->pending_emul += read;
if (read < read_len) {
break;
}
}
start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
*size = MIN(hw->pending_emul, hw->size_emul - start);
return hw->buf_emul + start;
}
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
{
assert(size <= hw->pending_emul);
hw->pending_emul -= size;
}
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
if (unlikely(!hw->buf_emul)) {
size_t calc_size = hw->mix_buf->size << hw->info.shift;
hw->buf_emul = g_malloc(calc_size);
hw->size_emul = calc_size;
hw->pos_emul = hw->pending_emul = 0;
}
*size = MIN(hw->size_emul - hw->pending_emul,
hw->size_emul - hw->pos_emul);
return hw->buf_emul + hw->pos_emul;
}
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
size_t size)
{
assert(buf == hw->buf_emul + hw->pos_emul &&
size + hw->pending_emul <= hw->size_emul);
hw->pending_emul += size;
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
return size;
}
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
{
audio_generic_put_buffer_out_nowrite(hw, buf, size);
while (hw->pending_emul) {
size_t write_len, written;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
write_len = MIN(hw->pending_emul, hw->size_emul - start);
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
hw->pending_emul -= written;
if (written < write_len) {
break;
}
}
/*
* fake we have written everything. non-written data remain in pending_emul,
* so we do not have to clip them multiple times
*/
return size;
}
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
{
size_t dst_size, copy_size;
void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);
copy_size = MIN(size, dst_size);
memcpy(dst, buf, copy_size);
return hw->pcm_ops->put_buffer_out(hw, buf, copy_size);
}
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
{
size_t dst_size, copy_size;
void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size);
copy_size = MIN(size, dst_size);
memcpy(dst, buf, copy_size);
hw->pcm_ops->put_buffer_in(hw, buf, copy_size);
return copy_size;
}
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
bool msg, Audiodev *dev)
{
s->drv_opaque = drv->init(dev);
if (s->drv_opaque) {
if (!drv->pcm_ops->get_buffer_in) {
drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
}
if (!drv->pcm_ops->get_buffer_out) {
drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
}
audio_init_nb_voices_out(s, drv);
audio_init_nb_voices_in(s, drv);
s->drv = drv;
@ -1284,15 +1478,18 @@ static void audio_vm_change_state_handler (void *opaque, int running,
AudioState *s = opaque;
HWVoiceOut *hwo = NULL;
HWVoiceIn *hwi = NULL;
int op = running ? VOICE_ENABLE : VOICE_DISABLE;
s->vm_running = running;
while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
hwo->pcm_ops->ctl_out(hwo, op);
if (hwo->pcm_ops->enable_out) {
hwo->pcm_ops->enable_out(hwo, running);
}
}
while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
hwi->pcm_ops->ctl_in(hwi, op);
if (hwi->pcm_ops->enable_in) {
hwi->pcm_ops->enable_in(hwi, running);
}
}
audio_reset_timer (s);
}
@ -1312,8 +1509,8 @@ static void free_audio_state(AudioState *s)
QLIST_FOREACH_SAFE(hwo, &s->hw_head_out, entries, hwon) {
SWVoiceCap *sc;
if (hwo->enabled) {
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
if (hwo->enabled && hwo->pcm_ops->enable_out) {
hwo->pcm_ops->enable_out(hwo, false);
}
hwo->pcm_ops->fini_out (hwo);
@ -1329,8 +1526,8 @@ static void free_audio_state(AudioState *s)
}
QLIST_FOREACH_SAFE(hwi, &s->hw_head_in, entries, hwin) {
if (hwi->enabled) {
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
if (hwi->enabled && hwi->pcm_ops->enable_in) {
hwi->pcm_ops->enable_in(hwi, false);
}
hwi->pcm_ops->fini_in (hwi);
QLIST_REMOVE(hwi, entries);
@ -1582,11 +1779,11 @@ CaptureVoiceOut *AUD_add_capture(
/* XXX find a more elegant way */
hw->samples = 4096 * 4;
hw->mix_buf = g_new0(struct st_sample, hw->samples);
audio_pcm_hw_alloc_resources_out(hw);
audio_pcm_init_info (&hw->info, as);
cap->buf = g_malloc0_n(hw->samples, 1 << hw->info.shift);
cap->buf = g_malloc0_n(hw->mix_buf->size, 1 << hw->info.shift);
hw->clip = mixeng_clip
[hw->info.nchannels == 2]
@ -1652,8 +1849,8 @@ void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
sw->vol.l = nominal_volume.l * lvol / 255;
sw->vol.r = nominal_volume.r * rvol / 255;
if (hw->pcm_ops->ctl_out) {
hw->pcm_ops->ctl_out (hw, VOICE_VOLUME, sw);
if (hw->pcm_ops->volume_out) {
hw->pcm_ops->volume_out(hw, &sw->vol);
}
}
}
@ -1667,8 +1864,8 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
sw->vol.l = nominal_volume.l * lvol / 255;
sw->vol.r = nominal_volume.r * rvol / 255;
if (hw->pcm_ops->ctl_in) {
hw->pcm_ops->ctl_in (hw, VOICE_VOLUME, sw);
if (hw->pcm_ops->volume_in) {
hw->pcm_ops->volume_in(hw, &sw->vol);
}
}
}
@ -1865,3 +2062,33 @@ const char *audio_get_id(QEMUSoundCard *card)
return "";
}
}
void audio_rate_start(RateCtl *rate)
{
memset(rate, 0, sizeof(RateCtl));
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t bytes_avail)
{
int64_t now;
int64_t ticks;
int64_t bytes;
int64_t samples;
size_t ret;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
samples = (bytes - rate->bytes_sent) >> info->shift;
if (samples < 0 || samples > 65536) {
AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples);
audio_rate_start(rate);
samples = 0;
}
ret = MIN(samples << info->shift, bytes_avail);
rate->bytes_sent += ret;
return ret;
}

View File

@ -52,6 +52,11 @@ struct audio_pcm_info {
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer {
size_t pos, size;
st_sample samples[];
} STSampleBuffer;
typedef struct HWVoiceOut {
AudioState *s;
int enabled;
@ -60,16 +65,15 @@ typedef struct HWVoiceOut {
struct audio_pcm_info info;
f_sample *clip;
size_t rpos;
uint64_t ts_helper;
struct st_sample *mix_buf;
STSampleBuffer *mix_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
size_t samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
@ -82,15 +86,15 @@ typedef struct HWVoiceIn {
t_sample *conv;
size_t wpos;
size_t total_samples_captured;
uint64_t ts_helper;
struct st_sample *conv_buf;
STSampleBuffer *conv_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
size_t samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn;
@ -142,22 +146,46 @@ struct audio_driver {
int max_voices_in;
int voice_size_out;
int voice_size_in;
int ctl_caps;
QLIST_ENTRY(audio_driver) next;
};
struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
size_t (*run_out)(HWVoiceOut *hw, size_t live);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
/*
* get a buffer that after later can be passed to put_buffer_out; optional
* returns the buffer, and writes it's size to size (in bytes)
* this is unrelated to the above buffer_size_out function
*/
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
/*
* put back the buffer returned by get_buffer_out; optional
* buf must be equal the pointer returned by get_buffer_out,
* size may be smaller
*/
size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
void (*enable_out)(HWVoiceOut *hw, bool enable);
void (*volume_out)(HWVoiceOut *hw, struct mixeng_volume *vol);
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
size_t (*run_in)(HWVoiceIn *hw);
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
void (*enable_in)(HWVoiceIn *hw, bool enable);
void (*volume_in)(HWVoiceIn *hw, struct mixeng_volume *vol);
};
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
size_t size);
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
struct capture_callback {
struct audio_capture_ops ops;
void *opaque;
@ -208,21 +236,19 @@ audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw);
size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
size_t live, size_t pending);
int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size);
void audio_run(AudioState *s, const char *msg);
#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
#define VOICE_VOLUME 3
typedef struct RateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} RateCtl;
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t bytes_avail);
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
{

View File

@ -1,173 +0,0 @@
#include "qemu/osdep.h"
#include "audio.h"
#define AUDIO_CAP "audio-pt"
#include "audio_int.h"
#include "audio_pt_int.h"
static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
AUD_vlog (pt->drv, fmt, ap);
va_end (ap);
AUD_log (NULL, "\n");
AUD_log (pt->drv, "Reason: %s\n", strerror (err));
}
int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
void *opaque, const char *drv, const char *cap)
{
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, __func__);
return -1;
}
err = pthread_mutex_init (&p->mutex, NULL);
if (err) {
efunc = "pthread_mutex_init";
goto err0;
}
err = pthread_cond_init (&p->cond, NULL);
if (err) {
efunc = "pthread_cond_init";
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, __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;
}
return 0;
err2:
err2 = pthread_cond_destroy (&p->cond);
if (err2) {
logerr(p, err2, "%s(%s): pthread_cond_destroy failed", cap, __func__);
}
err1:
err2 = pthread_mutex_destroy (&p->mutex);
if (err2) {
logerr(p, err2, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
}
err0:
logerr(p, err, "%s(%s): %s failed", cap, __func__, efunc);
return -1;
}
int audio_pt_fini (struct audio_pt *p, const char *cap)
{
int err, ret = 0;
err = pthread_cond_destroy (&p->cond);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_destroy failed", cap, __func__);
ret = -1;
}
err = pthread_mutex_destroy (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
ret = -1;
}
return ret;
}
int audio_pt_lock (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_mutex_lock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_lock failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_unlock (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_mutex_unlock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_wait (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_cond_wait (&p->cond, &p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_wait failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_mutex_unlock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
return -1;
}
err = pthread_cond_signal (&p->cond);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_signal failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
{
int err;
void *ret;
err = pthread_join (p->thread, &ret);
if (err) {
logerr(p, err, "%s(%s): pthread_join failed", cap, __func__);
return -1;
}
*arg = ret;
return 0;
}

View File

@ -1,22 +0,0 @@
#ifndef QEMU_AUDIO_PT_INT_H
#define QEMU_AUDIO_PT_INT_H
#include <pthread.h>
struct audio_pt {
const char *drv;
pthread_t thread;
pthread_cond_t cond;
pthread_mutex_t mutex;
};
int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
const char *, const char *);
int audio_pt_fini (struct audio_pt *, const char *);
int audio_pt_lock (struct audio_pt *, const char *);
int audio_pt_unlock (struct audio_pt *, const char *);
int audio_pt_wait (struct audio_pt *, const char *);
int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
int audio_pt_join (struct audio_pt *, void **, const char *);
#endif /* QEMU_AUDIO_PT_INT_H */

View File

@ -71,20 +71,20 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{
g_free(hw->buf_emul);
g_free (HWBUF);
HWBUF = NULL;
}
static bool glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
{
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
if (!HWBUF) {
dolog("Could not allocate " NAME " buffer (%zu samples)\n",
hw->samples);
return false;
size_t samples = hw->samples;
if (audio_bug(__func__, samples == 0)) {
dolog("Attempted to allocate empty buffer\n");
}
return true;
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
HWBUF->size = samples;
}
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@ -103,7 +103,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) {
@ -254,7 +254,6 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
hw->s = s;
hw->pcm_ops = drv->pcm_ops;
hw->ctl_caps = drv->ctl_caps;
QLIST_INIT (&hw->sw_head);
#ifdef DAC
@ -279,9 +278,7 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
[hw->info.swap_endianness]
[audio_bits_to_index (hw->info.bits)];
if (!glue(audio_pcm_hw_alloc_resources_, TYPE)(hw)) {
goto err1;
}
glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1;

View File

@ -43,9 +43,6 @@ typedef struct coreaudioVoiceOut {
UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription;
AudioDeviceIOProcID ioprocid;
size_t live;
size_t decr;
size_t rpos;
} coreaudioVoiceOut;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@ -397,31 +394,29 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
return 0;
}
static size_t coreaudio_run_out(HWVoiceOut *hw, size_t live)
{
size_t decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (coreaudio_lock (core, "coreaudio_run_out")) {
return 0;
#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
static ret_type glue(coreaudio_, name)args_decl \
{ \
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
ret_type ret; \
\
if (coreaudio_lock(core, "coreaudio_" #name)) { \
return 0; \
} \
\
ret = glue(audio_generic_, name)args; \
\
coreaudio_unlock(core, "coreaudio_" #name); \
return ret; \
}
if (core->decr > live) {
ldebug ("core->decr %d live %d core->live %d\n",
core->decr,
live,
core->live);
}
decr = MIN (core->decr, live);
core->decr -= decr;
core->live = live - decr;
hw->rpos = core->rpos;
coreaudio_unlock (core, "coreaudio_run_out");
return decr;
}
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size))
COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
(HWVoiceOut *hw, void *buf, size_t size),
(hw, buf, size))
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
(hw, buf, size))
#undef COREAUDIO_WRAPPER_FUNC
/* callback to feed audiooutput buffer */
static OSStatus audioDeviceIOProc(
@ -433,19 +428,11 @@ static OSStatus audioDeviceIOProc(
const AudioTimeStamp* inOutputTime,
void* hwptr)
{
UInt32 frame, frameCount;
float *out = outOutputData->mBuffers[0].mData;
UInt32 frameCount, pending_frames;
void *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
int rpos, live;
struct st_sample *src;
#ifndef FLOAT_MIXENG
#ifdef RECIPROCAL
const float scale = 1.f / UINT_MAX;
#else
const float scale = UINT_MAX;
#endif
#endif
size_t len;
if (coreaudio_lock (core, "audioDeviceIOProc")) {
inInputTime = 0;
@ -453,42 +440,51 @@ static OSStatus audioDeviceIOProc(
}
frameCount = core->audioDevicePropertyBufferFrameSize;
live = core->live;
pending_frames = hw->pending_emul >> hw->info.shift;
/* if there are not enough samples, set signal and return */
if (live < frameCount) {
if (pending_frames < frameCount) {
inInputTime = 0;
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
return 0;
}
rpos = core->rpos;
src = hw->mix_buf + rpos;
len = frameCount << hw->info.shift;
while (len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
/* fill buffer */
for (frame = 0; frame < frameCount; frame++) {
#ifdef FLOAT_MIXENG
*out++ = src[frame].l; /* left channel */
*out++ = src[frame].r; /* right channel */
#else
#ifdef RECIPROCAL
*out++ = src[frame].l * scale; /* left channel */
*out++ = src[frame].r * scale; /* right channel */
#else
*out++ = src[frame].l / scale; /* left channel */
*out++ = src[frame].r / scale; /* right channel */
#endif
#endif
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
memcpy(out, hw->buf_emul + start, write_len);
hw->pending_emul -= write_len;
len -= write_len;
out += write_len;
}
rpos = (rpos + frameCount) % hw->samples;
core->decr += frameCount;
core->rpos = rpos;
coreaudio_unlock (core, "audioDeviceIOProc");
return 0;
}
static UInt32 coreaudio_get_flags(struct audio_pcm_info *info,
struct audsettings *as)
{
UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0;
if (as->endianness) { /* 0 = little, 1 = big */
flags |= kAudioFormatFlagIsBigEndian;
}
if (flags == 0) { /* must not be 0 */
flags = kAudioFormatFlagsAreAllClear;
}
return flags;
}
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque)
{
@ -576,6 +572,16 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
/* set Samplerate */
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
core->outputStreamBasicDescription.mFormatFlags =
coreaudio_get_flags(&hw->info, as);
core->outputStreamBasicDescription.mBytesPerPacket =
core->outputStreamBasicDescription.mBytesPerFrame =
hw->info.nchannels * hw->info.bits / 8;
core->outputStreamBasicDescription.mFramesPerPacket = 1;
core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels;
core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits;
status = coreaudio_set_streamformat(core->outputDeviceID,
&core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
@ -642,13 +648,12 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
}
}
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
{
OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
switch (cmd) {
case VOICE_ENABLE:
if (enable) {
/* start playback */
if (!isPlaying(core->outputDeviceID)) {
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
@ -656,9 +661,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
coreaudio_logerr (status, "Could not resume playback\n");
}
}
break;
case VOICE_DISABLE:
} else {
/* stop playback */
if (!audio_is_cleaning_up()) {
if (isPlaying(core->outputDeviceID)) {
@ -669,9 +672,7 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
}
}
}
break;
}
return 0;
}
static void *coreaudio_audio_init(Audiodev *dev)
@ -686,8 +687,10 @@ static void coreaudio_audio_fini (void *opaque)
static struct audio_pcm_ops coreaudio_pcm_ops = {
.init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out,
.run_out = coreaudio_run_out,
.ctl_out = coreaudio_ctl_out
.write = coreaudio_write,
.get_buffer_out = coreaudio_get_buffer_out,
.put_buffer_out = coreaudio_put_buffer_out_nowrite,
.enable_out = coreaudio_enable_out
};
static struct audio_driver coreaudio_audio_driver = {

View File

@ -29,6 +29,8 @@
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
#define FIELD dsound_capture_buffer
#define FIELD2 dsound_capture
#define HWVOICE HWVoiceIn
#define DSOUNDVOICE DSoundVoiceIn
#else
#define NAME "playback buffer"
#define NAME2 "DirectSound"
@ -37,6 +39,8 @@
#define BUFPTR LPDIRECTSOUNDBUFFER
#define FIELD dsound_buffer
#define FIELD2 dsound
#define HWVOICE HWVoiceOut
#define DSOUNDVOICE DSoundVoiceOut
#endif
static int glue (dsound_unlock_, TYPE) (
@ -72,8 +76,6 @@ static int glue (dsound_lock_, TYPE) (
)
{
HRESULT hr;
LPVOID p1 = NULL, p2 = NULL;
DWORD blen1 = 0, blen2 = 0;
DWORD flag;
#ifdef DSBTYPE_IN
@ -81,7 +83,7 @@ static int glue (dsound_lock_, TYPE) (
#else
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
#endif
hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
hr = glue(IFACE, _Lock)(buf, pos, len, p1p, blen1p, p2p, blen2p, flag);
if (FAILED (hr)) {
#ifndef DSBTYPE_IN
@ -96,34 +98,34 @@ static int glue (dsound_lock_, TYPE) (
goto fail;
}
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
blen1, blen2);
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
if ((p1p && *p1p && (*blen1p & info->align)) ||
(p2p && *p2p && (*blen2p & info->align))) {
dolog("DirectSound returned misaligned buffer %ld %ld\n",
*blen1p, *blen2p);
glue(dsound_unlock_, TYPE)(buf, *p1p, p2p ? *p2p : NULL, *blen1p,
blen2p ? *blen2p : 0);
goto fail;
}
if (!p1 && blen1) {
dolog ("warning: !p1 && blen1=%ld\n", blen1);
blen1 = 0;
if (p1p && !*p1p && *blen1p) {
dolog("warning: !p1 && blen1=%ld\n", *blen1p);
*blen1p = 0;
}
if (!p2 && blen2) {
dolog ("warning: !p2 && blen2=%ld\n", blen2);
blen2 = 0;
if (p2p && !*p2p && *blen2p) {
dolog("warning: !p2 && blen2=%ld\n", *blen2p);
*blen2p = 0;
}
*p1p = p1;
*p2p = p2;
*blen1p = blen1;
*blen2p = blen2;
return 0;
fail:
*p1p = NULL - 1;
*p2p = NULL - 1;
*blen1p = -1;
*blen2p = -1;
if (p2p) {
*p2p = NULL - 1;
*blen2p = -1;
}
return -1;
}
@ -242,7 +244,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
goto fail0;
}
ds->first_time = 1;
obt_as.endianness = 0;
audio_pcm_init_info (&hw->info, &obt_as);
@ -252,15 +253,13 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
bc.dwBufferBytes, hw->info.align + 1
);
}
hw->size_emul = bc.dwBufferBytes;
hw->samples = bc.dwBufferBytes >> hw->info.shift;
ds->s = s;
#ifdef DEBUG_DSOUND
dolog ("caps %ld, desc %ld\n",
bc.dwBufferBytes, bd.dwBufferBytes);
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
#endif
return 0;
@ -276,3 +275,5 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
#undef BUFPTR
#undef FIELD
#undef FIELD2
#undef HWVOICE
#undef DSOUNDVOICE

View File

@ -53,19 +53,11 @@ typedef struct {
typedef struct {
HWVoiceOut hw;
LPDIRECTSOUNDBUFFER dsound_buffer;
DWORD old_pos;
int first_time;
dsound *s;
#ifdef DEBUG_DSOUND
DWORD old_ppos;
DWORD played;
DWORD mixed;
#endif
} DSoundVoiceOut;
typedef struct {
HWVoiceIn hw;
int first_time;
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
dsound *s;
} DSoundVoiceIn;
@ -243,11 +235,6 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
dsound_log_hresult (hr);
}
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
{
return muldiv64(usecs, info->bytes_per_second, 1000000);
}
#ifdef DEBUG_DSOUND
static void print_wave_format (WAVEFORMATEX *wfx)
{
@ -312,33 +299,6 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
return 0;
}
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;
struct st_sample *src1 = hw->mix_buf + hw->rpos;
struct st_sample *src2 = NULL;
if (pos > hw->samples) {
src_len1 = hw->samples - hw->rpos;
src2 = hw->mix_buf;
src_len2 = dst_len - src_len1;
pos = src_len2;
}
if (src_len1) {
hw->clip (dst, src1, src_len1);
}
if (src_len2) {
dst = advance (dst, src_len1 << hw->info.shift);
hw->clip (dst, src2, src_len2);
}
hw->rpos = pos % hw->samples;
}
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsound *s)
{
@ -350,7 +310,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsb,
&hw->info,
0,
hw->samples << hw->info.shift,
hw->size_emul,
&p1, &p2,
&blen1, &blen2,
1,
@ -401,7 +361,7 @@ static int dsound_open (dsound *s)
return 0;
}
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
{
HRESULT hr;
DWORD status;
@ -411,18 +371,17 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
if (!dsb) {
dolog ("Attempt to control voice without a buffer\n");
return 0;
return;
}
switch (cmd) {
case VOICE_ENABLE:
if (enable) {
if (dsound_get_status_out (dsb, &status, s)) {
return -1;
return;
}
if (status & DSBSTATUS_PLAYING) {
dolog ("warning: Voice is already playing\n");
return 0;
return;
}
dsound_clear_sample (hw, dsb, s);
@ -430,166 +389,74 @@ static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not start playing buffer\n");
return -1;
return;
}
break;
case VOICE_DISABLE:
} else {
if (dsound_get_status_out (dsb, &status, s)) {
return -1;
return;
}
if (status & DSBSTATUS_PLAYING) {
hr = IDirectSoundBuffer_Stop (dsb);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop playing buffer\n");
return -1;
return;
}
}
else {
dolog ("warning: Voice is not playing\n");
}
break;
}
return 0;
}
static size_t dsound_run_out(HWVoiceOut *hw, size_t live)
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
int err;
HRESULT hr;
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
size_t len;
int hwshift;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
DWORD wpos, ppos, old_pos;
LPVOID p1, p2;
size_t bufsize;
dsound *s = ds->s;
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
HRESULT hr;
DWORD ppos, act_size;
size_t req_size;
int err;
void *ret;
if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n");
return 0;
hr = IDirectSoundBuffer_GetCurrentPosition(dsb, &ppos, NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get playback buffer position\n");
*size = 0;
return NULL;
}
hwshift = hw->info.shift;
bufsize = hw->samples << hwshift;
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
hr = IDirectSoundBuffer_GetCurrentPosition (
dsb,
&ppos,
ds->first_time ? &wpos : NULL
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not get playback buffer position\n");
return 0;
}
len = live << hwshift;
if (ds->first_time) {
if (dso->latency) {
DWORD cur_blat;
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
ds->first_time = 0;
old_pos = wpos;
old_pos +=
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
old_pos %= bufsize;
old_pos &= ~hw->info.align;
}
else {
old_pos = wpos;
}
#ifdef DEBUG_DSOUND
ds->played = 0;
ds->mixed = 0;
#endif
}
else {
if (ds->old_pos == ppos) {
#ifdef DEBUG_DSOUND
dolog ("old_pos == ppos\n");
#endif
return 0;
}
#ifdef DEBUG_DSOUND
ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
#endif
old_pos = ds->old_pos;
}
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
len = ppos - old_pos;
}
else {
if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
len = bufsize - old_pos + ppos;
}
}
if (audio_bug(__func__, len > bufsize)) {
dolog("len=%zu bufsize=%zu old_pos=%ld ppos=%ld\n",
len, bufsize, old_pos, ppos);
return 0;
}
len &= ~hw->info.align;
if (!len) {
return 0;
}
#ifdef DEBUG_DSOUND
ds->old_ppos = ppos;
#endif
err = dsound_lock_out (
dsb,
&hw->info,
old_pos,
len,
&p1, &p2,
&blen1, &blen2,
0,
s
);
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
&act_size, NULL, false, ds->s);
if (err) {
return 0;
dolog("Failed to lock buffer\n");
*size = 0;
return NULL;
}
len1 = blen1 >> hwshift;
len2 = blen2 >> hwshift;
decr = len1 + len2;
if (p1 && len1) {
dsound_write_sample (hw, p1, len1);
}
if (p2 && len2) {
dsound_write_sample (hw, p2, len2);
}
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
#ifdef DEBUG_DSOUND
ds->mixed += decr << hwshift;
dolog ("played %lu mixed %lu diff %ld sec %f\n",
ds->played,
ds->mixed,
ds->mixed - ds->played,
abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
#endif
return decr;
*size = act_size;
return ret;
}
static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
{
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
if (err) {
dolog("Failed to unlock buffer!!\n");
return 0;
}
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
return len;
}
static void dsound_enable_in(HWVoiceIn *hw, bool enable)
{
HRESULT hr;
DWORD status;
@ -598,18 +465,17 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
if (!dscb) {
dolog ("Attempt to control capture voice without a buffer\n");
return -1;
return;
}
switch (cmd) {
case VOICE_ENABLE:
if (enable) {
if (dsound_get_status_in (dscb, &status)) {
return -1;
return;
}
if (status & DSCBSTATUS_CAPTURING) {
dolog ("warning: Voice is already capturing\n");
return 0;
return;
}
/* clear ?? */
@ -617,120 +483,69 @@ static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not start capturing\n");
return -1;
return;
}
break;
case VOICE_DISABLE:
} else {
if (dsound_get_status_in (dscb, &status)) {
return -1;
return;
}
if (status & DSCBSTATUS_CAPTURING) {
hr = IDirectSoundCaptureBuffer_Stop (dscb);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop capturing\n");
return -1;
return;
}
}
else {
dolog ("warning: Voice is not capturing\n");
}
break;
}
return 0;
}
static size_t dsound_run_in(HWVoiceIn *hw)
static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
{
int err;
HRESULT hr;
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
size_t live, len, dead;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
DWORD cpos, rpos;
LPVOID p1, p2;
int hwshift;
dsound *s = ds->s;
HRESULT hr;
DWORD cpos, act_size;
size_t req_size;
int err;
void *ret;
if (!dscb) {
dolog ("Attempt to run without capture buffer\n");
return 0;
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, &cpos, NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get capture buffer position\n");
*size = 0;
return NULL;
}
hwshift = hw->info.shift;
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
live = audio_pcm_hw_get_live_in (hw);
dead = hw->samples - live;
if (!dead) {
return 0;
}
hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
dscb,
&cpos,
ds->first_time ? &rpos : NULL
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not get capture buffer position\n");
return 0;
}
if (ds->first_time) {
ds->first_time = 0;
if (rpos & hw->info.align) {
ldebug ("warning: Misaligned capture read position %ld(%d)\n",
rpos, hw->info.align);
}
hw->wpos = rpos >> hwshift;
}
if (cpos & hw->info.align) {
ldebug ("warning: Misaligned capture position %ld(%d)\n",
cpos, hw->info.align);
}
cpos >>= hwshift;
len = audio_ring_dist (cpos, hw->wpos, hw->samples);
if (!len) {
return 0;
}
len = MIN (len, dead);
err = dsound_lock_in (
dscb,
&hw->info,
hw->wpos << hwshift,
len << hwshift,
&p1,
&p2,
&blen1,
&blen2,
0,
s
);
err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
&act_size, NULL, false, ds->s);
if (err) {
return 0;
dolog("Failed to lock buffer\n");
*size = 0;
return NULL;
}
len1 = blen1 >> hwshift;
len2 = blen2 >> hwshift;
decr = len1 + len2;
*size = act_size;
return ret;
}
if (p1 && len1) {
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
{
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
if (err) {
dolog("Failed to unlock buffer!!\n");
return;
}
if (p2 && len2) {
hw->conv (hw->conv_buf, p2, len2);
}
dsound_unlock_in (dscb, p1, p2, blen1, blen2);
hw->wpos = (hw->wpos + decr) % hw->samples;
return decr;
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
}
static void dsound_audio_fini (void *opaque)
@ -846,13 +661,17 @@ static void *dsound_audio_init(Audiodev *dev)
static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out,
.fini_out = dsound_fini_out,
.run_out = dsound_run_out,
.ctl_out = dsound_ctl_out,
.write = audio_generic_write,
.get_buffer_out = dsound_get_buffer_out,
.put_buffer_out = dsound_put_buffer_out,
.enable_out = dsound_enable_out,
.init_in = dsound_init_in,
.fini_in = dsound_fini_in,
.run_in = dsound_run_in,
.ctl_in = dsound_ctl_in
.read = audio_generic_read,
.get_buffer_in = dsound_get_buffer_in,
.put_buffer_in = dsound_put_buffer_in,
.enable_in = dsound_enable_in,
};
static struct audio_driver dsound_audio_driver = {

View File

@ -33,38 +33,27 @@
typedef struct NoVoiceOut {
HWVoiceOut hw;
int64_t old_ticks;
RateCtl rate;
} NoVoiceOut;
typedef struct NoVoiceIn {
HWVoiceIn hw;
int64_t old_ticks;
RateCtl rate;
} NoVoiceIn;
static size_t no_run_out(HWVoiceOut *hw, size_t live)
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
size_t decr, samples;
int64_t now;
int64_t ticks;
int64_t bytes;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - no->old_ticks;
bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
bytes = MIN(bytes, SIZE_MAX);
samples = bytes >> hw->info.shift;
no->old_ticks = now;
decr = MIN (live, samples);
hw->rpos = (hw->rpos + decr) % hw->samples;
return decr;
return audio_rate_get_bytes(&hw->info, &no->rate, len);
}
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
audio_rate_start(&no->rate);
return 0;
}
@ -73,17 +62,22 @@ static void no_fini_out (HWVoiceOut *hw)
(void) hw;
}
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void no_enable_out(HWVoiceOut *hw, bool enable)
{
(void) hw;
(void) cmd;
return 0;
NoVoiceOut *no = (NoVoiceOut *) hw;
if (enable) {
audio_rate_start(&no->rate);
}
}
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
audio_rate_start(&no->rate);
return 0;
}
@ -92,32 +86,22 @@ static void no_fini_in (HWVoiceIn *hw)
(void) hw;
}
static size_t no_run_in(HWVoiceIn *hw)
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
size_t live = audio_pcm_hw_get_live_in(hw);
size_t dead = hw->samples - live;
size_t samples = 0;
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
if (dead) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t ticks = now - no->old_ticks;
int64_t bytes =
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
no->old_ticks = now;
bytes = MIN (bytes, SIZE_MAX);
samples = bytes >> hw->info.shift;
samples = MIN (samples, dead);
}
return samples;
audio_pcm_info_clear_buf(&hw->info, buf, bytes >> hw->info.shift);
return bytes;
}
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
static void no_enable_in(HWVoiceIn *hw, bool enable)
{
(void) hw;
(void) cmd;
return 0;
NoVoiceIn *no = (NoVoiceIn *) hw;
if (enable) {
audio_rate_start(&no->rate);
}
}
static void *no_audio_init(Audiodev *dev)
@ -133,13 +117,13 @@ static void no_audio_fini (void *opaque)
static struct audio_pcm_ops no_pcm_ops = {
.init_out = no_init_out,
.fini_out = no_fini_out,
.run_out = no_run_out,
.ctl_out = no_ctl_out,
.write = no_write,
.enable_out = no_enable_out,
.init_in = no_init_in,
.fini_in = no_fini_in,
.run_in = no_run_in,
.ctl_in = no_ctl_in
.read = no_read,
.enable_in = no_enable_in
};
static struct audio_driver no_audio_driver = {

View File

@ -40,19 +40,15 @@
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
int fd;
int wpos;
int nfrags;
int fragsize;
int mmapped;
int pending;
Audiodev *dev;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
HWVoiceIn hw;
void *pcm_buf;
int fd;
int nfrags;
int fragsize;
@ -371,98 +367,87 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
return -1;
}
static void oss_write_pending (OSSVoiceOut *oss)
static size_t oss_get_available_bytes(OSSVoiceOut *oss)
{
HWVoiceOut *hw = &oss->hw;
int err;
struct count_info cntinfo;
assert(oss->mmapped);
if (oss->mmapped) {
return;
err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
while (oss->pending) {
int samples_written;
ssize_t bytes_written;
int samples_till_end = hw->samples - oss->wpos;
int samples_to_write = 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);
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
}
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 void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
if (oss->mmapped) {
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
return hw->buf_emul + hw->pos_emul;
} else {
return audio_generic_get_buffer_out(hw, size);
}
}
static size_t oss_run_out(HWVoiceOut *hw, size_t live)
static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
int err;
size_t decr;
struct audio_buf_info abinfo;
struct count_info cntinfo;
size_t bufsize;
if (oss->mmapped) {
assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
bufsize = hw->samples << hw->info.shift;
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
return size;
} else {
return audio_generic_put_buffer_out(hw, buf, size);
}
}
static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
size_t pos;
if (oss->mmapped) {
int bytes, pos;
size_t total_len;
len = MIN(len, oss_get_available_bytes(oss));
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
total_len = len;
while (len) {
size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
pos = hw->rpos << hw->info.shift;
bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
decr = MIN (bytes >> hw->info.shift, live);
}
else {
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
if (err < 0) {
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
if (abinfo.bytes > bufsize) {
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
abinfo.bytes = bufsize;
}
if (abinfo.bytes < 0) {
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
return 0;
}
decr = MIN (abinfo.bytes >> hw->info.shift, live);
if (!decr) {
return 0;
hw->pos_emul = (hw->pos_emul + to_copy) % hw->pos_emul;
buf += to_copy;
len -= to_copy;
}
return total_len;
}
decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
oss->pending += decr;
oss_write_pending (oss);
pos = 0;
while (len) {
ssize_t bytes_written;
void *pcm = advance(buf, pos);
return decr;
bytes_written = write(oss->fd, pcm, len);
if (bytes_written < 0) {
if (errno != EAGAIN) {
oss_logerr(errno, "failed to write %zu bytes\n",
len);
}
return pos;
}
pos += bytes_written;
if (bytes_written < len) {
break;
}
len -= bytes_written;
}
return pos;
}
static void oss_fini_out (HWVoiceOut *hw)
@ -473,18 +458,13 @@ static void oss_fini_out (HWVoiceOut *hw)
ldebug ("oss_fini\n");
oss_anal_close (&oss->fd);
if (oss->pcm_buf) {
if (oss->mmapped) {
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
oss->pcm_buf, hw->samples << hw->info.shift);
}
if (oss->mmapped && hw->buf_emul) {
err = munmap(hw->buf_emul, hw->size_emul);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
hw->buf_emul, hw->size_emul);
}
else {
g_free (oss->pcm_buf);
}
oss->pcm_buf = NULL;
hw->buf_emul = NULL;
}
}
@ -535,19 +515,20 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
oss->mmapped = 0;
if (oopts->has_try_mmap && oopts->try_mmap) {
oss->pcm_buf = mmap (
hw->size_emul = hw->samples << hw->info.shift;
hw->buf_emul = mmap(
NULL,
hw->samples << hw->info.shift,
hw->size_emul,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (oss->pcm_buf == MAP_FAILED) {
if (hw->buf_emul == MAP_FAILED) {
oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
hw->samples << hw->info.shift);
}
else {
hw->size_emul);
hw->buf_emul = NULL;
} else {
int err;
int trig = 0;
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@ -567,88 +548,65 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
}
if (!oss->mmapped) {
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
err = munmap(hw->buf_emul, hw->size_emul);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
oss->pcm_buf, hw->samples << hw->info.shift);
hw->buf_emul, hw->size_emul);
}
hw->buf_emul = NULL;
}
}
}
if (!oss->mmapped) {
oss->pcm_buf = audio_calloc(__func__,
hw->samples,
1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog (
"Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
hw->samples,
1 << hw->info.shift
);
oss_anal_close (&fd);
return -1;
}
}
oss->fd = fd;
oss->dev = dev;
return 0;
}
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void oss_enable_out(HWVoiceOut *hw, bool enable)
{
int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = opdo->try_poll;
if (enable) {
bool poll_mode = opdo->try_poll;
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;
}
ldebug("enabling voice\n");
if (poll_mode) {
oss_poll_out(hw);
poll_mode = 0;
}
break;
hw->poll_mode = poll_mode;
case VOICE_DISABLE:
if (!oss->mmapped) {
return;
}
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->mix_buf->size);
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;
}
} else {
if (hw->poll_mode) {
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
hw->poll_mode = 0;
}
if (!oss->mmapped) {
return 0;
return;
}
ldebug ("disabling voice\n");
trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
return -1;
return;
}
break;
}
return 0;
}
static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@ -692,13 +650,6 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
oss_anal_close (&fd);
return -1;
}
oss->fd = fd;
oss->dev = dev;
@ -710,106 +661,57 @@ static void oss_fini_in (HWVoiceIn *hw)
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
oss_anal_close (&oss->fd);
g_free(oss->pcm_buf);
oss->pcm_buf = NULL;
}
static size_t oss_run_in(HWVoiceIn *hw)
static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
int hwshift = hw->info.shift;
int i;
size_t live = audio_pcm_hw_get_live_in (hw);
size_t dead = hw->samples - live;
size_t read_samples = 0;
struct {
size_t add;
size_t len;
} bufs[2] = {
{ .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 }
};
size_t pos = 0;
if (!dead) {
return 0;
}
if (hw->wpos + dead > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
}
else {
bufs[0].len = dead << hwshift;
}
for (i = 0; i < 2; ++i) {
while (len) {
ssize_t nread;
if (bufs[i].len) {
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
nread = read (oss->fd, p, bufs[i].len);
void *dst = advance(buf, pos);
nread = read(oss->fd, dst, len);
if (nread > 0) {
if (nread & hw->info.align) {
dolog("warning: Misaligned read %zd (requested %zu), "
"alignment %d\n", nread, bufs[i].add << hwshift,
hw->info.align + 1);
}
read_samples += nread >> hwshift;
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
}
if (bufs[i].len - nread) {
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr(
errno,
"Failed to read %zu bytes of audio (to %p)\n",
bufs[i].len, p
);
break;
}
}
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
len, dst);
break;
}
}
pos += nread;
len -= nread;
}
hw->wpos = (hw->wpos + read_samples) % hw->samples;
return read_samples;
return pos;
}
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
static void oss_enable_in(HWVoiceIn *hw, bool enable)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = opdo->try_poll;
if (enable) {
bool poll_mode = opdo->try_poll;
if (poll_mode) {
oss_poll_in (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
if (poll_mode) {
oss_poll_in(hw);
poll_mode = 0;
}
break;
case VOICE_DISABLE:
hw->poll_mode = poll_mode;
} else {
if (hw->poll_mode) {
hw->poll_mode = 0;
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
}
break;
}
return 0;
}
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
@ -845,13 +747,15 @@ static void oss_audio_fini (void *opaque)
static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out,
.fini_out = oss_fini_out,
.run_out = oss_run_out,
.ctl_out = oss_ctl_out,
.write = oss_write,
.get_buffer_out = oss_get_buffer_out,
.put_buffer_out = oss_put_buffer_out,
.enable_out = oss_enable_out,
.init_in = oss_init_in,
.fini_in = oss_fini_in,
.run_in = oss_run_in,
.ctl_in = oss_ctl_in
.read = oss_read,
.enable_in = oss_enable_in
};
static struct audio_driver oss_audio_driver = {

View File

@ -9,7 +9,6 @@
#define AUDIO_CAP "pulseaudio"
#include "audio_int.h"
#include "audio_pt_int.h"
typedef struct PAConnection {
char *server;
@ -30,28 +29,16 @@ typedef struct {
typedef struct {
HWVoiceOut hw;
size_t done;
size_t live;
size_t decr;
size_t rpos;
pa_stream *stream;
void *pcm_buf;
struct audio_pt pt;
paaudio *g;
size_t samples;
} PAVoiceOut;
typedef struct {
HWVoiceIn hw;
size_t done;
size_t dead;
size_t incr;
size_t wpos;
pa_stream *stream;
void *pcm_buf;
struct audio_pt pt;
const void *read_data;
size_t read_index, read_length;
size_t read_length;
paaudio *g;
size_t samples;
} PAVoiceIn;
@ -89,298 +76,96 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
}
#endif
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
#define CHECK_SUCCESS_GOTO(c, expression, label, msg) \
do { \
if (!(expression)) { \
if (rerror) { \
*(rerror) = pa_context_errno ((c)->context); \
} \
qpa_logerr(pa_context_errno((c)->context), msg); \
goto label; \
} \
} while (0)
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
#define CHECK_DEAD_GOTO(c, stream, label, msg) \
do { \
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
!(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
if (rerror) { \
*(rerror) = pa_context_errno ((c)->context); \
} \
qpa_logerr(pa_context_errno((c)->context), msg); \
} else { \
if (rerror) { \
*(rerror) = PA_ERR_BADSTATE; \
} \
qpa_logerr(PA_ERR_BADSTATE, msg); \
} \
goto label; \
} \
} while (0)
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
{
PAVoiceIn *p = (PAVoiceIn *) hw;
PAConnection *c = p->g->conn;
size_t l;
int r;
pa_threaded_mainloop_lock(c->mainloop);
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
"pa_threaded_mainloop_lock failed\n");
while (length > 0) {
size_t l;
if (!p->read_length) {
r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
"pa_stream_peek failed\n");
}
while (!p->read_data) {
int r;
l = MIN(p->read_length, length);
memcpy(data, p->read_data, l);
r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
p->read_data += l;
p->read_length -= l;
if (!p->read_data) {
pa_threaded_mainloop_wait(c->mainloop);
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
} else {
p->read_index = 0;
}
}
l = p->read_length < length ? p->read_length : length;
memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
data = (uint8_t *) data + l;
length -= l;
p->read_index += l;
p->read_length -= l;
if (!p->read_length) {
int r;
r = pa_stream_drop (p->stream);
p->read_data = NULL;
p->read_length = 0;
p->read_index = 0;
CHECK_SUCCESS_GOTO(c, rerror, r == 0, unlock_and_fail);
}
if (!p->read_length) {
r = pa_stream_drop(p->stream);
CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
"pa_stream_drop failed\n");
}
pa_threaded_mainloop_unlock(c->mainloop);
return 0;
return l;
unlock_and_fail:
pa_threaded_mainloop_unlock(c->mainloop);
return -1;
return 0;
}
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
{
PAVoiceOut *p = (PAVoiceOut *) hw;
PAConnection *c = p->g->conn;
size_t l;
int r;
pa_threaded_mainloop_lock(c->mainloop);
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
"pa_threaded_mainloop_lock failed\n");
while (length > 0) {
size_t l;
int r;
l = pa_stream_writable_size(p->stream);
while (!(l = pa_stream_writable_size (p->stream))) {
pa_threaded_mainloop_wait(c->mainloop);
CHECK_DEAD_GOTO(c, p->stream, rerror, unlock_and_fail);
}
CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
"pa_stream_writable_size failed\n");
CHECK_SUCCESS_GOTO(c, rerror, l != (size_t) -1, unlock_and_fail);
if (l > length) {
l = length;
}
r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO(c, rerror, r >= 0, unlock_and_fail);
data = (const uint8_t *) data + l;
length -= l;
if (l > length) {
l = length;
}
r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
pa_threaded_mainloop_unlock(c->mainloop);
return 0;
return l;
unlock_and_fail:
pa_threaded_mainloop_unlock(c->mainloop);
return -1;
}
static void *qpa_thread_out (void *arg)
{
PAVoiceOut *pa = arg;
HWVoiceOut *hw = &pa->hw;
if (audio_pt_lock(&pa->pt, __func__)) {
return NULL;
}
for (;;) {
size_t decr, to_mix, rpos;
for (;;) {
if (pa->done) {
goto exit;
}
if (pa->live > 0) {
break;
}
if (audio_pt_wait(&pa->pt, __func__)) {
goto exit;
}
}
decr = to_mix = MIN(pa->live, pa->samples >> 5);
rpos = pa->rpos;
if (audio_pt_unlock(&pa->pt, __func__)) {
return NULL;
}
while (to_mix) {
int error;
size_t chunk = MIN (to_mix, hw->samples - rpos);
struct st_sample *src = hw->mix_buf + rpos;
hw->clip (pa->pcm_buf, src, chunk);
if (qpa_simple_write (pa, pa->pcm_buf,
chunk << hw->info.shift, &error) < 0) {
qpa_logerr (error, "pa_simple_write failed\n");
return NULL;
}
rpos = (rpos + chunk) % hw->samples;
to_mix -= chunk;
}
if (audio_pt_lock(&pa->pt, __func__)) {
return NULL;
}
pa->rpos = rpos;
pa->live -= decr;
pa->decr += decr;
}
exit:
audio_pt_unlock(&pa->pt, __func__);
return NULL;
}
static size_t qpa_run_out(HWVoiceOut *hw, size_t live)
{
size_t decr;
PAVoiceOut *pa = (PAVoiceOut *) hw;
if (audio_pt_lock(&pa->pt, __func__)) {
return 0;
}
decr = MIN (live, pa->decr);
pa->decr -= decr;
pa->live = live - decr;
hw->rpos = pa->rpos;
if (pa->live > 0) {
audio_pt_unlock_and_signal(&pa->pt, __func__);
}
else {
audio_pt_unlock(&pa->pt, __func__);
}
return decr;
}
/* capture */
static void *qpa_thread_in (void *arg)
{
PAVoiceIn *pa = arg;
HWVoiceIn *hw = &pa->hw;
if (audio_pt_lock(&pa->pt, __func__)) {
return NULL;
}
for (;;) {
size_t incr, to_grab, wpos;
for (;;) {
if (pa->done) {
goto exit;
}
if (pa->dead > 0) {
break;
}
if (audio_pt_wait(&pa->pt, __func__)) {
goto exit;
}
}
incr = to_grab = MIN(pa->dead, pa->samples >> 5);
wpos = pa->wpos;
if (audio_pt_unlock(&pa->pt, __func__)) {
return NULL;
}
while (to_grab) {
int error;
size_t chunk = MIN (to_grab, hw->samples - wpos);
void *buf = advance (pa->pcm_buf, wpos);
if (qpa_simple_read (pa, buf,
chunk << hw->info.shift, &error) < 0) {
qpa_logerr (error, "pa_simple_read failed\n");
return NULL;
}
hw->conv (hw->conv_buf + wpos, buf, chunk);
wpos = (wpos + chunk) % hw->samples;
to_grab -= chunk;
}
if (audio_pt_lock(&pa->pt, __func__)) {
return NULL;
}
pa->wpos = wpos;
pa->dead -= incr;
pa->incr += incr;
}
exit:
audio_pt_unlock(&pa->pt, __func__);
return NULL;
}
static size_t qpa_run_in(HWVoiceIn *hw)
{
size_t live, incr, dead;
PAVoiceIn *pa = (PAVoiceIn *) hw;
if (audio_pt_lock(&pa->pt, __func__)) {
return 0;
}
live = audio_pcm_hw_get_live_in (hw);
dead = hw->samples - live;
incr = MIN (dead, pa->incr);
pa->incr -= incr;
pa->dead = dead - incr;
hw->wpos = pa->wpos;
if (pa->dead > 0) {
audio_pt_unlock_and_signal(&pa->pt, __func__);
}
else {
audio_pt_unlock(&pa->pt, __func__);
}
return incr;
return 0;
}
static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
@ -468,13 +253,6 @@ static void stream_state_cb (pa_stream *s, void * userdata)
}
}
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
{
PAConnection *c = userdata;
pa_threaded_mainloop_signal(c->mainloop, 0);
}
static pa_stream *qpa_simple_new (
PAConnection *c,
const char *name,
@ -497,8 +275,6 @@ static pa_stream *qpa_simple_new (
}
pa_stream_set_state_callback(stream, stream_state_cb, c);
pa_stream_set_read_callback(stream, stream_request_cb, c);
pa_stream_set_write_callback(stream, stream_request_cb, c);
flags =
PA_STREAM_INTERPOLATE_TIMING
@ -579,28 +355,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
hw->samples = pa->samples = audio_buffer_samples(
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
&obt_as, ppdo->buffer_length);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
pa->rpos = hw->rpos;
if (!pa->pcm_buf) {
dolog("Could not allocate buffer (%zu bytes)\n",
hw->samples << hw->info.shift);
goto fail2;
}
if (audio_pt_init(&pa->pt, qpa_thread_out, hw, AUDIO_CAP, __func__)) {
goto fail3;
}
return 0;
fail3:
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
fail2:
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
fail1:
return -1;
}
@ -647,28 +404,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
hw->samples = pa->samples = audio_buffer_samples(
qapi_AudiodevPaPerDirectionOptions_base(ppdo),
&obt_as, ppdo->buffer_length);
pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
pa->wpos = hw->wpos;
if (!pa->pcm_buf) {
dolog("Could not allocate buffer (%zu bytes)\n",
hw->samples << hw->info.shift);
goto fail2;
}
if (audio_pt_init(&pa->pt, qpa_thread_in, hw, AUDIO_CAP, __func__)) {
goto fail3;
}
return 0;
fail3:
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
fail2:
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
fail1:
return -1;
}
@ -696,45 +434,25 @@ static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
static void qpa_fini_out (HWVoiceOut *hw)
{
void *ret;
PAVoiceOut *pa = (PAVoiceOut *) hw;
audio_pt_lock(&pa->pt, __func__);
pa->done = 1;
audio_pt_unlock_and_signal(&pa->pt, __func__);
audio_pt_join(&pa->pt, &ret, __func__);
if (pa->stream) {
qpa_simple_disconnect(pa->g->conn, pa->stream);
pa->stream = NULL;
}
audio_pt_fini(&pa->pt, __func__);
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
}
static void qpa_fini_in (HWVoiceIn *hw)
{
void *ret;
PAVoiceIn *pa = (PAVoiceIn *) hw;
audio_pt_lock(&pa->pt, __func__);
pa->done = 1;
audio_pt_unlock_and_signal(&pa->pt, __func__);
audio_pt_join(&pa->pt, &ret, __func__);
if (pa->stream) {
qpa_simple_disconnect(pa->g->conn, pa->stream);
pa->stream = NULL;
}
audio_pt_fini(&pa->pt, __func__);
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
}
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void qpa_volume_out(HWVoiceOut *hw, struct mixeng_volume *vol)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
pa_operation *op;
@ -745,49 +463,36 @@ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
#endif
switch (cmd) {
case VOICE_VOLUME:
{
SWVoiceOut *sw;
va_list ap;
v.channels = 2;
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->r) / UINT32_MAX;
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceOut *);
va_end (ap);
pa_threaded_mainloop_lock(c->mainloop);
v.channels = 2;
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
pa_threaded_mainloop_lock(c->mainloop);
op = pa_context_set_sink_input_volume(c->context,
pa_stream_get_index (pa->stream),
&v, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_sink_input_volume() failed\n");
} else {
pa_operation_unref(op);
}
op = pa_context_set_sink_input_mute(c->context,
pa_stream_get_index (pa->stream),
sw->vol.mute, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_sink_input_mute() failed\n");
} else {
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(c->mainloop);
}
op = pa_context_set_sink_input_volume(c->context,
pa_stream_get_index(pa->stream),
&v, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_sink_input_volume() failed\n");
} else {
pa_operation_unref(op);
}
return 0;
op = pa_context_set_sink_input_mute(c->context,
pa_stream_get_index(pa->stream),
vol->mute, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_sink_input_mute() failed\n");
} else {
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(c->mainloop);
}
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
static void qpa_volume_in(HWVoiceIn *hw, struct mixeng_volume *vol)
{
PAVoiceIn *pa = (PAVoiceIn *) hw;
pa_operation *op;
@ -798,46 +503,33 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
pa_cvolume_init (&v);
#endif
switch (cmd) {
case VOICE_VOLUME:
{
SWVoiceIn *sw;
va_list ap;
v.channels = 2;
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->r) / UINT32_MAX;
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceIn *);
va_end (ap);
pa_threaded_mainloop_lock(c->mainloop);
v.channels = 2;
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
pa_threaded_mainloop_lock(c->mainloop);
op = pa_context_set_source_output_volume(c->context,
pa_stream_get_index(pa->stream),
&v, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_source_output_volume() failed\n");
} else {
pa_operation_unref(op);
}
op = pa_context_set_source_output_mute(c->context,
pa_stream_get_index (pa->stream),
sw->vol.mute, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_source_output_mute() failed\n");
} else {
pa_operation_unref (op);
}
pa_threaded_mainloop_unlock(c->mainloop);
}
op = pa_context_set_source_output_volume(c->context,
pa_stream_get_index(pa->stream),
&v, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_source_output_volume() failed\n");
} else {
pa_operation_unref(op);
}
return 0;
op = pa_context_set_source_output_mute(c->context,
pa_stream_get_index(pa->stream),
vol->mute, NULL, NULL);
if (!op) {
qpa_logerr(pa_context_errno(c->context),
"set_source_output_mute() failed\n");
} else {
pa_operation_unref(op);
}
pa_threaded_mainloop_unlock(c->mainloop);
}
static int qpa_validate_per_direction_opts(Audiodev *dev,
@ -1005,13 +697,13 @@ static void qpa_audio_fini (void *opaque)
static struct audio_pcm_ops qpa_pcm_ops = {
.init_out = qpa_init_out,
.fini_out = qpa_fini_out,
.run_out = qpa_run_out,
.ctl_out = qpa_ctl_out,
.write = qpa_write,
.volume_out = qpa_volume_out,
.init_in = qpa_init_in,
.fini_in = qpa_fini_in,
.run_in = qpa_run_in,
.ctl_in = qpa_ctl_in
.read = qpa_read,
.volume_in = qpa_volume_in
};
static struct audio_driver pa_audio_driver = {
@ -1025,7 +717,6 @@ static struct audio_driver pa_audio_driver = {
.max_voices_in = INT_MAX,
.voice_size_out = sizeof (PAVoiceOut),
.voice_size_in = sizeof (PAVoiceIn),
.ctl_caps = VOICE_VOLUME_CAP
};
static void register_audio_pa(void)

View File

@ -41,8 +41,6 @@
typedef struct SDLVoiceOut {
HWVoiceOut hw;
size_t live;
size_t decr;
} SDLVoiceOut;
static struct SDLAudioState {
@ -184,62 +182,59 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLVoiceOut *sdl = opaque;
SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw;
size_t samples = len >> hw->info.shift;
size_t to_mix, decr;
if (s->exit || !sdl->live) {
if (s->exit) {
return;
}
/* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */
to_mix = MIN(samples, sdl->live);
decr = to_mix;
while (to_mix) {
size_t chunk = MIN(to_mix, hw->samples - hw->rpos);
struct st_sample *src = hw->mix_buf + hw->rpos;
while (hw->pending_emul && len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
/* dolog ("in callback to_mix %zu, chunk %zu\n", to_mix, chunk); */
hw->clip(buf, src, chunk);
hw->rpos = (hw->rpos + chunk) % hw->samples;
to_mix -= chunk;
buf += chunk << hw->info.shift;
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
memcpy(buf, hw->buf_emul + start, write_len);
hw->pending_emul -= write_len;
len -= write_len;
buf += write_len;
}
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
/* dolog ("done len=%zu\n", len); */
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
if (samples) {
memset(buf, 0, samples << hw->info.shift);
/* clear remaining buffer that we couldn't fill with data */
if (len) {
memset(buf, 0, len);
}
}
static size_t sdl_run_out(HWVoiceOut *hw, size_t live)
{
size_t decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDL_LockAudio();
if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n",
sdl->decr,
live,
sdl->live);
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \
static ret_type glue(sdl_, name)args_decl \
{ \
ret_type ret; \
\
SDL_LockAudio(); \
\
ret = glue(audio_generic_, name)args; \
\
SDL_UnlockAudio(); \
return ret; \
}
decr = MIN (sdl->decr, live);
sdl->decr -= decr;
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size), *size = 0, sdl_unlock)
SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
SDL_WRAPPER_FUNC(write, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
sdl->live = live;
SDL_UnlockAudio();
return decr;
}
#undef SDL_WRAPPER_FUNC
static void sdl_fini_out (HWVoiceOut *hw)
{
@ -290,20 +285,9 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
return 0;
}
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void sdl_enable_out(HWVoiceOut *hw, bool enable)
{
(void) hw;
switch (cmd) {
case VOICE_ENABLE:
SDL_PauseAudio (0);
break;
case VOICE_DISABLE:
SDL_PauseAudio (1);
break;
}
return 0;
SDL_PauseAudio(!enable);
}
static void *sdl_audio_init(Audiodev *dev)
@ -336,8 +320,10 @@ static void sdl_audio_fini (void *opaque)
static struct audio_pcm_ops sdl_pcm_ops = {
.init_out = sdl_init_out,
.fini_out = sdl_fini_out,
.run_out = sdl_run_out,
.ctl_out = sdl_ctl_out,
.write = sdl_write,
.get_buffer_out = sdl_get_buffer_out,
.put_buffer_out = sdl_put_buffer_out_nowrite,
.enable_out = sdl_enable_out,
};
static struct audio_driver sdl_audio_driver = {

View File

@ -40,27 +40,21 @@
#define LINE_IN_SAMPLES (256 * 4)
#endif
typedef struct SpiceRateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} SpiceRateCtl;
typedef struct SpiceVoiceOut {
HWVoiceOut hw;
SpicePlaybackInstance sin;
SpiceRateCtl rate;
RateCtl rate;
int active;
uint32_t *frame;
uint32_t *fpos;
uint32_t fpos;
uint32_t fsize;
} SpiceVoiceOut;
typedef struct SpiceVoiceIn {
HWVoiceIn hw;
SpiceRecordInstance sin;
SpiceRateCtl rate;
RateCtl rate;
int active;
uint32_t samples[LINE_IN_SAMPLES];
} SpiceVoiceIn;
static const SpicePlaybackInterface playback_sif = {
@ -90,32 +84,6 @@ static void spice_audio_fini (void *opaque)
/* nothing */
}
static void rate_start (SpiceRateCtl *rate)
{
memset (rate, 0, sizeof (*rate));
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
{
int64_t now;
int64_t ticks;
int64_t bytes;
int64_t samples;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
samples = (bytes - rate->bytes_sent) >> info->shift;
if (samples < 0 || samples > 65536) {
error_report("Resetting rate control (%" PRId64 " samples)", samples);
rate_start(rate);
samples = 0;
}
rate->bytes_sent += samples << info->shift;
return samples;
}
/* playback */
static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
@ -152,94 +120,77 @@ static void line_out_fini (HWVoiceOut *hw)
spice_server_remove_interface (&out->sin.base);
}
static size_t line_out_run (HWVoiceOut *hw, size_t live)
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
size_t rpos, decr;
size_t samples;
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
if (!live) {
return 0;
if (!out->frame) {
spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
out->fpos = 0;
}
decr = rate_get_samples (&hw->info, &out->rate);
decr = MIN (live, decr);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int len = MIN (samples, left_till_end_samples);
if (!out->frame) {
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
out->fpos = out->frame;
}
if (out->frame) {
len = MIN (len, out->fsize);
hw->clip (out->fpos, hw->mix_buf + rpos, len);
out->fsize -= len;
out->fpos += len;
if (out->fsize == 0) {
spice_server_playback_put_samples (&out->sin, out->frame);
out->frame = out->fpos = NULL;
}
}
rpos = (rpos + len) % hw->samples;
samples -= len;
if (out->frame) {
*size = audio_rate_get_bytes(
&hw->info, &out->rate, (out->fsize - out->fpos) << hw->info.shift);
} else {
audio_rate_start(&out->rate);
}
hw->rpos = rpos;
return decr;
return out->frame + out->fpos;
}
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
out->fpos += size >> 2;
if (out->fpos == out->fsize) { /* buffer full */
spice_server_playback_put_samples(&out->sin, out->frame);
out->frame = NULL;
}
return size;
}
static void line_out_enable(HWVoiceOut *hw, bool enable)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
switch (cmd) {
case VOICE_ENABLE:
if (enable) {
if (out->active) {
break;
return;
}
out->active = 1;
rate_start (&out->rate);
audio_rate_start(&out->rate);
spice_server_playback_start (&out->sin);
break;
case VOICE_DISABLE:
} else {
if (!out->active) {
break;
return;
}
out->active = 0;
if (out->frame) {
memset (out->fpos, 0, out->fsize << 2);
memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2);
spice_server_playback_put_samples (&out->sin, out->frame);
out->frame = out->fpos = NULL;
out->frame = NULL;
}
spice_server_playback_stop (&out->sin);
break;
case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
SWVoiceOut *sw;
va_list ap;
uint16_t vol[2];
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceOut *);
va_end (ap);
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
spice_server_playback_set_volume (&out->sin, 2, vol);
spice_server_playback_set_mute (&out->sin, sw->vol.mute);
#endif
break;
}
}
return 0;
}
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
static void line_out_volume(HWVoiceOut *hw, struct mixeng_volume *vol)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
uint16_t svol[2];
svol[0] = vol->l / ((1ULL << 16) + 1);
svol[1] = vol->r / ((1ULL << 16) + 1);
spice_server_playback_set_volume(&out->sin, 2, svol);
spice_server_playback_set_mute(&out->sin, vol->mute);
}
#endif
/* record */
static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@ -275,104 +226,73 @@ static void line_in_fini (HWVoiceIn *hw)
spice_server_remove_interface (&in->sin.base);
}
static size_t line_in_run(HWVoiceIn *hw)
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
size_t num_samples;
int ready;
size_t len[2];
uint64_t delta_samp;
const uint32_t *samples;
uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
return 0;
}
delta_samp = rate_get_samples (&hw->info, &in->rate);
num_samples = MIN (num_samples, delta_samp);
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
samples = in->samples;
/* XXX: do we need this? */
if (ready == 0) {
static const uint32_t silence[LINE_IN_SAMPLES];
samples = silence;
ready = LINE_IN_SAMPLES;
memset(buf, 0, to_read << 2);
ready = to_read;
}
num_samples = MIN (ready, num_samples);
if (hw->wpos + num_samples > hw->samples) {
len[0] = hw->samples - hw->wpos;
len[1] = num_samples - len[0];
} else {
len[0] = num_samples;
len[1] = 0;
}
hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
if (len[1]) {
hw->conv (hw->conv_buf, samples + len[0], len[1]);
}
hw->wpos = (hw->wpos + num_samples) % hw->samples;
return num_samples;
return ready << 2;
}
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
static void line_in_enable(HWVoiceIn *hw, bool enable)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
switch (cmd) {
case VOICE_ENABLE:
if (enable) {
if (in->active) {
break;
return;
}
in->active = 1;
rate_start (&in->rate);
audio_rate_start(&in->rate);
spice_server_record_start (&in->sin);
break;
case VOICE_DISABLE:
} else {
if (!in->active) {
break;
return;
}
in->active = 0;
spice_server_record_stop (&in->sin);
break;
case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
SWVoiceIn *sw;
va_list ap;
uint16_t vol[2];
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceIn *);
va_end (ap);
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
spice_server_record_set_volume (&in->sin, 2, vol);
spice_server_record_set_mute (&in->sin, sw->vol.mute);
#endif
break;
}
}
return 0;
}
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
static void line_in_volume(HWVoiceIn *hw, struct mixeng_volume *vol)
{
SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
uint16_t svol[2];
svol[0] = vol->l / ((1ULL << 16) + 1);
svol[1] = vol->r / ((1ULL << 16) + 1);
spice_server_record_set_volume(&in->sin, 2, svol);
spice_server_record_set_mute(&in->sin, vol->mute);
}
#endif
static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init,
.fini_out = line_out_fini,
.run_out = line_out_run,
.ctl_out = line_out_ctl,
.write = audio_generic_write,
.get_buffer_out = line_out_get_buffer,
.put_buffer_out = line_out_put_buffer,
.enable_out = line_out_enable,
#if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \
(SPICE_INTERFACE_PLAYBACK_MINOR >= 2)
.volume_out = line_out_volume,
#endif
.init_in = line_in_init,
.fini_in = line_in_fini,
.run_in = line_in_run,
.ctl_in = line_in_ctl,
.read = line_in_read,
.enable_in = line_in_enable,
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
.volume_in = line_in_volume,
#endif
};
static struct audio_driver spice_audio_driver = {
@ -385,9 +305,6 @@ static struct audio_driver spice_audio_driver = {
.max_voices_in = 1,
.voice_size_out = sizeof (SpiceVoiceOut),
.voice_size_in = sizeof (SpiceVoiceIn),
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
.ctl_caps = VOICE_VOLUME_CAP
#endif
};
void qemu_spice_audio_init (void)

View File

@ -35,53 +35,23 @@
typedef struct WAVVoiceOut {
HWVoiceOut hw;
FILE *f;
int64_t old_ticks;
void *pcm_buf;
RateCtl rate;
int total_samples;
} WAVVoiceOut;
static size_t wav_run_out(HWVoiceOut *hw, size_t live)
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
size_t rpos, decr, samples;
uint8_t *dst;
struct st_sample *src;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t ticks = now - wav->old_ticks;
int64_t bytes =
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
assert(bytes >> hw->info.shift << hw->info.shift == bytes);
if (bytes > INT_MAX) {
samples = INT_MAX >> hw->info.shift;
}
else {
samples = bytes >> hw->info.shift;
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
bytes, strerror(errno));
}
wav->old_ticks = now;
decr = MIN (live, samples);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int convert_samples = MIN (samples, left_till_end_samples);
src = hw->mix_buf + rpos;
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples);
if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
convert_samples << hw->info.shift, strerror (errno));
}
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
wav->total_samples += convert_samples;
}
hw->rpos = rpos;
return decr;
wav->total_samples += bytes >> hw->info.shift;
return bytes;
}
/* VICE code: Store number as little endian. */
@ -137,13 +107,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &wav_as);
hw->samples = 1024;
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!wav->pcm_buf) {
dolog("Could not allocate buffer (%zu bytes)\n",
hw->samples << hw->info.shift);
return -1;
}
le_store (hdr + 22, hw->info.nchannels, 2);
le_store (hdr + 24, hw->info.freq, 4);
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
@ -153,8 +116,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
wav_path, strerror(errno));
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1;
}
@ -163,6 +124,8 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
strerror(errno));
return -1;
}
audio_rate_start(&wav->rate);
return 0;
}
@ -208,16 +171,15 @@ static void wav_fini_out (HWVoiceOut *hw)
wav->f, strerror (errno));
}
wav->f = NULL;
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
}
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
static void wav_enable_out(HWVoiceOut *hw, bool enable)
{
(void) hw;
(void) cmd;
return 0;
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
if (enable) {
audio_rate_start(&wav->rate);
}
}
static void *wav_audio_init(Audiodev *dev)
@ -234,8 +196,8 @@ static void wav_audio_fini (void *opaque)
static struct audio_pcm_ops wav_pcm_ops = {
.init_out = wav_init_out,
.fini_out = wav_fini_out,
.run_out = wav_run_out,
.ctl_out = wav_ctl_out,
.write = wav_write_out,
.enable_out = wav_enable_out,
};
static struct audio_driver wav_audio_driver = {

5
configure vendored
View File

@ -297,7 +297,6 @@ host_cc="cc"
libs_cpu=""
libs_softmmu=""
libs_tools=""
audio_pt_int=""
audio_win_int=""
libs_qga=""
debug_info="yes"
@ -3388,7 +3387,6 @@ for drv in $audio_drv_list; do
pa | try-pa)
if $pkg_config libpulse --exists; then
pulse_libs=$($pkg_config libpulse --libs)
audio_pt_int="yes"
if test "$drv" = "try-pa"; then
audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa/pa/')
fi
@ -6611,9 +6609,6 @@ echo "PULSE_LIBS=$pulse_libs" >> $config_host_mak
echo "COREAUDIO_LIBS=$coreaudio_libs" >> $config_host_mak
echo "DSOUND_LIBS=$dsound_libs" >> $config_host_mak
echo "OSS_LIBS=$oss_libs" >> $config_host_mak
if test "$audio_pt_int" = "yes" ; then
echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak
fi
if test "$audio_win_int" = "yes" ; then
echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak
fi

View File

@ -439,13 +439,13 @@ DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev,
" in|out.format= sample format to use with fixed settings\n"
" valid values: s8, s16, s32, u8, u16, u32\n"
" in|out.voices= number of voices to use\n"
" in|out.buffer-len= length of buffer in microseconds\n"
" in|out.buffer-length= length of buffer in microseconds\n"
"-audiodev none,id=id,[,prop[=value][,...]]\n"
" dummy driver that discards all output\n"
#ifdef CONFIG_AUDIO_ALSA
"-audiodev alsa,id=id[,prop[=value][,...]]\n"
" in|out.dev= name of the audio device to use\n"
" in|out.period-len= length of period in microseconds\n"
" in|out.period-length= length of period in microseconds\n"
" in|out.try-poll= attempt to use poll mode\n"
" threshold= threshold (in microseconds) when playback starts\n"
#endif
@ -524,7 +524,7 @@ Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8},
@item in|out.voices=@var{voices}
Specify the number of @var{voices} to use. Default is 1.
@item in|out.buffer=@var{usecs}
@item in|out.buffer-length=@var{usecs}
Sets the size of the buffer in microseconds.
@end table
@ -545,7 +545,7 @@ ALSA specific options are:
Specify the ALSA @var{device} to use for input and/or output. Default
is @code{default}.
@item in|out.period-len=@var{usecs}
@item in|out.period-length=@var{usecs}
Sets the period length in microseconds.
@item in|out.try-poll=on|off