aboutsummaryrefslogtreecommitdiffstats
path: root/audio/esdaudio.c
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2011-01-02 12:58:51 +0100
committerDavid 'Digit' Turner <digit@android.com>2011-01-02 13:05:31 +0100
commit5d0e37bc290d1743cb5acf76eb6608f1303f27dd (patch)
tree142486cd40940a38aa4fe5947521b39dc2cec339 /audio/esdaudio.c
parente3650680f44fed0262d33eb4f486e5c1e58ddc32 (diff)
downloadexternal_qemu-5d0e37bc290d1743cb5acf76eb6608f1303f27dd.zip
external_qemu-5d0e37bc290d1743cb5acf76eb6608f1303f27dd.tar.gz
external_qemu-5d0e37bc290d1743cb5acf76eb6608f1303f27dd.tar.bz2
upstream: audio sub-system improvements.
This patch updates the audio subsystem to match the one in upstream. Note that this gets rid of the ability to specify different audio backends for input and output, which was never really used. A future patch will remove the -audio-in and -audio-out options and related code. Change-Id: I37c21672bcb15ef1f0e928c56bf99fbecda2bce6
Diffstat (limited to 'audio/esdaudio.c')
-rw-r--r--audio/esdaudio.c500
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)
};