aboutsummaryrefslogtreecommitdiff
path: root/audio
diff options
context:
space:
mode:
authorbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>2008-01-14 04:24:29 +0000
committerbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>2008-01-14 04:24:29 +0000
commitca9cc28c62a2c2877186569f4ab0cf1034502a73 (patch)
treed46c878d8f03c294452d625c851d6bb3668c5087 /audio
parentb34d259a81500d75e4cf435f1e8b262ba7e1421a (diff)
downloadqemu-ca9cc28c62a2c2877186569f4ab0cf1034502a73.zip
qemu-ca9cc28c62a2c2877186569f4ab0cf1034502a73.tar.gz
qemu-ca9cc28c62a2c2877186569f4ab0cf1034502a73.tar.bz2
pthreads-based audio and miscellaneous audio clean-up (malc).
ESD support (malc, Frederick Reeve). git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3917 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'audio')
-rw-r--r--audio/alsaaudio.c88
-rw-r--r--audio/audio.c5
-rw-r--r--audio/audio_int.h1
-rw-r--r--audio/audio_pt_int.c149
-rw-r--r--audio/audio_pt_int.h22
-rw-r--r--audio/dsound_template.h9
-rw-r--r--audio/dsoundaudio.c20
-rw-r--r--audio/esdaudio.c591
-rw-r--r--audio/ossaudio.c2
-rw-r--r--audio/wavaudio.c2
10 files changed, 837 insertions, 52 deletions
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 77a08a1..43cfa25 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -86,9 +86,9 @@ static struct {
};
struct alsa_params_req {
- unsigned int freq;
- audfmt_e fmt;
- unsigned int nchannels;
+ int freq;
+ snd_pcm_format_t fmt;
+ int nchannels;
unsigned int buffer_size;
unsigned int period_size;
};
@@ -96,6 +96,7 @@ struct alsa_params_req {
struct alsa_params_obt {
int freq;
audfmt_e fmt;
+ int endianness;
int nchannels;
snd_pcm_uframes_t samples;
};
@@ -143,7 +144,7 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
-static int aud_to_alsafmt (audfmt_e fmt)
+static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt)
{
switch (fmt) {
case AUD_FMT_S8:
@@ -173,7 +174,8 @@ static int aud_to_alsafmt (audfmt_e fmt)
}
}
-static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+ int *endianness)
{
switch (alsafmt) {
case SND_PCM_FORMAT_S8:
@@ -234,7 +236,6 @@ static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
return 0;
}
-#if defined DEBUG_MISMATCHES || defined DEBUG
static void alsa_dump_info (struct alsa_params_req *req,
struct alsa_params_obt *obt)
{
@@ -248,7 +249,6 @@ static void alsa_dump_info (struct alsa_params_req *req,
req->buffer_size, req->period_size);
dolog ("obtained: samples %ld\n", obt->samples);
}
-#endif
static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
{
@@ -291,6 +291,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
unsigned int period_size, buffer_size;
snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC";
+ snd_pcm_format_t obtfmt;
freq = req->freq;
period_size = req->period_size;
@@ -327,9 +328,8 @@ static int alsa_open (int in, struct alsa_params_req *req,
}
err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
- if (err < 0) {
+ if (err < 0 && conf.verbose) {
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
- goto err;
}
err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
@@ -494,6 +494,17 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
+ err = snd_pcm_hw_params_get_format (hw_params, &obtfmt);
+ if (err < 0) {
+ alsa_logerr2 (err, typ, "Failed to get format\n");
+ goto err;
+ }
+
+ if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
+ dolog ("Invalid format was returned %d\n", obtfmt);
+ goto err;
+ }
+
err = snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
@@ -504,28 +515,41 @@ static int alsa_open (int in, struct alsa_params_req *req,
snd_pcm_uframes_t threshold;
int bytes_per_sec;
- bytes_per_sec = freq
- << (nchannels == 2)
- << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
+ bytes_per_sec = freq << (nchannels == 2);
+
+ switch (obt->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ bytes_per_sec <<= 1;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ bytes_per_sec <<= 2;
+ break;
+ }
threshold = (conf.threshold * bytes_per_sec) / 1000;
alsa_set_threshold (handle, threshold);
}
- obt->fmt = req->fmt;
obt->nchannels = nchannels;
obt->freq = freq;
obt->samples = obt_buffer_size;
+
*handlep = handle;
-#if defined DEBUG_MISMATCHES || defined DEBUG
- if (obt->fmt != req->fmt ||
- obt->nchannels != req->nchannels ||
- obt->freq != req->freq) {
- dolog ("Audio paramters mismatch for %s\n", typ);
+ if (conf.verbose &&
+ (obt->fmt != req->fmt ||
+ obt->nchannels != req->nchannels ||
+ obt->freq != req->freq)) {
+ dolog ("Audio paramters for %s\n", typ);
alsa_dump_info (req, obt);
}
-#endif
#ifdef DEBUG
alsa_dump_info (req, obt);
@@ -665,9 +689,6 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
struct alsa_params_req req;
struct alsa_params_obt obt;
- audfmt_e effective_fmt;
- int endianness;
- int err;
snd_pcm_t *handle;
audsettings_t obt_as;
@@ -681,16 +702,10 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
return -1;
}
- err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
- if (err) {
- alsa_anal_close (&handle);
- return -1;
- }
-
obt_as.freq = obt.freq;
obt_as.nchannels = obt.nchannels;
- obt_as.fmt = effective_fmt;
- obt_as.endianness = endianness;
+ obt_as.fmt = obt.fmt;
+ obt_as.endianness = obt.endianness;
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
@@ -751,9 +766,6 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
struct alsa_params_req req;
struct alsa_params_obt obt;
- int endianness;
- int err;
- audfmt_e effective_fmt;
snd_pcm_t *handle;
audsettings_t obt_as;
@@ -767,16 +779,10 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
return -1;
}
- err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
- if (err) {
- alsa_anal_close (&handle);
- return -1;
- }
-
obt_as.freq = obt.freq;
obt_as.nchannels = obt.nchannels;
- obt_as.fmt = effective_fmt;
- obt_as.endianness = endianness;
+ obt_as.fmt = obt.fmt;
+ obt_as.endianness = obt.endianness;
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
diff --git a/audio/audio.c b/audio/audio.c
index 5e9d88b..2bea53e 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -56,6 +56,9 @@ static struct audio_driver *drvtab[] = {
#ifdef CONFIG_SDL
&sdl_audio_driver,
#endif
+#ifdef CONFIG_ESD
+ &esd_audio_driver,
+#endif
&no_audio_driver,
&wav_audio_driver
};
@@ -414,7 +417,7 @@ static void audio_print_options (const char *prefix,
{
audfmt_e *fmtp = opt->valp;
printf (
- "format, %s = %s, (one of: U8 S8 U16 S16)\n",
+ "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
state,
audio_audfmt_to_string (*fmtp)
);
diff --git a/audio/audio_int.h b/audio/audio_int.h
index cd22a30..f969602 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -202,6 +202,7 @@ extern struct audio_driver fmod_audio_driver;
extern struct audio_driver alsa_audio_driver;
extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
+extern struct audio_driver esd_audio_driver;
extern volume_t nominal_volume;
void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as);
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
new file mode 100644
index 0000000..e889a98
--- /dev/null
+++ b/audio/audio_pt_int.c
@@ -0,0 +1,149 @@
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "audio-pt"
+
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (pt->drv, fmt, ap);
+ va_end (ap);
+
+ AUD_log (NULL, "\n");
+ AUD_log (pt->drv, "Reason: %s\n", strerror (err));
+}
+
+int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
+ void *opaque, const char *drv, const char *cap)
+{
+ int err, err2;
+ const char *efunc;
+
+ p->drv = drv;
+
+ err = pthread_mutex_init (&p->mutex, NULL);
+ if (err) {
+ efunc = "pthread_mutex_init";
+ goto err0;
+ }
+
+ err = pthread_cond_init (&p->cond, NULL);
+ if (err) {
+ efunc = "pthread_cond_init";
+ goto err1;
+ }
+
+ err = pthread_create (&p->thread, NULL, func, opaque);
+ if (err) {
+ efunc = "pthread_create";
+ goto err2;
+ }
+
+ return 0;
+
+ err2:
+ err2 = pthread_cond_destroy (&p->cond);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+ }
+
+ err1:
+ err2 = pthread_mutex_destroy (&p->mutex);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+ }
+
+ err0:
+ logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
+ return -1;
+}
+
+int audio_pt_fini (struct audio_pt *p, const char *cap)
+{
+ int err, ret = 0;
+
+ err = pthread_cond_destroy (&p->cond);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+ ret = -1;
+ }
+
+ err = pthread_mutex_destroy (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+ ret = -1;
+ }
+ return ret;
+}
+
+int audio_pt_lock (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_lock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_unlock (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_wait (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_cond_wait (&p->cond, &p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ err = pthread_cond_signal (&p->cond);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
+{
+ int err;
+ void *ret;
+
+ err = pthread_join (p->thread, &ret);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ *arg = ret;
+ return 0;
+}
diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h
new file mode 100644
index 0000000..0dfff76
--- /dev/null
+++ b/audio/audio_pt_int.h
@@ -0,0 +1,22 @@
+#ifndef QEMU_AUDIO_PT_INT_H
+#define QEMU_AUDIO_PT_INT_H
+
+#include <pthread.h>
+
+struct audio_pt {
+ const char *drv;
+ pthread_t thread;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+};
+
+int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
+ const char *, const char *);
+int audio_pt_fini (struct audio_pt *, const char *);
+int audio_pt_lock (struct audio_pt *, const char *);
+int audio_pt_unlock (struct audio_pt *, const char *);
+int audio_pt_wait (struct audio_pt *, const char *);
+int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
+int audio_pt_join (struct audio_pt *, void **, const char *);
+
+#endif /* audio_pt_int.h */
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 0896b04..d517c90 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -23,16 +23,20 @@
*/
#ifdef DSBTYPE_IN
#define NAME "capture buffer"
+#define NAME2 "DirectSoundCapture"
#define TYPE in
#define IFACE IDirectSoundCaptureBuffer
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
#define FIELD dsound_capture_buffer
+#define FIELD2 dsound_capture
#else
#define NAME "playback buffer"
+#define NAME2 "DirectSound"
#define TYPE out
#define IFACE IDirectSoundBuffer
#define BUFPTR LPDIRECTSOUNDBUFFER
#define FIELD dsound_buffer
+#define FIELD2 dsound
#endif
static int glue (dsound_unlock_, TYPE) (
@@ -192,6 +196,11 @@ static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
DSBCAPS bc;
#endif
+ if (!s->FIELD2) {
+ dsound_logerr ("Attempt to initialize voice without " NAME2 " object");
+ return -1;
+ }
+
err = waveformat_from_audio_settings (&wfx, as);
if (err) {
return -1;
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 41ddf4a..cba8c80 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -320,23 +320,22 @@ static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
switch (as->fmt) {
case AUD_FMT_S8:
- wfx->wBitsPerSample = 8;
- break;
-
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_U16:
- wfx->wBitsPerSample = 16;
- wfx->nAvgBytesPerSec <<= 1;
- wfx->nBlockAlign <<= 1;
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ wfx->wBitsPerSample = 32;
+ wfx->nAvgBytesPerSec <<= 2;
+ wfx->nBlockAlign <<= 2;
break;
default:
@@ -387,8 +386,13 @@ static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
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 or 16, but %d\n",
+ dolog ("Invalid wave format, bits per sample is not "
+ "8, 16 or 32, but %d\n",
wfx->wBitsPerSample);
return -1;
}
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
new file mode 100644
index 0000000..fa42348
--- /dev/null
+++ b/audio/esdaudio.c
@@ -0,0 +1,591 @@
+/*
+ * QEMU ESD audio driver
+ *
+ * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <esd.h>
+#include "qemu-common.h"
+#include "audio.h"
+#include <signal.h>
+
+#define AUDIO_CAP "esd"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+typedef struct {
+ HWVoiceOut hw;
+ int done;
+ int live;
+ int decr;
+ int rpos;
+ void *pcm_buf;
+ int fd;
+ struct audio_pt pt;
+} ESDVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ int done;
+ int dead;
+ int incr;
+ int wpos;
+ void *pcm_buf;
+ int fd;
+ struct audio_pt pt;
+} ESDVoiceIn;
+
+static struct {
+ int samples;
+ int divisor;
+ char *dac_host;
+ char *adc_host;
+} conf = {
+ 1024,
+ 2,
+ NULL,
+ NULL
+};
+
+static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+/* playback */
+static void *qesd_thread_out (void *arg)
+{
+ ESDVoiceOut *esd = arg;
+ HWVoiceOut *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ 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);
+ st_sample_t *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 %d), "
+ "alignment %d\n",
+ wbytes, written, hw->info.align + 1);
+ }
+ to_mix -= wsamples;
+ rpos = (rpos + wsamples) % hw->samples;
+ break;
+ }
+
+ rpos = (rpos + chunk) % hw->samples;
+ to_mix -= chunk;
+ }
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ 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, decr;
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_out (hw);
+ 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);
+ }
+ return decr;
+}
+
+static int qesd_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int qesd_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+ audsettings_t obt_as = *as;
+ int esdfmt = ESD_STREAM | ESD_PLAY;
+ int err;
+ sigset_t set, old_set;
+
+ sigfillset (&set);
+
+ esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ esdfmt |= ESD_BITS8;
+ obt_as.fmt = AUD_FMT_U8;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("Will use 16 instead of 32 bit samples\n");
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ deffmt:
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
+#ifdef DEBUG_FMOD
+ abort ();
+#endif
+ goto deffmt;
+
+ }
+ obt_as.endianness = 0;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ hw->samples = conf.samples;
+ esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!esd->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ return -1;
+ }
+
+ esd->fd = -1;
+ err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+ if (err) {
+ qesd_logerr (err, "pthread_sigmask failed\n");
+ goto fail1;
+ }
+
+ esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
+ if (esd->fd < 0) {
+ qesd_logerr (errno, "esd_play_stream failed\n");
+ goto fail2;
+ }
+
+ if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail3;
+ }
+
+ err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err) {
+ qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+ }
+
+ return 0;
+
+ fail3:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail2:
+ err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err) {
+ qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+ }
+
+ fail1:
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+ 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;
+}
+
+static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* capture */
+static void *qesd_thread_in (void *arg)
+{
+ ESDVoiceIn *esd = arg;
+ HWVoiceIn *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ for (;;) {
+ int incr, to_grab, wpos;
+
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->dead > threshold) {
+ break;
+ }
+
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ incr = to_grab = esd->dead;
+ wpos = hw->wpos;
+
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ 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 %d), "
+ "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;
+ }
+
+ esd->wpos = wpos;
+ esd->dead -= incr;
+ esd->incr += incr;
+ }
+
+ 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)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int qesd_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+ audsettings_t obt_as = *as;
+ int esdfmt = ESD_STREAM | ESD_RECORD;
+ int err;
+ sigset_t set, old_set;
+
+ sigfillset (&set);
+
+ esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ esdfmt |= ESD_BITS8;
+ obt_as.fmt = AUD_FMT_U8;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("Will use 16 instead of 32 bit samples\n");
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+ }
+ obt_as.endianness = 0;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ hw->samples = conf.samples;
+ esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!esd->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ return -1;
+ }
+
+ esd->fd = -1;
+
+ err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+ if (err) {
+ qesd_logerr (err, "pthread_sigmask failed\n");
+ goto fail1;
+ }
+
+ esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
+ if (esd->fd < 0) {
+ qesd_logerr (errno, "esd_record_stream failed\n");
+ goto fail2;
+ }
+
+ if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail3;
+ }
+
+ err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err) {
+ qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+ }
+
+ return 0;
+
+ fail3:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail2:
+ err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+ if (err) {
+ qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+ }
+
+ fail1:
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+ 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;
+}
+
+static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* common */
+static void *qesd_audio_init (void)
+{
+ return &conf;
+}
+
+static void qesd_audio_fini (void *opaque)
+{
+ (void) opaque;
+ ldebug ("esd_fini");
+}
+
+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}
+};
+
+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,
+};
+
+struct audio_driver esd_audio_driver = {
+ INIT_FIELD (name = ) "esd",
+ INIT_FIELD (descr = )
+ "http://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 = ) 0,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) INT_MAX,
+ INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn)
+};
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 5a91556..2a300c2 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -150,7 +150,7 @@ static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
{
switch (ossfmt) {
case AFMT_S8:
- *endianness =0;
+ *endianness = 0;
*fmt = AUD_FMT_S8;
break;
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 11ca86d..ea7c6a8 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -44,7 +44,7 @@ static struct {
44100,
2,
AUD_FMT_S16,
- AUDIO_HOST_ENDIANNESS
+ 0
},
"qemu.wav"
};