chiark / gitweb /
make sure tests is last directory
[disorder] / server / speaker.c
index d4714bd22bec0d128c7073f7db59ef277311a3e6..be783afd7c8958f43909720e59af1f3f6990ba81 100644 (file)
@@ -42,6 +42,8 @@
 #include <sys/select.h>
 #include <sys/wait.h>
 #include <time.h>
+#include <fcntl.h>
+#include <poll.h>
 
 #include "configuration.h"
 #include "syscalls.h"
 #include "speaker.h"
 #include "user.h"
 
-#if BUILD_SPEAKER
+#if API_ALSA
 #include <alsa/asoundlib.h>
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define MACHINE_AO_FMT AO_FMT_BIG
+#else
+# define MACHINE_AO_FMT AO_FMT_LITTLE
+#endif
 
 #define BUFFER_SECONDS 5                /* How many seconds of input to
                                          * buffer. */
@@ -79,13 +88,15 @@ static struct track {
 
 static time_t last_report;              /* when we last reported */
 static int paused;                      /* pause status */
-static snd_pcm_t *pcm;                  /* current pcm handle */
 static ao_sample_format pcm_format;     /* current format if aodev != 0 */
 static size_t bpf;                      /* bytes per frame */
 static struct pollfd fds[NFDS];         /* if we need more than that */
 static int fdno;                        /* fd number */
-static snd_pcm_uframes_t pcm_bufsize;   /* buffer size */
+static size_t bufsize;                  /* buffer size */
+#if API_ALSA
+static snd_pcm_t *pcm;                  /* current pcm handle */
 static snd_pcm_uframes_t last_pcm_bufsize; /* last seen buffer size */
+#endif
 static int ready;                       /* ready to send audio */
 static int forceplay;                   /* frames to force play */
 static int kidfd = -1;                  /* child process input */
@@ -234,10 +245,11 @@ static int formats_equal(const ao_sample_format *a,
 
 /* Close the sound device. */
 static void idle(void) {
-  int  err;
-
   D(("idle"));
-  if(pcm) {
+#if API_ALSA
+  if(!config->speaker_command && pcm) {
+    int  err;
+
     if((err = snd_pcm_nonblock(pcm, 0)) < 0)
       fatal(0, "error calling snd_pcm_nonblock: %d", err);
     D(("draining pcm"));
@@ -248,6 +260,7 @@ static void idle(void) {
     forceplay = 0;
     D(("released audio device"));
   }
+#endif
   ready = 0;
 }
 
@@ -266,6 +279,7 @@ static void abandon(void) {
   forceplay = 0;
 }
 
+#if API_ALSA
 static void log_params(snd_pcm_hw_params_t *hwparams,
                        snd_pcm_sw_params_t *swparams) {
   snd_pcm_uframes_t f;
@@ -290,32 +304,46 @@ static void log_params(snd_pcm_hw_params_t *hwparams,
     info("sw xfer_align=%lu", (unsigned long)f);
   }
 }
+#endif
 
-static void soxargs(const char ***pp, char **qq, ao_sample_format *ao)
-{
+static void soxargs(const char ***pp, char **qq, ao_sample_format *ao) {
   int n;
 
   *(*pp)++ = "-t.raw";
   *(*pp)++ = "-s";
   *(*pp)++ = *qq; n = sprintf(*qq, "-r%d", ao->rate); *qq += n + 1;
-  switch(ao->byte_format) {
+  *(*pp)++ = *qq; n = sprintf(*qq, "-c%d", ao->channels); *qq += n + 1;
+  /* sox 12.17.9 insists on -b etc; CVS sox insists on -<n> etc; both are
+   * deployed! */
+  switch(config->sox_generation) {
+  case 0:
+    if(ao->bits != 8
+       && ao->byte_format != AO_FMT_NATIVE
+       && ao->byte_format != MACHINE_AO_FMT) {
+      *(*pp)++ = "-x";
+    }
+    switch(ao->bits) {
+    case 8: *(*pp)++ = "-b"; break;
+    case 16: *(*pp)++ = "-w"; break;
+    case 32: *(*pp)++ = "-l"; break;
+    case 64: *(*pp)++ = "-d"; break;
+    default: fatal(0, "cannot handle sample size %d", (int)ao->bits);
+    }
+    break;
+  case 1:
+    switch(ao->byte_format) {
     case AO_FMT_NATIVE: break;
-    case AO_FMT_BIG: *(*pp)++ = "-B";
-    case AO_FMT_LITTLE: *(*pp)++ = "-L";
+    case AO_FMT_BIG: *(*pp)++ = "-B"; break;
+    case AO_FMT_LITTLE: *(*pp)++ = "-L"; break;
+    }
+    *(*pp)++ = *qq; n = sprintf(*qq, "-%d", ao->bits/8); *qq += n + 1;
+    break;
   }
-  *(*pp)++ = *qq; n = sprintf(*qq, "-%d", ao->bits/8); *qq += n + 1;
-  *(*pp)++ = *qq; n = sprintf(*qq, "-c%d", ao->channels); *qq += n + 1;
 }
 
 /* Make sure the sound device is open and has the right sample format.  Return
  * 0 on success and -1 on error. */
 static int activate(void) {
-  int err;
-  snd_pcm_hw_params_t *hwparams;
-  snd_pcm_sw_params_t *swparams;
-  int sample_format = 0;
-  unsigned rate;
-
   /* If we don't know the format yet we cannot start. */
   if(!playing->got_format) {
     D((" - not got format for %s", playing->id));
@@ -359,17 +387,27 @@ static int activate(void) {
     }
     if(!ready) {
       pcm_format = config->sample_format;
-      pcm_bufsize = 3 * FRAMES;
+      bufsize = 3 * FRAMES;
       bpf = bytes_per_frame(&config->sample_format);
       D(("acquired audio device"));
       ready = 1;
     }
     return 0;
   }
+  if(config->speaker_command)
+    return -1;
+#if API_ALSA
   /* If we need to change format then close the current device. */
   if(pcm && !formats_equal(&playing->format, &pcm_format))
      idle();
   if(!pcm) {
+    snd_pcm_hw_params_t *hwparams;
+    snd_pcm_sw_params_t *swparams;
+    snd_pcm_uframes_t pcm_bufsize;
+    int err;
+    int sample_format = 0;
+    unsigned rate;
+
     D(("snd_pcm_open"));
     if((err = snd_pcm_open(&pcm,
                            config->device,
@@ -422,7 +460,8 @@ static int activate(void) {
             playing->format.channels, err);
       goto fatal;
     }
-    pcm_bufsize = 3 * FRAMES;
+    bufsize = 3 * FRAMES;
+    pcm_bufsize = bufsize;
     if((err = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams,
                                                      &pcm_bufsize)) < 0)
       fatal(0, "error from snd_pcm_hw_params_set_buffer_size (%d): %d",
@@ -457,6 +496,7 @@ error:
     snd_pcm_close(pcm);
     pcm = 0;
   }
+#endif
   return -1;
 }
 
@@ -488,10 +528,8 @@ static void fork_kid(void) {
 }
 
 static void play(size_t frames) {
-  snd_pcm_sframes_t written_frames;
-  size_t avail_bytes, avail_frames;
+  size_t avail_bytes, written_frames;
   ssize_t written_bytes;
-  int err;
 
   if(activate()) {
     if(playing)
@@ -519,19 +557,24 @@ static void play(size_t frames) {
   else
     avail_bytes = playing->used;
 
-  if(kidfd == -1) {
+  if(!config->speaker_command) {
+#if API_ALSA
+    snd_pcm_sframes_t pcm_written_frames;
+    size_t avail_frames;
+    int err;
+
     avail_frames = avail_bytes / bpf;
     if(avail_frames > frames)
       avail_frames = frames;
     if(!avail_frames)
       return;
-    written_frames = snd_pcm_writei(pcm,
-                                    playing->buffer + playing->start,
-                                    avail_frames);
+    pcm_written_frames = snd_pcm_writei(pcm,
+                                        playing->buffer + playing->start,
+                                        avail_frames);
     D(("actually play %zu frames, wrote %d",
-       avail_frames, (int)written_frames));
-    if(written_frames < 0) {
-      switch(written_frames) {
+       avail_frames, (int)pcm_written_frames));
+    if(pcm_written_frames < 0) {
+      switch(pcm_written_frames) {
         case -EPIPE:                        /* underrun */
           error(0, "snd_pcm_writei reports underrun");
           if((err = snd_pcm_prepare(pcm)) < 0)
@@ -540,10 +583,15 @@ static void play(size_t frames) {
         case -EAGAIN:
           return;
         default:
-          fatal(0, "error calling snd_pcm_writei: %d", (int)written_frames);
+          fatal(0, "error calling snd_pcm_writei: %d",
+                (int)pcm_written_frames);
       }
     }
+    written_frames = pcm_written_frames;
     written_bytes = written_frames * bpf;
+#else
+    assert(!"reached");
+#endif
   } else {
     if(avail_bytes > frames * bpf)
       avail_bytes = frames * bpf;
@@ -607,13 +655,14 @@ static int addfd(int fd, int events) {
 }
 
 int main(int argc, char **argv) {
-  int n, fd, stdin_slot, alsa_slots, alsa_nslots = -1, kid_slot, err;
-  unsigned short alsa_revents;
+  int n, fd, stdin_slot, alsa_slots, kid_slot;
   struct track *t;
   struct speaker_message sm;
+#if API_ALSA
+  int alsa_nslots = -1, err;
+#endif
 
   set_progname(argv);
-  mem_init(0);
   if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "hVc:dD", options, 0)) >= 0) {
     switch(n) {
@@ -643,7 +692,15 @@ int main(int argc, char **argv) {
   /* make sure we're not root, whatever the config says */
   if(getuid() == 0 || geteuid() == 0) fatal(0, "do not run as root");
   info("started");
-  if(config->speaker_command) fork_kid();
+  if(config->speaker_command)
+    fork_kid();
+  else {
+#if API_ALSA
+    /* ok */
+#else
+    fatal(0, "invoked speaker but no speaker_command and no known sound API");
+ #endif
+  }
   while(getppid() != 1) {
     fdno = 0;
     /* Always ready for commands from the main server. */
@@ -658,10 +715,12 @@ int main(int argc, char **argv) {
      * sound device. */
     alsa_slots = -1;
     kid_slot = -1;
-    if(pcm && !forceplay) {
-      if(kidfd >= 0)
-        kid_slot = addfd(kidfd, POLLOUT);
-      else {
+    if(ready && !forceplay) {
+      if(config->speaker_command) {
+        if(kidfd >= 0)
+          kid_slot = addfd(kidfd, POLLOUT);
+      } else {
+#if API_ALSA
         int retry = 3;
         
         alsa_slots = fdno;
@@ -679,6 +738,7 @@ int main(int argc, char **argv) {
         } while(retry-- > 0);
         if(alsa_nslots >= 0)
           fdno += alsa_nslots;
+#endif
       }
     }
     /* If any other tracks don't have a full buffer, try to read sample data
@@ -698,6 +758,9 @@ int main(int argc, char **argv) {
     }
     /* Play some sound before doing anything else */
     if(alsa_slots != -1) {
+#if API_ALSA
+      unsigned short alsa_revents;
+      
       if((err = snd_pcm_poll_descriptors_revents(pcm,
                                                  &fds[alsa_slots],
                                                  alsa_nslots,
@@ -705,6 +768,7 @@ int main(int argc, char **argv) {
         fatal(0, "error calling snd_pcm_poll_descriptors_revents: %d", err);
       if(alsa_revents & (POLLOUT | POLLERR))
         play(3 * FRAMES);
+#endif
     } else if(kid_slot != -1) {
       if(fds[kid_slot].revents & (POLLOUT | POLLERR))
         play(3 * FRAMES);
@@ -732,7 +796,7 @@ int main(int argc, char **argv) {
          t = findtrack(sm.id, 1);
           if(fd != -1) acquire(t, fd);
           playing = t;
-          play(pcm_bufsize);
+          play(bufsize);
           report();
          break;
        case SM_PAUSE:
@@ -745,7 +809,7 @@ int main(int argc, char **argv) {
           if(paused) {
             paused = 0;
             if(playing)
-              play(pcm_bufsize);
+              play(bufsize);
           }
           report();
          break;
@@ -793,13 +857,6 @@ int main(int argc, char **argv) {
   info("stopped (parent terminated)");
   exit(0);
 }
-#else
-int main(int attribute((unused)) argc, char **argv) {
-  set_progname(argv);
-  mem_init(0);
-  fatal(0, "disorder-speaker not supported on this platform");
-}
-#endif
 
 /*
 Local Variables: