Index: Makefile.target =================================================================== RCS file: /home/malc/cvsroot/bellard/qemu/Makefile.target,v retrieving revision 1.1.1.10 retrieving revision 1.1.1.1.2.11 diff -u -r1.1.1.10 -r1.1.1.1.2.11 --- Makefile.target 4 Aug 2006 23:49:02 -0000 1.1.1.10 +++ Makefile.target 3 Sep 2006 21:56:08 -0000 1.1.1.1.2.11 @@ -315,6 +315,10 @@ AUDIODRV += dsoundaudio.o LIBS += -lole32 -ldxguid endif +ifdef CONFIG_ESD +AUDIODRV += esdaudio.o +LIBS += -lesd +endif ifdef CONFIG_FMOD AUDIODRV += fmodaudio.o audio.o fmodaudio.o: DEFINES := -I$(CONFIG_FMOD_INC) $(DEFINES) @@ -539,7 +543,7 @@ endif ifeq (1, 0) -audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \ +audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o esdaudio.o \ fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \ CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare endif Index: configure =================================================================== RCS file: /home/malc/cvsroot/bellard/qemu/configure,v retrieving revision 1.1.1.8 retrieving revision 1.1.1.1.2.6 diff -u -r1.1.1.8 -r1.1.1.1.2.6 --- configure 27 Jun 2006 11:05:10 -0000 1.1.1.8 +++ configure 3 Sep 2006 21:56:15 -0000 1.1.1.1.2.6 @@ -81,6 +81,7 @@ dsound="no" coreaudio="no" alsa="no" +esd="no" fmod="no" fmod_lib="" fmod_inc="" @@ -207,6 +208,8 @@ ;; --enable-dsound) dsound="yes" ;; + --enable-esd) esd="yes" + ;; --enable-fmod) fmod="yes" ;; --fmod-lib=*) fmod_lib="$optarg" @@ -282,6 +285,7 @@ echo " --enable-adlib enable Adlib emulation" echo " --enable-coreaudio enable Coreaudio audio driver" echo " --enable-alsa enable ALSA audio driver" +echo " --enable-esd enable esd audio" echo " --enable-fmod enable FMOD audio driver" echo " --enabled-dsound enable DirectSound audio driver" echo " --enable-system enable all system emulation targets" @@ -545,6 +549,7 @@ echo "CoreAudio support $coreaudio" echo "ALSA support $alsa" echo "DSound support $dsound" +echo "ESD support $esd" if test "$fmod" = "yes"; then if test -z $fmod_lib || test -z $fmod_inc; then echo @@ -694,6 +699,10 @@ echo "CONFIG_DSOUND=yes" >> $config_mak echo "#define CONFIG_DSOUND 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 "$fmod" = "yes" ; then echo "CONFIG_FMOD=yes" >> $config_mak echo "CONFIG_FMOD_LIB=$fmod_lib" >> $config_mak Index: audio/audio.c =================================================================== RCS file: /home/malc/cvsroot/bellard/qemu/audio/audio.c,v retrieving revision 1.1.1.8 retrieving revision 1.1.1.1.2.20 diff -u -r1.1.1.8 -r1.1.1.1.2.20 --- audio/audio.c 4 Aug 2006 23:49:09 -0000 1.1.1.8 +++ audio/audio.c 3 Sep 2006 21:57:03 -0000 1.1.1.1.2.20 @@ -46,6 +46,9 @@ #ifdef CONFIG_DSOUND &dsound_audio_driver, #endif +#ifdef CONFIG_ESD + &esd_audio_driver, +#endif #ifdef CONFIG_FMOD &fmod_audio_driver, #endif @@ -605,11 +608,11 @@ } if (info->sign) { - memset (buf, 0x00, len << info->shift); + memset (buf, len << info->shift, 0x00); } else { if (info->bits == 8) { - memset (buf, 0x80, len << info->shift); + memset (buf, len << info->shift, 0x80); } else { int i; Index: audio/audio_int.h =================================================================== RCS file: /home/malc/cvsroot/bellard/qemu/audio/audio_int.h,v retrieving revision 1.1.1.7 retrieving revision 1.1.1.1.2.7 diff -u -r1.1.1.7 -r1.1.1.1.2.7 --- audio/audio_int.h 4 Aug 2006 23:49:09 -0000 1.1.1.7 +++ audio/audio_int.h 3 Sep 2006 21:57:09 -0000 1.1.1.1.2.7 @@ -200,6 +200,7 @@ extern struct audio_driver wav_audio_driver; extern struct audio_driver fmod_audio_driver; extern struct audio_driver alsa_audio_driver; +extern struct audio_driver esd_audio_driver; extern struct audio_driver coreaudio_audio_driver; extern struct audio_driver dsound_audio_driver; extern volume_t nominal_volume; Index: audio/esdaudio.c =================================================================== RCS file: audio/esdaudio.c diff -N audio/esdaudio.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ audio/esdaudio.c 3 Sep 2006 21:57:19 -0000 1.1.2.1 @@ -0,0 +1,404 @@ +/* + * QEMU ESD audio driver + * + * Copyright (c) 2006 Frederick Reeve (brushed up 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 +#include + +#include "vl.h" + +#define AUDIO_CAP "esd" +#include "audio_int.h" + +typedef struct ESDVoiceOut { + HWVoiceOut hw; + int done; + int live; + int decr; + int rpos; + void *pcm_buf; + int fd; + pthread_t thread; + pthread_cond_t cond; + pthread_mutex_t mutex; +} ESDVoiceOut; + +static struct { + int samples; +} conf = { + 1024 +}; + +static void GCC_FMT_ATTR (2, 3) esd_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)); +} + +static int esd_mlock (ESDVoiceOut *esd, const char *caller) +{ + int err; + + err = pthread_mutex_lock (&esd->mutex); + if (err) { + esd_logerr (err, "pthread_mutex_lock failed for %s", caller); + return -1; + } + return 0; +} + +static int esd_munlock (ESDVoiceOut *esd, const char *caller) +{ + int err; + + err = pthread_mutex_unlock (&esd->mutex); + if (err) { + esd_logerr (err, "pthread_mutex_unlock failed for %s", caller); + return -1; + } + return 0; +} + +static int esd_cwait (ESDVoiceOut *esd, const char *caller) +{ + int err; + + err = pthread_cond_wait (&esd->cond, &esd->mutex); + if (err) { + esd_logerr (err, "pthread_cond_wait failed for %s", caller); + return -1; + } + return 0; +} + +static int esd_munlock_and_csignal (ESDVoiceOut *esd, const char *caller) +{ + int err; + + err = pthread_mutex_unlock (&esd->mutex); + if (err) { + esd_logerr (err, "[us] pthread_mutex_unlock failed for %s", caller); + return -1; + } + + err = pthread_cond_signal (&esd->cond); + if (err) { + esd_logerr (err, "[us] pthread_cond_signal failed for %s", caller); + return -1; + } + return 0; +} + +static void *esd_thread (void *arg) +{ + ESDVoiceOut *esd = arg; + HWVoiceOut *hw = &esd->hw; + + for (;;) { + int decr, to_mix, rpos; + + if (esd->done) { + break; + } + + if (esd_cwait (esd, AUDIO_FUNC)) { + if (esd->done) { + break; + } + } + + if (esd->live < hw->samples / 2) { + continue; + } + + decr = to_mix = esd->live; + rpos = hw->rpos; + + if (esd_munlock (esd, 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; + } + esd_logerr (errno, "write failed"); + 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 (esd_mlock (esd, AUDIO_FUNC)) + return NULL; + + esd->rpos = rpos; + esd->live = 0; + esd->decr += decr; + } + + esd_munlock (esd, AUDIO_FUNC); + return NULL; +} + +static int esd_run_out (HWVoiceOut *hw) +{ + int live, decr; + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + + if (esd_mlock (esd, 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) { + esd_munlock_and_csignal (esd, AUDIO_FUNC); + } + else { + esd_munlock (esd, AUDIO_FUNC); + } + return decr; +} + +static int esd_write_out (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int esd_init_out (HWVoiceOut *hw, audsettings_t *as) +{ + int i, err; + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + audsettings_t obt_as = *as; + int esdfmt = ESD_STREAM | ESD_PLAY; + + err = pthread_mutex_init (&esd->mutex, NULL); + if (err) { + esd_logerr (err, "%s: pthread_mutex_init failed", AUDIO_FUNC); + goto fail0; + } + + err = pthread_cond_init (&esd->cond, NULL); + if (err) { + esd_logerr (err, "%s: pthread_cond_init failed", AUDIO_FUNC); + goto fail1; + } + + 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; + } + 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; + for (i = 0; i < 10000; i++) { + esd->fd = esd_play_stream (esdfmt, as->freq, NULL, NULL); + if (esd->fd < 0) { + if (errno == EINTR) { + continue; + } + esd_logerr (errno, "esd_play_stream failed"); + goto fail2; + } + break; + } + + if (esd->fd < 0) { + esd_logerr (errno, "esd_play_stream failed"); + goto fail2; + } + + err = pthread_create (&esd->thread, NULL, esd_thread, esd); + if (err) { + esd_logerr (err, "%s: pthred_create failed", AUDIO_FUNC); + goto fail3; + } + + return 0; + + fail3: + if (close (esd->fd)) { + esd_logerr (errno, "%s: close on esd socket(%d) failed", + AUDIO_FUNC, esd->fd); + } + esd->fd = -1; + + fail2: + qemu_free (esd->pcm_buf); + esd->pcm_buf = NULL; + + err = pthread_cond_destroy (&esd->cond); + if (err) { + esd_logerr (err, "%s: pthread_cond_destroy failed", AUDIO_FUNC); + } + + fail1: + err = pthread_mutex_destroy (&esd->mutex); + if (err) { + esd_logerr (err, "%s: pthread_mutex_destroy failed", AUDIO_FUNC); + } + + fail0: + return -1; +} + +static void esd_fini_out (HWVoiceOut *hw) +{ + int err; + void *ret; + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + + esd_mlock (esd, AUDIO_FUNC); + esd->done = 1; + esd_munlock_and_csignal (esd, AUDIO_FUNC); + + err = pthread_join (esd->thread, &ret); + if (err) { + esd_logerr (err, "%s: pthread_join failed", AUDIO_FUNC); + } + + if (esd->fd >= 0) { + if (close (esd->fd)) { + esd_logerr (errno, "failed to close esd socket"); + } + esd->fd = -1; + } + + err = pthread_cond_destroy (&esd->cond); + if (err) { + esd_logerr (err, "%s: pthread_cond_destroy failed", AUDIO_FUNC); + } + + err = pthread_mutex_destroy (&esd->mutex); + if (err) { + esd_logerr (err, "%s: pthread_mutex_destroy failed", AUDIO_FUNC); + } + + qemu_free (esd->pcm_buf); + esd->pcm_buf = NULL; +} + +static int esd_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + (void) hw; + (void) cmd; + return 0; +} + +static void *esd_audio_init (void) +{ + return &conf; +} + +static void esd_audio_fini (void *opaque) +{ + (void) opaque; + ldebug ("esd_fini"); +} + +struct audio_option esd_options[] = { + {"SAMPLES", AUD_OPT_INT, &conf.samples, + "buffer size in samples", NULL, 0}, + + {NULL, 0, NULL, NULL, NULL, 0} +}; + +struct audio_pcm_ops esd_pcm_ops = { + esd_init_out, + esd_fini_out, + esd_run_out, + esd_write_out, + esd_ctl_out, + + NULL, + NULL, + NULL, + NULL, + NULL +}; + +struct audio_driver esd_audio_driver = { + INIT_FIELD (name = ) "esd", + INIT_FIELD (descr = ) + "http://en.wikipedia.org/wiki/Esound", + INIT_FIELD (options = ) esd_options, + INIT_FIELD (init = ) esd_audio_init, + INIT_FIELD (fini = ) esd_audio_fini, + INIT_FIELD (pcm_ops = ) &esd_pcm_ops, + INIT_FIELD (can_be_default = ) 0, + INIT_FIELD (max_voices_out = ) 1, + INIT_FIELD (max_voices_in = ) 0, + INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut), + INIT_FIELD (voice_size_in = ) 0 +};