chiark / gitweb /
stop disorder(1) permuting command line
[disorder] / clients / playrtp-oss.c
index a59e749..15f6b53 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * This file is part of DisOrder.
  * Copyright (C) 2007 Richard Kettlewell
+ * Portions copyright (C) 2007 Ross Younger
  *
  * 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
  * USA
  */
 /** @file clients/playrtp-oss.c
- * @brief RTP player - OSS support
+ * @brief RTP player - OSS and empeg support
  */
 
 #include <config.h>
 
-#if HAVE_SYS_SOUNDCARD_H
+#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
 #include "types.h"
 
 #include <poll.h>
 #include <sys/ioctl.h>
+#if !EMPEG_HOST
 #include <sys/soundcard.h>
+#endif
 #include <assert.h>
 #include <pthread.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <arpa/inet.h>
 
 #include "mem.h"
 #include "log.h"
 /** @brief /dev/dsp (or whatever) */
 static int playrtp_oss_fd = -1;
 
+/** @brief Audio buffer */
+static char *playrtp_oss_buffer;
+
+/** @brief Size of @ref playrtp_oss_buffer in bytes */
+static int playrtp_oss_bufsize;
+
+/** @brief Number of bytes used in @ref playrtp_oss_buffer */
+static int playrtp_oss_bufused;
+
 /** @brief Open and configure the OSS audio device */
 static void playrtp_oss_enable(void) {
   if(playrtp_oss_fd == -1) {
+#if EMPEG_HOST
+    /* empeg audio driver only knows /dev/audio, only supports the equivalent
+     * of AFMT_S16_NE, has a fixed buffer size, and does not support the
+     * SNDCTL_ ioctls. */
+    if(!device)
+      device = "/dev/audio";
+    if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
+      fatal(errno, "error opening %s", device);
+    playrtp_oss_bufsize = 4608;
+#else
     int rate = 44100, stereo = 1, format = AFMT_S16_BE;
     if(!device) {
       if(access("/dev/dsp", W_OK) == 0)
@@ -68,10 +91,69 @@ static void playrtp_oss_enable(void) {
       fatal(errno, "ioctl SNDCTL_DSP_SPEED");
     if(rate != 44100)
       error(0, "asking for 44100Hz, got %dHz", rate);
+    if(ioctl(playrtp_oss_fd, SNDCTL_DSP_GETBLKSIZE, &playrtp_oss_bufsize) < 0)
+      fatal(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
+    info("OSS buffer size %d", playrtp_oss_bufsize);
+#endif
+    playrtp_oss_buffer = xmalloc(playrtp_oss_bufsize);
+    playrtp_oss_bufused = 0;
     nonblock(playrtp_oss_fd);
   }
 }
 
+/** @brief Flush the OSS output buffer
+ * @return 0 on success, non-0 on error
+ */
+static int playrtp_oss_flush(void) {
+  int nbyteswritten;
+
+  if(!playrtp_oss_bufused)
+    return 0;                           /* nothing to do */
+  /* 0 out the unused portion of the buffer */
+  memset(playrtp_oss_buffer + playrtp_oss_bufused, 0,
+         playrtp_oss_bufsize - playrtp_oss_bufused);
+#if EMPEG_HOST 
+  /* empeg audio driver insists on native-endian samples */
+  {
+    uint16_t *ptr,
+      *const limit = (uint16_t *)(playrtp_oss_buffer + playrtp_oss_bufused);
+
+    for(ptr = (uint16_t *)playrtp_oss_buffer; ptr < limit; ++ptr)
+      *ptr = ntohs(*ptr);
+  }
+#endif
+  for(;;) {
+    nbyteswritten = write(playrtp_oss_fd,
+                          playrtp_oss_buffer, playrtp_oss_bufsize);
+    if(nbyteswritten < 0) {
+      switch(errno) {
+      case EINTR:
+        break;                          /* try again */
+      case EAGAIN:
+        return 0;                       /* try later */
+      default:
+        error(errno, "error writing to %s", device);
+        return -1;
+      }
+    } else {
+      if(nbyteswritten < playrtp_oss_bufsize)
+        error(0, "%s: short write (%d/%d)",
+              device, nbyteswritten, playrtp_oss_bufsize);
+      if(dump_buffer) {
+        int count;
+        const int16_t *sp = (const int16_t *)playrtp_oss_buffer;
+        
+        for(count = 0; count < playrtp_oss_bufsize; count += sizeof(int16_t)) {
+          dump_buffer[dump_index++] = (int16_t)ntohs(*sp++);
+          dump_index %= dump_size;
+        }
+      }
+      playrtp_oss_bufused = 0;
+      return 0;
+    }
+  }
+}
+
 /** @brief Wait until the audio device can accept more data */
 static void playrtp_oss_wait(void) {
   struct pollfd fds[1];
@@ -91,35 +173,42 @@ static void playrtp_oss_wait(void) {
  * @param hard If nonzero, drop pending data
  */
 static void playrtp_oss_disable(int hard) {
-  if(hard)
+  if(hard) {
+#if !EMPEG_HOST
+    /* No SNDCTL_DSP_ ioctls on empeg */
     if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
       error(errno, "ioctl SNDCTL_DSP_RESET");
+#endif
+  } else
+    playrtp_oss_flush();
   xclose(playrtp_oss_fd);
   playrtp_oss_fd = -1;
+  free(playrtp_oss_buffer);
+  playrtp_oss_buffer = 0;
 }
 
 /** @brief Write samples to OSS output device
  * @param data Pointer to sample data
- * @param nsamples Number of samples
+ * @param samples Number of samples
  * @return 0 on success, non-0 on error
  */
-static int playrtp_oss_write(const void *data, size_t samples) {
-  const ssize_t nbyteswritten = write(playrtp_oss_fd, data,
-                                     samples * sizeof (int16_t));
-
-  if(nbyteswritten < 0) {
-    switch(errno) {
-    case EAGAIN:
-    case EINTR:
-      return 0;
-    default:
-      error(errno, "error writing to %s", device);
-      return -1;
-    }
-  } else {
-    next_timestamp += nbyteswritten / 2;
-    return 0;
+static int playrtp_oss_write(const char *data, size_t samples) {
+  long bytes = samples * sizeof(int16_t);
+  while(bytes > 0) {
+    int n = playrtp_oss_bufsize - playrtp_oss_bufused;
+
+    if(n > bytes)
+      n = bytes;
+    memcpy(playrtp_oss_buffer + playrtp_oss_bufused, data, n);
+    bytes -= n;
+    data += n;
+    playrtp_oss_bufused += n;
+    if(playrtp_oss_bufused == playrtp_oss_bufsize)
+      if(playrtp_oss_flush())
+        return -1;
   }
+  next_timestamp += samples;
+  return 0;
 }
 
 /** @brief Play some data from packet @p p
@@ -127,8 +216,9 @@ static int playrtp_oss_write(const void *data, size_t samples) {
  * @p p is assumed to contain @ref next_timestamp.
  */
 static int playrtp_oss_play(const struct packet *p) {
-  return playrtp_oss_write(p->samples_raw + next_timestamp - p->timestamp,
-                          (p->timestamp + p->nsamples) - next_timestamp);
+  return playrtp_oss_write
+    ((const char *)(p->samples_raw + next_timestamp - p->timestamp),
+     (p->timestamp + p->nsamples) - next_timestamp);
 }
 
 /** @brief Play some silence before packet @p p
@@ -136,7 +226,7 @@ static int playrtp_oss_play(const struct packet *p) {
  * @p p is assumed to be entirely before @ref next_timestamp.
  */
 static int playrtp_oss_infill(const struct packet *p) {
-  static const uint16_t zeros[INFILL_SAMPLES];
+  static const char zeros[INFILL_SAMPLES * sizeof(int16_t)];
   size_t samples_available = INFILL_SAMPLES;
 
   if(p && samples_available > p->timestamp - next_timestamp)