]> git.proxmox.com Git - qemu.git/blobdiff - audio/ossaudio.c
janitor: do not include qemu-char everywhere
[qemu.git] / audio / ossaudio.c
index ecb88395b7729f92751d5b7c91100ca61fda7e59..8249a0044935e45a429098cabc6f363b8f8e4c5e 100644 (file)
 #include <sys/soundcard.h>
 #endif
 #include "qemu-common.h"
+#include "main-loop.h"
 #include "host-utils.h"
-#include "qemu-char.h"
 #include "audio.h"
 
 #define AUDIO_CAP "oss"
 #include "audio_int.h"
 
+#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
+#define USE_DSP_POLICY
+#endif
+
 typedef struct OSSVoiceOut {
     HWVoiceOut hw;
     void *pcm_buf;
     int fd;
+    int wpos;
     int nfrags;
     int fragsize;
     int mmapped;
+    int pending;
 } OSSVoiceOut;
 
 typedef struct OSSVoiceIn {
@@ -114,11 +120,13 @@ static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
 
 static void oss_anal_close (int *fdp)
 {
-    int err = close (*fdp);
+    int err;
+
+    qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
+    err = close (*fdp);
     if (err) {
         oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
     }
-    qemu_set_fd_handler (*fdp, NULL, NULL, NULL);
     *fdp = -1;
 }
 
@@ -153,7 +161,7 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static int aud_to_ossfmt (audfmt_e fmt)
+static int aud_to_ossfmt (audfmt_e fmt, int endianness)
 {
     switch (fmt) {
     case AUD_FMT_S8:
@@ -163,10 +171,20 @@ static int aud_to_ossfmt (audfmt_e fmt)
         return AFMT_U8;
 
     case AUD_FMT_S16:
-        return AFMT_S16_LE;
+        if (endianness) {
+            return AFMT_S16_BE;
+        }
+        else {
+            return AFMT_S16_LE;
+        }
 
     case AUD_FMT_U16:
-        return AFMT_U16_LE;
+        if (endianness) {
+            return AFMT_U16_BE;
+        }
+        else {
+            return AFMT_U16_LE;
+        }
 
     default:
         dolog ("Internal logic error: Bad audio format %d\n", fmt);
@@ -232,14 +250,39 @@ static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
 }
 #endif
 
+#ifdef USE_DSP_POLICY
+static int oss_get_version (int fd, int *version, const char *typ)
+{
+    if (ioctl (fd, OSS_GETVERSION, &version)) {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+        /*
+         * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
+         * since 7.x, but currently only on the mixer device (or in
+         * the Linuxolator), and in the native version that part of
+         * the code is in fact never reached so the ioctl fails anyway.
+         * Until this is fixed, just check the errno and if its what
+         * FreeBSD's sound drivers return atm assume they are new enough.
+         */
+        if (errno == EINVAL) {
+            *version = 0x040000;
+            return 0;
+        }
+#endif
+        oss_logerr2 (errno, typ, "Failed to get OSS version\n");
+        return -1;
+    }
+    return 0;
+}
+#endif
+
 static int oss_open (int in, struct oss_params *req,
                      struct oss_params *obt, int *pfd)
 {
     int fd;
-    int version;
     int oflags = conf.exclusive ? O_EXCL : 0;
     audio_buf_info abinfo;
     int fmt, freq, nchannels;
+    int setfragment = 1;
     const char *dspname = in ? conf.devpath_in : conf.devpath_out;
     const char *typ = in ? "ADC" : "DAC";
 
@@ -277,27 +320,30 @@ static int oss_open (int in, struct oss_params *req,
         goto err;
     }
 
-    if (ioctl (fd, OSS_GETVERSION, &version)) {
-        oss_logerr2 (errno, typ, "Failed to get OSS version\n");
-        version = 0;
-    }
+#ifdef USE_DSP_POLICY
+    if (conf.policy >= 0) {
+        int version;
 
-    if (conf.debug) {
-        dolog ("OSS version = %#x\n", version);
-    }
+        if (!oss_get_version (fd, &version, typ)) {
+            if (conf.debug) {
+                dolog ("OSS version = %#x\n", version);
+            }
 
-#ifdef SNDCTL_DSP_POLICY
-    if (conf.policy >= 0 && version >= 0x040000) {
-        int policy = conf.policy;
-        if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
-            oss_logerr2 (errno, typ, "Failed to set timing policy to %d\n",
-                         conf.policy);
-            goto err;
+            if (version >= 0x040000) {
+                int policy = conf.policy;
+                if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+                    oss_logerr2 (errno, typ,
+                                 "Failed to set timing policy to %d\n",
+                                 conf.policy);
+                    goto err;
+                }
+                setfragment = 0;
+            }
         }
     }
-    else
 #endif
-    {
+
+    if (setfragment) {
         int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
         if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
             oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
@@ -345,22 +391,54 @@ static int oss_open (int in, struct oss_params *req,
     return -1;
 }
 
-static int oss_run_out (HWVoiceOut *hw)
+static void oss_write_pending (OSSVoiceOut *oss)
+{
+    HWVoiceOut *hw = &oss->hw;
+
+    if (oss->mmapped) {
+        return;
+    }
+
+    while (oss->pending) {
+        int samples_written;
+        ssize_t bytes_written;
+        int samples_till_end = hw->samples - oss->wpos;
+        int samples_to_write = audio_MIN (oss->pending, samples_till_end);
+        int bytes_to_write = samples_to_write << hw->info.shift;
+        void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
+
+        bytes_written = write (oss->fd, pcm, bytes_to_write);
+        if (bytes_written < 0) {
+            if (errno != EAGAIN) {
+                oss_logerr (errno, "failed to write %d bytes\n",
+                            bytes_to_write);
+            }
+            break;
+        }
+
+        if (bytes_written & hw->info.align) {
+            dolog ("misaligned write asked for %d, but got %zd\n",
+                   bytes_to_write, bytes_written);
+            return;
+        }
+
+        samples_written = bytes_written >> hw->info.shift;
+        oss->pending -= samples_written;
+        oss->wpos = (oss->wpos + samples_written) % hw->samples;
+        if (bytes_written - bytes_to_write) {
+            break;
+        }
+    }
+}
+
+static int oss_run_out (HWVoiceOut *hw, int live)
 {
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
-    int err, rpos, live, decr;
-    int samples;
-    uint8_t *dst;
-    struct st_sample *src;
+    int err, decr;
     struct audio_buf_info abinfo;
     struct count_info cntinfo;
     int bufsize;
 
-    live = audio_pcm_hw_get_live_out (hw);
-    if (!live) {
-        return 0;
-    }
-
     bufsize = hw->samples << hw->info.shift;
 
     if (oss->mmapped) {
@@ -386,7 +464,7 @@ static int oss_run_out (HWVoiceOut *hw)
         if (abinfo.bytes > bufsize) {
             if (conf.debug) {
                 dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
-                       "please report your OS/audio hw to malc@pulsesoft.com\n",
+                       "please report your OS/audio hw to av1474@comtv.ru\n",
                        abinfo.bytes, bufsize);
             }
             abinfo.bytes = bufsize;
@@ -406,50 +484,10 @@ static int oss_run_out (HWVoiceOut *hw)
         }
     }
 
-    samples = decr;
-    rpos = hw->rpos;
-    while (samples) {
-        int left_till_end_samples = hw->samples - rpos;
-        int convert_samples = audio_MIN (samples, left_till_end_samples);
-
-        src = hw->mix_buf + rpos;
-        dst = advance (oss->pcm_buf, rpos << hw->info.shift);
+    decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
+    oss->pending += decr;
+    oss_write_pending (oss);
 
-        hw->clip (dst, src, convert_samples);
-        if (!oss->mmapped) {
-            int written;
-
-            written = write (oss->fd, dst, convert_samples << hw->info.shift);
-            /* XXX: follow errno recommendations ? */
-            if (written == -1) {
-                oss_logerr (
-                    errno,
-                    "Failed to write %d bytes of audio data from %p\n",
-                    convert_samples << hw->info.shift,
-                    dst
-                    );
-                continue;
-            }
-
-            if (written != convert_samples << 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);
-                }
-                decr -= wsamples;
-                rpos = (rpos + wsamples) % hw->samples;
-                break;
-            }
-        }
-
-        rpos = (rpos + convert_samples) % hw->samples;
-        samples -= convert_samples;
-    }
-
-    hw->rpos = rpos;
     return decr;
 }
 
@@ -470,7 +508,7 @@ static void oss_fini_out (HWVoiceOut *hw)
             }
         }
         else {
-            qemu_free (oss->pcm_buf);
+            g_free (oss->pcm_buf);
         }
         oss->pcm_buf = NULL;
     }
@@ -488,7 +526,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 
     oss->fd = -1;
 
-    req.fmt = aud_to_ossfmt (as->fmt);
+    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
     req.fragsize = conf.fragsize;
@@ -587,34 +625,37 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
 static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
     int trig;
-    va_list ap;
-    int poll_mode;
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
 
-    va_start (ap, cmd);
-    poll_mode = va_arg (ap, int);
-    va_end (ap);
-
     switch (cmd) {
     case VOICE_ENABLE:
-        ldebug ("enabling voice\n");
-        if (poll_mode && oss_poll_out (hw)) {
-            poll_mode = 0;
-        }
-        hw->poll_mode = poll_mode;
+        {
+            va_list ap;
+            int poll_mode;
 
-        if (!oss->mmapped) {
-            return 0;
-        }
+            va_start (ap, cmd);
+            poll_mode = va_arg (ap, int);
+            va_end (ap);
 
-        audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
-        trig = PCM_ENABLE_OUTPUT;
-        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
-            oss_logerr (
-                errno,
-                "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
-                );
-            return -1;
+            ldebug ("enabling voice\n");
+            if (poll_mode && oss_poll_out (hw)) {
+                poll_mode = 0;
+            }
+            hw->poll_mode = poll_mode;
+
+            if (!oss->mmapped) {
+                return 0;
+            }
+
+            audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+            trig = PCM_ENABLE_OUTPUT;
+            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+                oss_logerr (
+                    errno,
+                    "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+                    );
+                return -1;
+            }
         }
         break;
 
@@ -651,7 +692,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
 
     oss->fd = -1;
 
-    req.fmt = aud_to_ossfmt (as->fmt);
+    req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
     req.freq = as->freq;
     req.nchannels = as->nchannels;
     req.fragsize = conf.fragsize;
@@ -700,7 +741,7 @@ static void oss_fini_in (HWVoiceIn *hw)
     oss_anal_close (&oss->fd);
 
     if (oss->pcm_buf) {
-        qemu_free (oss->pcm_buf);
+        g_free (oss->pcm_buf);
         oss->pcm_buf = NULL;
     }
 }
@@ -747,8 +788,7 @@ static int oss_run_in (HWVoiceIn *hw)
                            hw->info.align + 1);
                 }
                 read_samples += nread >> hwshift;
-                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
-                          &nominal_volume);
+                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
             }
 
             if (bufs[i].len - nread) {
@@ -782,20 +822,23 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size)
 
 static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
 {
-    va_list ap;
-    int poll_mode;
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
 
-    va_start (ap, cmd);
-    poll_mode = va_arg (ap, int);
-    va_end (ap);
-
     switch (cmd) {
     case VOICE_ENABLE:
-        if (poll_mode && oss_poll_in (hw)) {
-            poll_mode = 0;
+        {
+            va_list ap;
+            int poll_mode;
+
+            va_start (ap, cmd);
+            poll_mode = va_arg (ap, int);
+            va_end (ap);
+
+            if (poll_mode && oss_poll_in (hw)) {
+                poll_mode = 0;
+            }
+            hw->poll_mode = poll_mode;
         }
-        hw->poll_mode = poll_mode;
         break;
 
     case VOICE_DISABLE:
@@ -855,7 +898,7 @@ static struct audio_option oss_options[] = {
         .valp  = &conf.exclusive,
         .descr = "Open device in exclusive mode (vmix wont work)"
     },
-#ifdef SNDCTL_DSP_POLICY
+#ifdef USE_DSP_POLICY
     {
         .name  = "POLICY",
         .tag   = AUD_OPT_INT,