chiark / gitweb /
OSS support in speaker
authorrjk@greenend.org.uk <>
Thu, 4 Oct 2007 11:41:37 +0000 (12:41 +0100)
committerrjk@greenend.org.uk <>
Thu, 4 Oct 2007 11:41:37 +0000 (12:41 +0100)
doc/disorder_config.5.in
lib/configuration.c
lib/configuration.h
plugins/exec.c
server/Makefile.am
server/disorderd.c
server/normalize.c
server/play.c
server/speaker-oss.c [new file with mode: 0644]
server/speaker.c
server/speaker.h

index 1f0b266..26b1efc 100644 (file)
@@ -1,3 +1,4 @@
+
 .\"
 .\" Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
 .\"
@@ -349,7 +350,7 @@ The default is
 With the
 .B network
 backend the sample format is forced to
-.B 16/44100/2b
+.B 16b/44100/2
 and with the
 .B coreaudio
 backend it is forced to
@@ -377,6 +378,10 @@ default.
 Use Apple Core Audio.  This only available on OS X systems, on which it is the
 default.
 .TP
+.B oss
+Use the OSS (/dev/dsp) API.  Not available on all platforms.  Not well
+maintained at the moment.
+.TP
 .B command
 Execute a command.  This is the default if
 .B speaker_command
index 448a8ab..92acce1 100644 (file)
@@ -458,6 +458,14 @@ static int set_backend(const struct config_state *cs,
          cs->path, cs->line);
     return -1;
 #endif
+  } else if(!strcmp(vec[0], "oss")) {
+#if HAVE_SYS_SOUNDCARD_H
+    *valuep = BACKEND_OSS;
+#else
+    error(0, "%s:%d: OSS is not available on this platform",
+         cs->path, cs->line);
+    return -1;
+#endif
   } else {
     error(0, "%s:%d: invalid '%s' value '%s'",
          cs->path, cs->line, whoami->name, vec[0]);
@@ -1091,20 +1099,21 @@ static void config_postdefaults(struct config *c,
     if(c->speaker_backend == BACKEND_NETWORK && !c->broadcast.n)
       fatal(0, "speaker_backend is network but broadcast is not set");
   }
-  if(c->speaker_backend == BACKEND_NETWORK) {
-    /* Override sample format */
+  /* Override sample format */
+  switch(c->speaker_backend) {
+  case BACKEND_NETWORK:
     c->sample_format.rate = 44100;
     c->sample_format.channels = 2;
     c->sample_format.bits = 16;
     c->sample_format.endian = ENDIAN_BIG;
-  }
-  if(c->speaker_backend == BACKEND_COREAUDIO) {
-    /* Override sample format */
+    break;
+  case BACKEND_COREAUDIO:
     c->sample_format.rate = 44100;
     c->sample_format.channels = 2;
     c->sample_format.bits = 16;
     c->sample_format.endian = ENDIAN_NATIVE;
-  }
+    break; 
+ }
 }
 
 /** @brief (Re-)read the config file
index e4ac7b9..4dad503 100644 (file)
@@ -183,6 +183,7 @@ struct config {
 #define BACKEND_COMMAND 1              /**< Execute a command */
 #define BACKEND_NETWORK 2              /**< Transmit RTP  */
 #define BACKEND_COREAUDIO 3            /**< Use Core Audio (Mac only) */
+#define BACKEND_OSS 4                  /**< Use OSS */
 
   /** @brief Home directory for state files */
   const char *home;
index fb01df7..7584809 100644 (file)
@@ -46,7 +46,7 @@ void disorder_play_track(const char *const *parameters,
   vec[j++] = path;
   vec[j] = 0;
   execvp(vec[0], (char **)vec);
-  disorder_fatal(errno, "error executing %s", vec[0]);
+  disorder_fatal(errno, "executing %s", vec[0]);
 }
 
 /*
index 1aef562..8fc3dfe 100644 (file)
@@ -48,6 +48,7 @@ disorder_speaker_SOURCES=speaker.c speaker.h \
                         speaker-command.c \
                         speaker-network.c \
                         speaker-coreaudio.c \
+                        speaker-oss.c \
                         speaker-alsa.c
 disorder_speaker_LDADD=$(LIBOBJS) ../lib/libdisorder.a \
        $(LIBASOUND) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(COREAUDIO)
index b41dade..f4a1a62 100644 (file)
@@ -186,7 +186,8 @@ static void volumecheck_after(long offset) {
 /* We fix the path to include the bindir and sbindir we were installed into */
 static void fix_path(void) {
   char *path = getenv("PATH");
-  char *newpath;
+  static char *newpath;
+  /* static or libgc collects it! */
 
   if(!path)
     error(0, "PATH is not set at all!");
index 073b869..8ea6efa 100644 (file)
@@ -134,9 +134,11 @@ int main(int argc, char attribute((unused)) **argv) {
       if(r < 0) {
         if(errno != EINTR)
           fatal(errno, "error reading header");
-      } else if(r == 0)
-        fatal(0, "EOF reading header");
-      else
+      } else if(r == 0) {
+        if(n)
+          fatal(0, "EOF reading header");
+        break;
+      } else
         n += r;
     }
     /* Sanity check the header */
index 35d756b..1ca6434 100644 (file)
@@ -343,8 +343,11 @@ static int start(ev_source *ev,
     }
   /* Use the second arg as the tag if available (it's probably a command name),
    * otherwise the module name. */
-  lfd = logfd(ev, (config->player.s[n].s[2]
-                  ? config->player.s[n].s[2] : config->player.s[n].s[1]));
+  if(!isatty(2))
+    lfd = logfd(ev, (config->player.s[n].s[2]
+                    ? config->player.s[n].s[2] : config->player.s[n].s[1]));
+  else
+    lfd = -1;
   optc = config->player.s[n].n - 2;
   optv = (void *)&config->player.s[n].s[2];
   while(optc > 0 && optv[0][0] == '-') {
@@ -371,9 +374,11 @@ static int start(ev_source *ev,
     exitfn = _exit;
     ev_signal_atfork(ev);
     signal(SIGPIPE, SIG_DFL);
-    xdup2(lfd, 1);
-    xdup2(lfd, 2);
-    xclose(lfd);                       /* tidy up */
+    if(lfd != -1) {
+      xdup2(lfd, 1);
+      xdup2(lfd, 2);
+      xclose(lfd);                     /* tidy up */
+    }
     setpgid(0, 0);
     if((q->type & DISORDER_PLAYER_TYPEMASK) == DISORDER_PLAYER_RAW) {
       /* "Raw" format players always have their output send down a pipe
@@ -467,11 +472,13 @@ static int start(ev_source *ev,
     error(errno, "error calling fork");
     if(q->type & DISORDER_PLAYER_PREFORK)
       play_cleanup(q->pl, q->data);    /* else would leak */
-    xclose(lfd);
+    if(lfd != -1)
+      xclose(lfd);
     return START_SOFTFAIL;
   }
   store_player_pid(q->id, pid);
-  xclose(lfd);
+  if(lfd != -1)
+    xclose(lfd);
   setpgid(pid, pid);
   ev_child(ev, pid, 0, player_finished, q);
   D(("player subprocess ID %lu", (unsigned long)pid));
diff --git a/server/speaker-oss.c b/server/speaker-oss.c
new file mode 100644 (file)
index 0000000..f9a298c
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * This file is part of DisOrder
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+/** @file server/speaker-oss.c
+ * @brief Support for @ref BACKEND_OSS */
+
+#include <config.h>
+
+#if HAVE_SYS_SOUNDCARD_H
+
+#include "types.h"
+
+#include <unistd.h>
+#include <poll.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#include "configuration.h"
+#include "syscalls.h"
+#include "log.h"
+#include "speaker-protocol.h"
+#include "speaker.h"
+
+/** @brief Current device */
+static int ossfd = -1;
+
+/** @brief OSS backend initialization */
+static void oss_init(void) {
+  info("selected OSS backend");
+}
+
+/** @brief OSS deactivation */
+static void oss_deactivate(void) {
+  if(ossfd != -1) {
+    xclose(ossfd);
+    ossfd = -1;
+    device_state = device_closed;
+    D(("released audio device"));
+  }
+}
+
+/** @brief OSS backend activation */
+static void oss_activate(void) {
+  int stereo, format, rate;
+  const char *device;
+
+  if(ossfd == -1) {
+    /* Try to pick a device */
+    if(!strcmp(config->device, "default")) {
+      if(access("/dev/dsp", W_OK) == 0)
+       device = "/dev/dsp";
+      else if(access("/dev/audio", W_OK) == 0)
+       device = "/dev/audio";
+    } else
+      device = config->device; /* just believe the user */
+    /* Open the device */
+    if((ossfd = open(device, O_WRONLY, 0)) < 0) {
+      error(errno, "error opening %s", device);
+      goto failed;
+    }
+    /* Set the audio format */
+    stereo = (config->sample_format.channels == 2);
+    if(ioctl(ossfd, SNDCTL_DSP_STEREO, &stereo) < 0) {
+      error(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
+      goto failed;
+    }
+    if(config->sample_format.bits == 8)
+      format = AFMT_U8;
+    else if(config->sample_format.bits == 16)
+      format = (config->sample_format.endian == ENDIAN_LITTLE
+               ? AFMT_S16_LE : AFMT_S16_BE);
+    else {
+      error(0, "unsupported sample_format for oss backend"); 
+      goto failed;
+    }
+    if(ioctl(ossfd, SNDCTL_DSP_SETFMT, &format) < 0) {
+      error(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
+      goto failed;
+    }
+    rate = config->sample_format.rate;
+    if(ioctl(ossfd, SNDCTL_DSP_SPEED, &rate) < 0) {
+      error(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
+      goto failed;
+    }
+    if((unsigned)rate != config->sample_format.rate)
+      error(0, "asked for %luHz, got %dHz",
+           (unsigned long)config->sample_format.rate, rate);
+    nonblock(ossfd);
+    device_state = device_open;
+  }
+  return;
+failed:
+  device_state = device_error;
+  if(ossfd >= 0)
+    xclose(ossfd);
+}
+
+/** @brief Play via OSS */
+static size_t oss_play(size_t frames) {
+  const size_t bytes_to_play = frames * bpf;
+  ssize_t bytes_written;
+
+  bytes_written = write(ossfd, playing->buffer + playing->start,
+                       bytes_to_play);
+  if(bytes_written < 0)
+    switch(errno) {
+    case EINTR:                        /* interruped */
+    case EAGAIN:               /* overrun */
+      return 0;                        /* try again later */
+    default:
+      fatal(errno, "error writing to audio device");
+    }
+  return bytes_written / bpf;
+}
+
+static int oss_slot;
+
+/** @brief Fill in poll fd array for OSS */
+static void oss_beforepoll(void) {
+  oss_slot = addfd(ossfd, POLLOUT|POLLERR);
+}
+
+/** @brief Process poll() results for OSS */
+static int oss_ready(void) {
+  return !!(fds[oss_slot].revents & (POLLOUT|POLLERR));
+}
+
+const struct speaker_backend oss_backend = {
+  BACKEND_OSS,
+  0,
+  oss_init,
+  oss_activate,
+  oss_play,
+  oss_deactivate,
+  oss_beforepoll,
+  oss_ready
+};
+
+#endif
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 75e9cd7..7f4578d 100644 (file)
@@ -380,6 +380,9 @@ static const struct speaker_backend *backends[] = {
 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
   &coreaudio_backend,
 #endif
+#if HAVE_SYS_SOUNDCARD_H
+  &oss_backend,
+#endif
   0
 };
 
index 4e22d38..061da0b 100644 (file)
@@ -213,6 +213,7 @@ extern const struct speaker_backend network_backend;
 extern const struct speaker_backend alsa_backend;
 extern const struct speaker_backend command_backend;
 extern const struct speaker_backend coreaudio_backend;
+extern const struct speaker_backend oss_backend;
 
 extern struct pollfd fds[NFDS];
 extern int fdno;