[xiph-cvs] cvs commit: vorbis-tools/ogg123 Makefile.am ao_interface.c buffer.c buffer.h ogg123.c ogg123.h

Kenneth C. Arnold kcarnold at xiph.org
Fri Aug 10 09:33:42 PDT 2001



kcarnold    01/08/10 09:33:41

  Modified:    ogg123   Tag: kcarnold_work Makefile.am ao_interface.c
                        buffer.c buffer.h ogg123.c ogg123.h
  Log:
  A very nice (IMHO) pthreaded buffer. Many other fixups. Much better.
  
  Bang on this and try to find some deadlock cases.

Revision  Changes    Path
No                   revision

No                   revision

1.14.2.2  +1 -1      vorbis-tools/ogg123/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/Makefile.am,v
retrieving revision 1.14.2.1
retrieving revision 1.14.2.2
diff -u -r1.14.2.1 -r1.14.2.2
--- Makefile.am	2001/08/06 21:35:40	1.14.2.1
+++ Makefile.am	2001/08/10 16:33:40	1.14.2.2
@@ -13,7 +13,7 @@
 ogg123_LDADD = @VORBISFILE_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ @AO_LIBS@ \
                 @SOCKET_LIBS@
 
-ogg123_SOURCES = ogg123.c ao_interface.c buffer.c ogg123.h buffer.h getopt.c getopt1.c getopt.h
+ogg123_SOURCES = ogg123.c ao_interface.c buffer.c ogg123.h buffer.h getopt.c getopt1.c getopt.h ao_interface.h
 ## Comment the above and uncomment the next line to disable the buffer support
 ##ogg123_SOURCES = ogg123.c ao_interface.c nullbuffer.c ogg123.h buffer.h getopt.c getopt1.c getopt.h
 

1.5.2.2   +58 -3     vorbis-tools/ogg123/ao_interface.c

Index: ao_interface.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ao_interface.c,v
retrieving revision 1.5.2.1
retrieving revision 1.5.2.2
diff -u -r1.5.2.1 -r1.5.2.2
--- ao_interface.c	2001/08/06 21:35:40	1.5.2.1
+++ ao_interface.c	2001/08/10 16:33:40	1.5.2.2
@@ -1,3 +1,20 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+
+ last mod: $Id: ao_interface.c,v 1.5.2.2 2001/08/10 16:33:40 kcarnold Exp $
+
+ ********************************************************************/
+
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
@@ -26,12 +43,21 @@
     return devices_list;
 }
 
-void devices_write(void *ptr, size_t size, devices_t * d)
+size_t devices_write(void *ptr, size_t size, size_t nmemb, devices_t * d)
 {
+  size_t i, total = 0;
+  devices_t * start = d;
+  for (i=0; i < nmemb; i++) {
+    d = start;
     while (d != NULL) {
-	ao_play(d->device, ptr, size);
-	d = d->next_device;
+      int ret = ao_play(d->device, ptr, size);
+      if (ret < size)
+	return total + ret;
+      total += ret;
+      d = d->next_device;
     }
+  }
+  return total;
 }
 
 int add_option(ao_option ** op_h, const char *optstring)
@@ -97,4 +123,33 @@
       return ao_driver_id(device);
     
     return -1;
+}
+
+void close_audio_devices (devices_t *devices)
+{
+  devices_t *current = devices;
+  while (current != NULL) {
+    ao_close(current->device);
+    current = current->next_device;
+  }
+}
+
+void free_audio_devices (devices_t *devices)
+{
+  devices_t *current;
+  while (devices != NULL) {
+    current = devices->next_device;
+    free (devices);
+    devices = current;
+  }
+}
+
+void ao_atexit (int exitcode, void *arg)
+{
+  devices_t *devices = (devices_t *) arg;
+
+  close_audio_devices (devices);
+  free_audio_devices (devices);
+
+  ao_shutdown();
 }

1.7.2.6   +151 -203  vorbis-tools/ogg123/buffer.c

Index: buffer.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/buffer.c,v
retrieving revision 1.7.2.5
retrieving revision 1.7.2.6
diff -u -r1.7.2.5 -r1.7.2.6
--- buffer.c	2001/08/09 01:56:54	1.7.2.5
+++ buffer.c	2001/08/10 16:33:40	1.7.2.6
@@ -1,3 +1,20 @@
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+
+ last mod: $Id: buffer.c,v 1.7.2.6 2001/08/10 16:33:40 kcarnold Exp $
+
+ ********************************************************************/
+
 /* buffer.c
  *  buffering code for ogg123. This is Unix-specific. Other OSes anyone?
  *
@@ -32,263 +49,198 @@
 #define DEBUG1(x, y)
 #endif
 
-void signal_handler (int sig)
-{
-}
+#define LOCK_MUTEX(mutex) do { DEBUG1("Locking mutex %s.", #mutex); pthread_mutex_lock (&(mutex)); } while (0)
+#define UNLOCK_MUTEX(mutex) do { DEBUG1("Unlocking mutex %s", #mutex); pthread_mutex_unlock(&(mutex)); } while (0) 
 
-/* Initialize the buffer structure. */
-void buffer_init (buf_t *buf, long size, long prebuffer)
+void Prebuffer (buf_t * buf)
 {
-  DEBUG0("buffer init");
-  memset (buf, 0, sizeof(*buf));
-  buf->status = 0;
-  buf->reader = buf->writer = buf->buffer;
-  buf->end = buf->buffer + (size - 1);
-  buf->size = size;
-  buf->prebuffer = prebuffer;
-  buf->curfill = 0;
-  if (prebuffer > 0)
-    buf->status |= STAT_PREBUFFER;
+  if (buf->prebuffer > 0)
+    {
+      LOCK_MUTEX (buf->StatMutex);
+      buf->StatMask |= STAT_PREBUFFERING;
+      UNLOCK_MUTEX (buf->StatMutex);
+    }
 }
 
-/* Main write loop. No semaphores. No deadlock. No problem. I hope. */
-void writer_main (volatile buf_t *buf, devices_t *d)
+void PthreadCleanup (void *arg)
 {
-  devices_t *d1;
-  signal (SIGINT, SIG_IGN);
-  signal (SIGUSR1, signal_handler);
-
-  DEBUG0("r: writer_main");
-  while (! (buf->status & STAT_SHUTDOWN && buf->curfill == 0))
-    {
-      /* Writer just waits on reader to be done with submit_chunk
-       * Reader must ensure that we don't deadlock. */
-
-      /* prebuffering */
-    prebuffer:
-      while (buf->status & STAT_PREBUFFER)
-	pause();
-
-      if (buf->curfill == 0) {
-	if (! (buf->status & STAT_FLUSH)) /* likely unnecessary */
-	  buf->status |= STAT_UNDERFLOW;
-	DEBUG0("alerting writer");
-	kill (buf->readerpid, SIGUSR1);
-      }
-
-      if (buf->status & STAT_FLUSH) {
-      flush:
-	DEBUG0("r: buffer flush");
-	buf->curfill = 0;
-	buf->reader = buf->writer;
-	buf->status &= ~STAT_FLUSH;
-	DEBUG1("buf->status = %d", buf->status);
-	kill (buf->readerpid, SIGUSR1);
+  buf_t *buf = (buf_t*) arg;
+  
+  DEBUG0("PthreadCleanup");
+  UNLOCK_MUTEX (buf->SizeMutex);
+  UNLOCK_MUTEX (buf->StatMutex);
+
+  /* kludge to get around pthreads vs. signal handling */
+  pthread_cond_broadcast (&buf->DataReadyCondition);
+  pthread_cond_broadcast (&buf->UnderflowCondition);
+  pthread_cond_broadcast (&buf->OverflowCondition);
+  UNLOCK_MUTEX (buf->SizeMutex);
+  UNLOCK_MUTEX (buf->StatMutex);
+}
+
+void* BufferFunc (void *arg)
+{
+  sigset_t set;
+  buf_t *buf = (buf_t*) arg;
+  volatile buf_t *vbuf = (volatile buf_t*) buf; /* optimizers... grr */
+
+  DEBUG0("r: BufferFunc");
+  sigfillset (&set);
+  pthread_sigmask (SIG_SETMASK, &set, NULL);
+
+  pthread_cleanup_push (PthreadCleanup, buf);
+  while (1)
+    {
+      /* don't touch the size unless we ask you to. */
+      LOCK_MUTEX (buf->SizeMutex);
+
+      /* paused? Remember to signal DataReady when unpaused. */
+    checkPlaying:
+      while (!(buf->StatMask & STAT_PLAYING) ||
+	     (buf->StatMask & STAT_PREBUFFERING)) {
+	DEBUG1 ("r: waiting on !playing || prebuffering (stat=%d)", buf->StatMask);
+	pthread_cond_wait (&buf->DataReadyCondition, &buf->SizeMutex);
       }
 
-      if (buf->curfill == 0 && !(buf->status & STAT_SHUTDOWN)
-	  && !(buf->status & STAT_FLUSH)) {
-	buf->status |= STAT_PREBUFFER;
-	goto prebuffer;
+      if (buf->curfill == 0) {
+	UNLOCK_MUTEX (buf->SizeMutex);
+	DEBUG0 ("r: signalling buffer underflow");
+	pthread_cond_signal (&buf->UnderflowCondition);
+	LOCK_MUTEX (buf->SizeMutex);
+	Prebuffer (buf);
+	DEBUG0 ("r: waiting on data ready");
+	pthread_cond_wait (&buf->DataReadyCondition, &buf->SizeMutex);
+	goto checkPlaying;
       }
-	/*	DEBUG1("looping on buffer underflow, status is %d", buf->status);*/
-
-      if (buf->status & STAT_FLUSH)
-	goto flush;
 
-      if (buf->reader == buf->writer)
-	break;
+      /* unlock while playing sample */
+      UNLOCK_MUTEX (buf->SizeMutex);
 
-      /* devices_write (buf->writer->data, buf->writer->len, d); */
       DEBUG0("writing chunk");
-      {
-	d1 = d;
-	while (d1 != NULL) {
-	  ao_play(d1->device, buf->writer->data, buf->writer->len);
-	  d1 = d1->next_device;
-	}
-      }
+      buf->write_func (buf->writer->data, buf->writer->len, 1, buf->data);
 
       DEBUG0("incrementing pointer");
-      if (buf->writer == buf->end)
-	buf->writer = buf->buffer;
+      LOCK_MUTEX (buf->SizeMutex);
+      if (vbuf->writer == vbuf->end)
+	vbuf->writer = vbuf->buffer;
       else
-	buf->writer++;
-      buf->curfill--;
+	vbuf->writer++;
+      vbuf->curfill--;
+      UNLOCK_MUTEX (buf->SizeMutex);
+
+      /* slight abuse of the DataReady condition, but makes sense. */
+      DEBUG0 ("r: signalling buffer no longer full");
       if (buf->curfill + 1 == buf->size)
-	kill (buf->readerpid, SIGUSR1);
+	pthread_cond_signal (&buf->DataReadyCondition);
    }
-  DEBUG0("r: shutting down buffer");
-  buf->status = 0;
-  write (buf->fds[1], "2", sizeof(int));
-  kill (buf->writerpid, SIGUSR1);
+  /* should never get here */
+  pthread_cleanup_pop(1);
   DEBUG0("r: exiting");
-  _exit(0);
 }
 
-/* fork_writer is called to create the writer process. This creates
- * the shared memory segment of 'size' chunks, and returns a pointer
- * to the buffer structure that is shared. Just pass this straight to
- * submit_chunk and all will be happy. */
-
-buf_t *fork_writer (long size, devices_t *d, long prebuffer)
+buf_t *StartBuffer (long size, long prebuffer, void *data, 
+		    size_t (*write_func) (void *, size_t, size_t, void *))
 {
-  int childpid;
-  buf_t *buf;
-
-#if HAVE_SMMAP
-  int fd;
+  buf_t *buf = malloc (sizeof(buf_t) + sizeof (chunk_t) * (size - 1));
 
-  if ((fd = open("/dev/zero", O_RDWR)) < 0)
-    {
-      perror ("cannot open /dev/zero");
-      exit (1);
-    }
-  if ((buf = (buf_t *) mmap (0, sizeof(buf_t) + sizeof (chunk_t) * (size - 1),
-                             PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
-    {
-      perror("mmap");
-      exit(1);
-    }
-  close(fd);
-#else
-  /* Get the shared memory segment. */
-  int shmid = shmget (IPC_PRIVATE,
-			  sizeof(buf_t) + sizeof (chunk_t) * (size - 1),
-			  IPC_CREAT|SHM_R|SHM_W);
-
-  if (shmid == -1)
-    {
-      perror ("shmget");
-      exit (1);
-    }
-  
-  /* Attach the segment to us (and our kids). Get and store the pointer. */
-  buf = (buf_t *) shmat (shmid, 0, 0);
-  
   if (buf == NULL)
     {
-      perror ("shmat");
+      perror ("malloc");
       exit (1);
     }
 
-  /* Remove segment after last process detaches it or terminates. */
-  shmctl(shmid, IPC_RMID, 0);
-#endif /* HAVE_SMMAP */
+  /* we no longer need those hacked-up shared memory things! yippee! */
 
 #ifdef DEBUG_BUFFER
-  debugfile = fopen ("/tmp/bufferdebug", "w"); /* file can be a pipe */
+  debugfile = fopen ("/tmp/bufferdebug", "w");
   setvbuf (debugfile, NULL, _IONBF, 0);
 #endif
-
-  buffer_init (buf, size, prebuffer);
-  
-  /* Create a pipe for communication between the two processes. Unlike
-   * the first incarnation of an ogg123 buffer, the data is not transferred
-   * over this channel, only occasional "WAKE UP!"'s. */
-
-  if (pipe (buf->fds))
-    {
-      perror ("pipe");
-      exit (1);
-    }
 
-  /* write should never block; read should always block. */
-  fcntl (buf->fds[1], F_SETFL, O_NONBLOCK);
-
-  fflush (stdout);
+  /* Initialize the buffer structure. */
+  DEBUG0("buffer init");
+  memset (buf, 0, sizeof(*buf));
 
-  signal (SIGUSR1, signal_handler);
+  buf->data = data;
+  buf->write_func = write_func;
 
-  buf->readerpid = getpid();
+  buf->reader = buf->writer = buf->buffer;
+  buf->end = buf->buffer + (size - 1);
+  buf->size = size;
+  buf->prebuffer = prebuffer;
+  Prebuffer (buf);
+  buf->StatMask |= STAT_PLAYING;
 
-  childpid = fork();
+  /* pthreads initialization */
+  pthread_mutex_init (&buf->SizeMutex, NULL);
+  pthread_cond_init (&buf->UnderflowCondition, NULL);
+  pthread_cond_init (&buf->OverflowCondition, NULL);
   
-  if (childpid == -1)
-    {
-      perror ("fork");
-      exit (1);
-    }
+  pthread_create(&buf->BufferThread, NULL, BufferFunc, buf);
 
-  if (childpid == 0)
-    {
-      buf->writerpid = getpid();
-      writer_main (buf, d);
-      return NULL;
-    }
-  else {
-    buf->writerpid = childpid;
-    return buf;
-  }
+  return buf;
 }
 
 void submit_chunk (buf_t *buf, chunk_t chunk)
 {
-  struct timeval tv;
-  static fd_set set;
-
-  FD_ZERO(&set);
-  FD_SET(buf->fds[0], &set);
-
   DEBUG0("submit_chunk");
-  /* Wait wait, don't step on my sample! */
-  while (buf->curfill == buf->size)
-    {
-      /* buffer overflow (yikes! no actually it's a GOOD thing) */
-      int ret;
-      char t;
-      
-      DEBUG0("w: looping on buffer overflow");
-      tv.tv_sec = 1;
-      tv.tv_usec = 0;
-      ret = select (buf->fds[0]+1, &set, NULL, NULL, &tv);
-      
-      DEBUG1("w: select returned %d", ret);
-      if (ret > 0)
-	read (buf->fds[0], &t, sizeof(int));
-    }
-	      
+  LOCK_MUTEX (buf->SizeMutex);
+  /* wait on buffer overflow */
+  while (buf->curfill == buf->size && buf->StatMask & STAT_PLAYING)
+    pthread_cond_wait (&buf->DataReadyCondition, &buf->SizeMutex);
+
   DEBUG0("writing chunk");
   *(buf->reader) = chunk;
-  /* do this atomically */
   if (buf->reader == buf->end)
     buf->reader = buf->buffer;
   else
     buf->reader++;
   buf->curfill++;
 
-  if (buf->status & STAT_PREBUFFER && buffer_full(buf) >= buf->prebuffer) {
-    buf->status &= ~STAT_PREBUFFER;
-    kill (buf->writerpid, SIGUSR1);
-  }
-  else if (buf->status & STAT_UNDERFLOW) {
-    buf->status |= STAT_PREBUFFER;
-    buf->status &= ~STAT_UNDERFLOW;
-    kill (buf->writerpid, SIGUSR1);
+  UNLOCK_MUTEX (buf->SizeMutex);
+
+  if ((buf->StatMask & STAT_PREBUFFERING)
+      && buffer_full(buf) >= buf->prebuffer) {
+    DEBUG0("prebuffering done, starting writer");
+    LOCK_MUTEX (buf->StatMutex);
+    buf->StatMask &= ~STAT_PREBUFFERING;
+    UNLOCK_MUTEX (buf->StatMutex);
+    pthread_cond_signal (&buf->DataReadyCondition);
   }
+  else if (buf->curfill == 1)
+    pthread_cond_signal (&buf->DataReadyCondition);
+
   DEBUG0("submit_chunk exit");
 }
 
 void buffer_flush (buf_t *buf)
 {
   DEBUG0("flush buffer");
-  buf->status |= STAT_FLUSH;
+  LOCK_MUTEX (buf->SizeMutex);
+  buf->curfill = 0;
+  buf->reader = buf->writer;
+  UNLOCK_MUTEX (buf->SizeMutex);
+  Prebuffer (buf);
 }
 
+void buffer_WaitForEmpty (buf_t *buf)
+{
+  DEBUG0("waiting for empty");
+  LOCK_MUTEX (buf->SizeMutex);
+  while (buf->curfill > 0)
+    pthread_cond_wait (&buf->UnderflowCondition, &buf->SizeMutex);
+  UNLOCK_MUTEX (buf->SizeMutex);
+  Prebuffer (buf);
+}
+
 void buffer_shutdown (buf_t *buf)
 {
   DEBUG0("shutdown buffer");
-  buf->status |= STAT_SHUTDOWN;
-  buf->status &= ~STAT_PREBUFFER;
-  while (buf->status != 0)
-    {
-      DEBUG0("waiting on reader to quit");
-      pause();
-    } 
-#ifndef HAVE_SMMAP
-  /* Deallocate the shared memory segment. */
-  shmdt(buf);
-#endif /* HAVE_SMMAP */
+  if (buf && buf->BufferThread) {
+    buffer_WaitForEmpty (buf);
+    pthread_cancel (buf->BufferThread);
+    pthread_join (buf->BufferThread, NULL);
+    buf->BufferThread = 0;
+  }
   DEBUG0("buffer done.");
 }
 
@@ -298,12 +250,8 @@
 
 void buffer_cleanup (buf_t *buf) {
   if (buf) {
-    if (buf->writerpid)
-      kill (buf->writerpid, SIGTERM);
-    wait (0);
-    buf->writerpid = 0;
-#ifndef HAVE_SMMAP
-    shmdt(buf);
-#endif
+    buffer_shutdown (buf);
+    PthreadCleanup (buf);
+    free (buf);
   }
 }

1.2.2.6   +40 -13    vorbis-tools/ogg123/buffer.h

Index: buffer.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/buffer.h,v
retrieving revision 1.2.2.5
retrieving revision 1.2.2.6
diff -u -r1.2.2.5 -r1.2.2.6
--- buffer.h	2001/08/09 01:56:55	1.2.2.5
+++ buffer.h	2001/08/10 16:33:40	1.2.2.6
@@ -1,10 +1,26 @@
-/* Common things between reader and writer threads */
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+ 
+ last mod: $Id: buffer.h,v 1.2.2.6 2001/08/10 16:33:40 kcarnold Exp $
+ 
+********************************************************************/
 
+/* A (relatively) generic circular buffer interface */
+
 #ifndef __BUFFER_H
 #define __BUFFER_H
 
-#include "ogg123.h"
-#include <sys/types.h>
+#include <pthread.h>
 
 /* 4096 is the chunk size we request from libvorbis. */
 #define BUFFER_CHUNK_SIZE 4096
@@ -17,30 +33,41 @@
 
 typedef struct buf_s
 {
-  char status;       /* Status. See STAT_* below. */
-  int fds[2];        /* Pipe file descriptors. */
+  /* generic buffer interface */
+  void * data;
+  size_t (*write_func) (void *ptr, size_t size, size_t nmemb, void * d);
+  
+  /* pthreads variables */
+  pthread_t BufferThread;
+  pthread_mutex_t SizeMutex;
+  pthread_mutex_t StatMutex;
+  pthread_cond_t UnderflowCondition; /* signalled on buffer underflow */
+  pthread_cond_t OverflowCondition;  /* signalled on buffer overflow */
+  pthread_cond_t DataReadyCondition; /* signalled when data is ready and it wasn't before */
+  
+  /* the buffer itself */
+  char StatMask;
   long size;         /* buffer size, for reference */
   long curfill;      /* how much the buffer is currently filled */
   long prebuffer;    /* number of chunks to prebuffer */
-  pid_t readerpid;   /* PID of reader process */
-  pid_t writerpid;   /* PID of writer process */
   chunk_t *reader;   /* Chunk the reader is busy with */
   chunk_t *writer;   /* Chunk the writer is busy with */
   chunk_t *end;      /* Last chunk in the buffer (for convenience) */
   chunk_t buffer[1]; /* The buffer itself. It's more than one chunk. */
 } buf_t;
+
+#define STAT_PREBUFFERING 1
+#define STAT_PLAYING 2
+#define STAT_EMPTYING 4
 
-buf_t *fork_writer (long size, devices_t *d, long prebuffer);
+buf_t *StartBuffer (long size, long prebuffer, void *data, 
+		    size_t (*write_func) (void *, size_t, size_t, void *));
 void submit_chunk (buf_t *buf, chunk_t chunk);
 void buffer_shutdown (buf_t *buf);
 void buffer_cleanup (buf_t *buf);
 void buffer_flush (buf_t *buf);
+void buffer_WaitForEmpty (buf_t *buf);
 long buffer_full (buf_t *buf);
-
-#define STAT_FLUSH 1
-#define STAT_SHUTDOWN 2
-#define STAT_PREBUFFER 4
-#define STAT_UNDERFLOW 8
 
 #endif /* !defined (__BUFFER_H) */
 

1.39.2.8  +65 -83    vorbis-tools/ogg123/ogg123.c

Index: ogg123.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.c,v
retrieving revision 1.39.2.7
retrieving revision 1.39.2.8
diff -u -r1.39.2.7 -r1.39.2.8
--- ogg123.c	2001/08/09 01:56:55	1.39.2.7
+++ ogg123.c	2001/08/10 16:33:40	1.39.2.8
@@ -1,4 +1,4 @@
-/* ogg123.c by Kenneth Arnold <kcarnold at arnoldnet.net> */
+/* ogg123.c by Kenneth Arnold <ogg123 at arnoldnet.net> */
 /* Modified to use libao by Stan Seibert <volsung at asu.edu> */
 
 /********************************************************************
@@ -8,18 +8,16 @@
  * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
  * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2000             *
- * by Monty <monty at xiph.org> and the XIPHOPHORUS Company            *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
  * http://www.xiph.org/                                             *
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: ogg123.c,v 1.39.2.7 2001/08/09 01:56:55 kcarnold Exp $
+ last mod: $Id: ogg123.c,v 1.39.2.8 2001/08/10 16:33:40 kcarnold Exp $
 
  ********************************************************************/
 
-/* FIXME : That was a messy message. Fix it. */
-
 #include <sys/types.h>
 #include <string.h>
 #include <stdlib.h>
@@ -30,18 +28,19 @@
 #include <errno.h>
 #include <time.h>
 #include <getopt.h>
-
 #include <signal.h>
 
 #include "ogg123.h"
+#include "ao_interface.h"
+#include "buffer.h"
 
 /* take buffer out of the data segment, not the stack */
 char convbuffer[BUFFER_CHUNK_SIZE];
 int convsize = BUFFER_CHUNK_SIZE;
-buf_t * buffer = NULL;
+buf_t * InBuffer = NULL;
+buf_t * OutBuffer = NULL;
 
 static char skipfile_requested;
-/*static void (*old_sig)(int);*/
 
 struct {
     char *key;			/* includes the '=' for programming convenience */
@@ -108,6 +107,7 @@
             "      v to previously specified device (with -d).  See\n"
             "      man page for more info.\n"
             "  -b n, --buffer n  use a buffer of approximately 'n' kilobytes\n"
+	    "  -p n, --prebuffer n  prebuffer n% of the buffer before playing\n"
             "  -v, --verbose  display progress and other useful stuff\n"
             "  -q, --quiet    don't display anything (no title)\n"
             "  -z, --shuffle  shuffle play\n"
@@ -134,7 +134,7 @@
     opt.nth = 1;
     opt.ntimes = 1;
 
-    atexit (ogg123_atexit);
+    on_exit (ogg123_onexit, &opt);
     signal (SIGINT, signal_quit);
     ao_initialize();
 
@@ -187,7 +187,12 @@
             usage();
             exit(0);
         case 'p':
-	  opt.prebuffer = atoi (optarg);
+	  opt.prebuffer = atoi (optarg); /* prebuffer in integer percentage */
+	  if (opt.prebuffer < 0 || opt.prebuffer > 100)
+	    {
+	      fprintf (stderr, "Prebuffer value invalid. Range is 0-100, using nearest value.\n");
+	      opt.prebuffer = opt.prebuffer < 0 ? 0 : 100;
+	    }
           break;
         case 'q':
             opt.quiet++;
@@ -215,8 +220,10 @@
         }
     }
 
-    if (opt.buffer_size > 1 && opt.prebuffer == 0)
-      opt.prebuffer = 1; /* for good measure */
+    if (optind == argc) {
+	usage();
+	exit(1);
+    }
 
     /* Add last device to device list or use the default device */
     if (temp_driver_id < 0) {
@@ -233,11 +240,13 @@
                                        temp_options, NULL);
     }
 
-    if (optind == argc) {
-	usage();
-	exit(1);
-    }
-
+    if (opt.buffer_size)
+      {
+	opt.prebuffer = (int) ((double) opt.prebuffer * (double) opt.buffer_size / 100.0F);
+	OutBuffer = StartBuffer (opt.buffer_size, opt.prebuffer,
+				 opt.outdevices, devices_write);
+      }
+    
     if (opt.shuffle) {
         int i;
         
@@ -257,22 +266,13 @@
         optind++;
     }
 
-    while (opt.outdevices != NULL) {
-      if (opt.outdevices->device)
-        ao_close(opt.outdevices->device);
-      current = opt.outdevices->next_device;
-      free(opt.outdevices);
-      opt.outdevices = current;
+    if (OutBuffer != NULL) {
+      buffer_WaitForEmpty (OutBuffer);
+      buffer_cleanup (OutBuffer);
+      OutBuffer = NULL;
     }
-
-    if (buffer != NULL) {
-	    buffer_shutdown(buffer);
-            buffer = NULL;
-    }
     
-    ao_shutdown();
-
-    return (0);
+    exit (0);
 }
 
 /* Two signal handlers, one for SIGINT, and the second for
@@ -292,23 +292,12 @@
    * and blow away existing "output.wav" file.
    */
 
-  fprintf (stderr, "skipfile\n");
-  
-#if 0
-  if (old_sig != NULL) {
-    signal(which_signal,old_sig);
-    raise(which_signal);
-  }
-  else
-#endif
-    signal (SIGINT, signal_quit);
-  /* should that be unconditional? man pages are not clear on this */
+  signal (SIGINT, signal_quit);
 }
 
 void signal_activate_skipfile(int ignored)
 {
-  fprintf (stderr, "activate skipfile.\n");
-  /*old_sig = */signal(SIGINT,signal_skipfile);
+  signal(SIGINT,signal_skipfile);
 }
 
 void signal_quit(int ignored)
@@ -331,8 +320,6 @@
     int is_big_endian = ao_is_big_endian();
     double realseekpos = opt.seekpos;
     int nthc = 0, ntimesc = 0;
-
-    /* Junk left over from the failed info struct */
     double u_time, u_pos;
 
     if (strcmp(opt.read_file, "-")) {	/* input file not stdin */
@@ -436,7 +423,7 @@
         vorbis_comment *vc = ov_comment(&vf, -1);
         vorbis_info *vi = ov_info(&vf, -1);
 
-	if(open_audio_devices(&opt, vi->rate, vi->channels, &buffer) < 0)
+	if(open_audio_devices(&opt, vi->rate, vi->channels) < 0)
                 exit(1);
 
         if (opt.quiet < 1) {
@@ -487,8 +474,8 @@
               skipfile_requested = 0;
               signal(SIGALRM,signal_activate_skipfile);
               alarm(opt.delay);
-	      if (buffer) {
-		buffer_flush (buffer);
+	      if (OutBuffer) {
+		buffer_flush (OutBuffer);
               }
               break;
               }
@@ -515,16 +502,16 @@
 
                 do {
                   if (nthc-- == 0) {
-		    if (buffer)
+		    if (OutBuffer)
                       {
                         chunk_t chunk;
                         chunk.len = ret;
                         memcpy (chunk.data, convbuffer, ret);
                         
-			submit_chunk (buffer, chunk);
+			submit_chunk (OutBuffer, chunk);
                       }
                     else
-		      devices_write(convbuffer, ret, opt.outdevices);
+		      devices_write(convbuffer, ret, 1, opt.outdevices);
                     nthc = opt.nth - 1;
                   }
                 } while (++ntimesc < opt.ntimes);
@@ -537,12 +524,12 @@
                       c_sec = u_pos - 60 * c_min;
                       r_min = (long) (u_time - u_pos) / (long) 60;
                       r_sec = (u_time - u_pos) - 60 * r_min;
-		      if (buffer)
+		      if (OutBuffer)
                         fprintf(stderr,
                                 "\rTime: %02li:%05.2f [%02li:%05.2f] of %02li:%05.2f, Bitrate: %.1f, Buffer fill: %3.0f%%   \r",
                                 c_min, c_sec, r_min, r_sec, t_min, t_sec,
                                 (double) ov_bitrate_instant(&vf) / 1000.0F,
-				(double) buffer_full(buffer) / (double) buffer->size * 100.0F);
+				(double) buffer_full(OutBuffer) / (double) OutBuffer->size * 100.0F);
                       else
                         fprintf(stderr,
                                 "\rTime: %02li:%05.2f [%02li:%05.2f] of %02li:%05.2f, Bitrate: %.1f   \r",
@@ -550,15 +537,16 @@
                                 (double) ov_bitrate_instant(&vf) / 1000.0F);
                     } else {
                       /* working around a bug in vorbisfile */
+		      /* I don't think that bug is there anymore -ken */
                       u_pos = (double) ov_pcm_tell(&vf) / (double) vi->rate;
                       c_min = (long) u_pos / (long) 60;
                       c_sec = u_pos - 60 * c_min;
-		      if (buffer)
+		      if (OutBuffer)
                         fprintf(stderr,
                                 "\rTime: %02li:%05.2f, Bitrate: %.1f, Buffer fill: %3.0f%%   \r",
                                 c_min, c_sec,
                                 (float) ov_bitrate_instant (&vf) / 1000.0F,
-				(double) buffer_full(buffer) / (double) buffer->size * 100.0F);
+				(double) buffer_full(OutBuffer) / (double) OutBuffer->size * 100.0F);
                       else
                         fprintf(stderr,
                                 "\rTime: %02li:%05.2f, Bitrate: %.1f   \r",
@@ -580,14 +568,9 @@
         fprintf(stderr, "\nDone.\n");
 }
 
-int get_tcp_socket(void)
-{
-    return socket(AF_INET, SOCK_STREAM, 0);
-}
-
 FILE *http_open(char *server, int port, char *path)
 {
-    int sockfd = get_tcp_socket();
+    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
     struct hostent *host;
     struct sockaddr_in sock_name;
 
@@ -611,7 +594,9 @@
     return fdopen(sockfd, "r+b");
 }
 
-int open_audio_devices(ogg123_options_t *opt, int rate, int channels, buf_t **buffer)
+/* if not for the two lines involving the buffer, this would go in
+ * ao_interface.c. */
+int open_audio_devices(ogg123_options_t *opt, int rate, int channels)
 {
   static int prevrate=0, prevchan=0;
   devices_t *current;
@@ -622,16 +607,10 @@
   
   if(prevrate !=0 && prevchan!=0)
         {
-	  if (buffer != NULL && *buffer != NULL) {
-	    buffer_shutdown (*buffer);
-	    *buffer = NULL;
-	  }
-
-	  current = opt->outdevices;
-	  while (current != NULL) {
-	    ao_close(current->device);
-	    current = current->next_device;
-	  }
+	  if (OutBuffer)
+	    buffer_WaitForEmpty (OutBuffer);
+
+	  close_audio_devices (opt->outdevices);
         }
   
   format.rate = prevrate = rate;
@@ -660,7 +639,7 @@
     if (current->device == NULL) {
       switch (errno) {
       case AO_ENODRIVER:
-	fprintf(stderr, "Error: No device not available.\n");
+	fprintf(stderr, "Error: Device not available.\n");
         break;
       case AO_ENOTLIVE:
         fprintf(stderr, "Error: %s requires an output filename to be specified with -f.\n", info->short_name);
@@ -696,15 +675,18 @@
     current = current->next_device;
   }
   
-  if (opt->buffer_size)
-    *buffer = fork_writer (opt->buffer_size, opt->outdevices, opt->prebuffer);
-  
-    return 0;
+  return 0;
 }
 
-void ogg123_atexit (void)
+void ogg123_onexit (int exitcode, void *arg)
 {
-  if (buffer)
-    buffer_cleanup (buffer);
-  buffer = NULL;
+  ogg123_options_t *opt = (ogg123_options_t*) arg;
+
+  if (OutBuffer) {
+    buffer_flush (OutBuffer);
+    buffer_cleanup (OutBuffer);
+    OutBuffer = NULL;
+  }
+
+  ao_atexit (exitcode, opt->outdevices);
 }

1.7.2.7   +21 -22    vorbis-tools/ogg123/ogg123.h

Index: ogg123.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.h,v
retrieving revision 1.7.2.6
retrieving revision 1.7.2.7
diff -u -r1.7.2.6 -r1.7.2.7
--- ogg123.h	2001/08/06 21:35:40	1.7.2.6
+++ ogg123.h	2001/08/10 16:33:40	1.7.2.7
@@ -1,5 +1,20 @@
-/* This file is part of ogg123, an Ogg Vorbis player. See ogg123.c
- * for copyright information. */
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+
+ last mod: $Id: ogg123.h,v 1.7.2.7 2001/08/10 16:33:40 kcarnold Exp $
+
+ ********************************************************************/
+
 #ifndef __OGG123_H
 #define __OGG123_H
 
@@ -13,14 +28,7 @@
 #include <alloca.h>
 #endif
 
-/* For facilitating output to multiple devices */
-typedef struct devices_s {
-  int driver_id;
-  ao_device *device;
-  ao_option *options;
-  char *filename;
-  struct devices_s *next_device;
-} devices_t;
+#include "ao_interface.h"
 
 typedef struct ogg123_options_s {
   char *read_file;            /* File to decode */
@@ -39,20 +47,11 @@
   int ntimes;                 /* Play every chunk n times */
 } ogg123_options_t;           /* Changed in 0.6 to be non-static */
 
-/* This goes here because it relies on some of the above. */
-#include "buffer.h"
-
-devices_t *append_device(devices_t * devices_list, int driver_id,
-                         ao_option * options, char *filename);
-void devices_write(void *ptr, size_t size, devices_t * d);
 void usage(void);
-int add_option(ao_option ** op_h, const char *optstring);
-int get_default_device(void);
 void play_file(ogg123_options_t opt);
-int get_tcp_socket(void); /* Will be going soon. */
-FILE *http_open(char *server, int port, char *path); /* ditto */
-int open_audio_devices(ogg123_options_t *opt, int rate, int channels, buf_t ** buffer);
+FILE *http_open(char *server, int port, char *path);
+int open_audio_devices(ogg123_options_t *opt, int rate, int channels);
 void signal_quit (int ignored);
-void ogg123_atexit (void);
+void ogg123_onexit (int exitcode, void *arg);
 
 #endif /* !defined(__OGG123_H) */

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list