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