endif
ifdef CONFIG_COREAUDIO
AUDIO_OBJS += coreaudio.o
+AUDIO_PT = yes
endif
ifdef CONFIG_ALSA
AUDIO_OBJS += alsaaudio.o
AUDIO_OBJS += fmodaudio.o
audio/audio.o audio/fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS)
endif
+ifdef CONFIG_ESD
+AUDIO_PT = yes
+AUDIO_PT_INT = yes
+AUDIO_OBJS += esdaudio.o
+endif
+ifdef AUDIO_PT
+LDFLAGS += -pthread
+endif
+ifdef AUDIO_PT_INT
+AUDIO_OBJS += audio_pt_int.o
+endif
AUDIO_OBJS+= wavcapture.o
OBJS+=$(addprefix audio/, $(AUDIO_OBJS))
ifdef CONFIG_ALSA
LIBS += -lasound
endif
+ifdef CONFIG_ESD
+LIBS += -lesd
+endif
ifdef CONFIG_DSOUND
LIBS += -lole32 -ldxguid
endif
endif
SOUND_HW = sb16.o es1370.o
+ifdef CONFIG_AC97
+SOUND_HW += ac97.o
+endif
ifdef CONFIG_ADLIB
SOUND_HW += fmopl.o adlib.o
endif
ifeq (1, 0)
audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
-fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \
-CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
+fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o ac97.o gus.o adlib.o \
+esdaudio.o audio_pt_int.o: \
+CFLAGS := $(CFLAGS) -O0 -g -Wall -Werror -W -Wsign-compare -Wno-unused
endif
# Include automatically generated dependency files
};
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;
};
struct alsa_params_obt {
int freq;
audfmt_e fmt;
+ int endianness;
int nchannels;
snd_pcm_uframes_t samples;
};
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:
}
}
-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:
return 0;
}
-#if defined DEBUG_MISMATCHES || defined DEBUG
static void alsa_dump_info (struct alsa_params_req *req,
struct alsa_params_obt *obt)
{
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)
{
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;
}
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);
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);
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);
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;
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;
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;
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;
#endif
#ifdef CONFIG_SDL
&sdl_audio_driver,
+#endif
+#ifdef CONFIG_ESD
+ &esd_audio_driver,
#endif
&no_audio_driver,
&wav_audio_driver
{
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)
);
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);
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
*/
#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) (
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;
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:
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;
}
--- /dev/null
+/*
+ * 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)
+};
{
switch (ossfmt) {
case AFMT_S8:
- *endianness =0;
+ *endianness = 0;
*fmt = AUD_FMT_S8;
break;
44100,
2,
AUD_FMT_S16,
- AUDIO_HOST_ENDIANNESS
+ 0
},
"qemu.wav"
};
dsound="no"
coreaudio="no"
alsa="no"
+esd="no"
fmod="no"
fmod_lib=""
fmod_inc=""
;;
--enable-alsa) alsa="yes"
;;
+ --enable-esd) esd="yes"
+ ;;
--enable-dsound) dsound="yes"
;;
--enable-fmod) fmod="yes"
echo " --enable-adlib enable Adlib emulation"
echo " --enable-coreaudio enable Coreaudio audio driver"
echo " --enable-alsa enable ALSA audio driver"
+echo " --enable-esd enable EsoundD audio driver"
echo " --enable-fmod enable FMOD audio driver"
echo " --enable-dsound enable DirectSound audio driver"
echo " --disable-vnc-tls disable TLS encryption for VNC server"
echo "Adlib support $adlib"
echo "CoreAudio support $coreaudio"
echo "ALSA support $alsa"
+echo "EsounD support $esd"
echo "DSound support $dsound"
if test "$fmod" = "yes"; then
if test -z $fmod_lib || test -z $fmod_inc; then
echo "CONFIG_ALSA=yes" >> $config_mak
echo "#define CONFIG_ALSA 1" >> $config_h
fi
+if test "$esd" = "yes" ; then
+ echo "CONFIG_ESD=yes" >> $config_mak
+ echo "#define CONFIG_ESD 1" >> $config_h
+fi
if test "$dsound" = "yes" ; then
echo "CONFIG_DSOUND=yes" >> $config_mak
echo "#define CONFIG_DSOUND 1" >> $config_h
write_cont (d, (0x0d << d->dshift), 0);
}
+static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+ dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
+ nchan, dma_pos, dma_len);
+ return dma_pos;
+}
+
/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
static void dma_init2(struct dma_cont *d, int base, int dshift,
int page_base, int pageh_base)
}
qemu_register_reset(dma_reset, d);
dma_reset(d);
+ for (i = 0; i < LENOFA (d->regs); ++i) {
+ d->regs[i].transfer_handler = dma_phony_handler;
+ }
}
static void dma_save (QEMUFile *f, void *opaque)
SB16State *s = opaque;
int till, copy, written, free;
+ if (s->block_size <= 0) {
+ dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
+ s->block_size, nchan, dma_pos, dma_len);
+ return dma_pos;
+ }
+
if (s->left_till_irq < 0) {
s->left_till_irq = s->block_size;
}