pthreads-based audio and miscellaneous audio clean-up (malc).
authorbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 14 Jan 2008 04:24:29 +0000 (04:24 +0000)
committerbalrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Mon, 14 Jan 2008 04:24:29 +0000 (04:24 +0000)
ESD support (malc, Frederick Reeve).

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3917 c046a42c-6fe2-441c-8c8c-71466251a162

15 files changed:
Makefile
Makefile.target
audio/alsaaudio.c
audio/audio.c
audio/audio_int.h
audio/audio_pt_int.c [new file with mode: 0644]
audio/audio_pt_int.h [new file with mode: 0644]
audio/dsound_template.h
audio/dsoundaudio.c
audio/esdaudio.c [new file with mode: 0644]
audio/ossaudio.c
audio/wavaudio.c
configure
hw/dma.c
hw/sb16.c

index 232c55d..eaa9e81 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -73,6 +73,7 @@ AUDIO_OBJS += ossaudio.o
 endif
 ifdef CONFIG_COREAUDIO
 AUDIO_OBJS += coreaudio.o
+AUDIO_PT = yes
 endif
 ifdef CONFIG_ALSA
 AUDIO_OBJS += alsaaudio.o
@@ -84,6 +85,17 @@ ifdef CONFIG_FMOD
 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))
 
index acd6cda..6c8180e 100644 (file)
@@ -404,6 +404,9 @@ endif
 ifdef CONFIG_ALSA
 LIBS += -lasound
 endif
+ifdef CONFIG_ESD
+LIBS += -lesd
+endif
 ifdef CONFIG_DSOUND
 LIBS += -lole32 -ldxguid
 endif
@@ -412,6 +415,9 @@ LIBS += $(CONFIG_FMOD_LIB)
 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
@@ -641,8 +647,9 @@ 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
index 77a08a1..43cfa25 100644 (file)
@@ -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;
index 5e9d88b..2bea53e 100644 (file)
@@ -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)
                     );
index cd22a30..f969602 100644 (file)
@@ -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 (file)
index 0000000..e889a98
--- /dev/null
@@ -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 (file)
index 0000000..0dfff76
--- /dev/null
@@ -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 */
index 0896b04..d517c90 100644 (file)
  */
 #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;
index 41ddf4a..cba8c80 100644 (file)
@@ -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 (file)
index 0000000..fa42348
--- /dev/null
@@ -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)
+};
index 5a91556..2a300c2 100644 (file)
@@ -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;
 
index 11ca86d..ea7c6a8 100644 (file)
@@ -44,7 +44,7 @@ static struct {
         44100,
         2,
         AUD_FMT_S16,
-        AUDIO_HOST_ENDIANNESS
+        0
     },
     "qemu.wav"
 };
index b8014fe..6de2fbf 100755 (executable)
--- a/configure
+++ b/configure
@@ -89,6 +89,7 @@ oss="no"
 dsound="no"
 coreaudio="no"
 alsa="no"
+esd="no"
 fmod="no"
 fmod_lib=""
 fmod_inc=""
@@ -261,6 +262,8 @@ for opt do
   ;;
   --enable-alsa) alsa="yes"
   ;;
+  --enable-esd) esd="yes"
+  ;;
   --enable-dsound) dsound="yes"
   ;;
   --enable-fmod) fmod="yes"
@@ -405,6 +408,7 @@ echo "  --enable-mingw32         enable Win32 cross compilation with mingw32"
 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"
@@ -717,6 +721,7 @@ echo "mingw32 support   $mingw32"
 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
@@ -902,6 +907,10 @@ if test "$alsa" = "yes" ; 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
index 1891b22..f192c63 100644 (file)
--- a/hw/dma.c
+++ b/hw/dma.c
@@ -439,6 +439,13 @@ static void dma_reset(void *opaque)
     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)
@@ -471,6 +478,9 @@ static void dma_init2(struct dma_cont *d, int base, int dshift,
     }
     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)
index effbfbc..c22de7a 100644 (file)
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -1193,6 +1193,12 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
     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;
     }