diff options
Diffstat (limited to 'audio/esdaudio.c')
-rw-r--r-- | audio/esdaudio.c | 500 |
1 files changed, 312 insertions, 188 deletions
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) }; |