[xiph-cvs] cvs commit: vorbis-tools/ogg123 audio.c audio.h cfgfile_options.c cfgfile_options.h cmdline_options.c cmdline_options.h file_transport.c format.c format.h oggvorbis_format.c transport.c transport.h Makefile.am buffer.c buffer.h ogg123.c ogg123.h status.c status.h ao_interface.c ao_interface.h curl_interface.c curl_interface.h nullbuffer.c options.c options.h

Stan Seibert volsung at xiph.org
Sat Dec 8 15:59:30 PST 2001



volsung     01/12/08 15:59:29

  Modified:    ogg123   Tag: volsung_kc_20011011 Makefile.am buffer.c
                        buffer.h ogg123.c ogg123.h status.c status.h
  Added:       ogg123   Tag: volsung_kc_20011011 audio.c audio.h
                        cfgfile_options.c cfgfile_options.h
                        cmdline_options.c cmdline_options.h
                        file_transport.c format.c format.h
                        oggvorbis_format.c transport.c transport.h
  Removed:     ogg123   Tag: volsung_kc_20011011 ao_interface.c
                        ao_interface.h curl_interface.c curl_interface.h
                        nullbuffer.c options.c options.h
  Log:
  A more severe cleaning up of ogg123, something I should have done earlier.
  
  Highlights are:
   - Abstracted data transport and file format interfaces to support other
   codecs besides Ogg Vorbis.  Right now files are the only transport method
   and Ogg Vorbis is the only format support.  It the future we can add
   support for FLAC, RTP, and whatever else comes our way.  This interfaces
   are quickly evolving.
  
   - Now use "action buffers" (term I made up) which can buffer callbacks
   ("actions") and data.  This now lets us stuff audio with different sample
   rates and channel numbers into the same buffer by putting an action to
   change the audio settings between the two audio blocks.  File information
   will also be displayed using actions so that the info is synchronized with
   playback rather than decoding.
  
   - Source code formatting and naming conventions are now mostly uniform.
  
   - Main ogg123.c is more readable now that much of it has been pushed into
   other files.
  
   - *LOTS* of changes to remove global references, clean up interfaces, and
   generally make thing easier to understand (I hope).
  
  Lowlights are:
   - I've removed a lot of the original ogg123 functionality.  I will add it
   back in pieces using the new infrastructure.
  
   - Lots of stuff is broken or semi-functional.  I've only tested to see that
   playback occurs with and without buffering.  Everything else is untested.

Revision  Changes    Path
No                   revision

No                   revision

1.14.2.4.2.2 +5 -2      vorbis-tools/ogg123/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/Makefile.am,v
retrieving revision 1.14.2.4.2.1
retrieving revision 1.14.2.4.2.2
diff -u -r1.14.2.4.2.1 -r1.14.2.4.2.2
--- Makefile.am	2001/11/23 05:15:29	1.14.2.4.2.1
+++ Makefile.am	2001/12/08 23:59:24	1.14.2.4.2.2
@@ -13,8 +13,11 @@
 ogg123_LDADD = @VORBISFILE_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ @AO_LIBS@ \
                 @SOCKET_LIBS@ @THREAD_LIBS@ -lcurl
 
-ogg123_SOURCES = ogg123.c ao_interface.c buffer.c getopt.c getopt1.c curl_interface.c options.c status.c \
-	         ogg123.h ao_interface.h buffer.h getopt.h           curl_interface.h options.h status.h
+ogg123_SOURCES = audio.c buffer.c cfgfile_options.c cmdline_options.c \
+                 file_transport.c format.c getopt.c getopt1.c         \
+                 ogg123.c oggvorbis_format.c status.c transport.c     \
+                 audio.h buffer.h cfgfile_options.h cmdline_options.h \
+                 format.h getopt.h ogg123.h status.h transport.h
 
 EXTRA_DIST = $(man_MANS) $(doc_DATA)
 

1.7.2.23.2.6 +343 -202  vorbis-tools/ogg123/buffer.c

Index: buffer.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/buffer.c,v
retrieving revision 1.7.2.23.2.5
retrieving revision 1.7.2.23.2.6
diff -u -r1.7.2.23.2.5 -r1.7.2.23.2.6
--- buffer.c	2001/11/23 05:15:29	1.7.2.23.2.5
+++ buffer.c	2001/12/08 23:59:24	1.7.2.23.2.6
@@ -11,7 +11,7 @@
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: buffer.c,v 1.7.2.23.2.5 2001/11/23 05:15:29 volsung Exp $
+ last mod: $Id: buffer.c,v 1.7.2.23.2.6 2001/12/08 23:59:24 volsung Exp $
 
  ********************************************************************/
 
@@ -31,13 +31,16 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
+#include <stdio.h>
 
-#include "ogg123.h"
 #include "buffer.h"
 
-#define MIN(x,y)     ( (x) < (y) ? (x) : (y) )
-#define MIN3(x,y,z)  MIN(x,MIN(y,z))
+#define MIN(x,y)       ( (x) < (y) ? (x) : (y) )
+#define MIN3(x,y,z)    MIN(x,MIN(y,z))
+#define MIN4(w,x,y,z)  MIN( MIN(w,x), MIN(y,z) )
 
+
+/* Special debugging code.  THIS IS NOT PORTABLE! */
 #ifdef DEBUG_BUFFER
 FILE *debugfile;
 #define DEBUG(x, y...) { fprintf (debugfile, "%d: " x "\n", getpid(),  ## y); }
@@ -45,25 +48,17 @@
 #define DEBUG(x, y...)
 #endif
 
+/* Macros that support debugging of threading structures */
+
 #define LOCK_MUTEX(mutex) { DEBUG("Locking mutex %s.", #mutex); pthread_mutex_lock (&(mutex)); }
 #define UNLOCK_MUTEX(mutex) { DEBUG("Unlocking mutex %s", #mutex); pthread_mutex_unlock(&(mutex)); }
 #define COND_WAIT(cond, mutex) { DEBUG("Unlocking %s and waiting on %s", #mutex, #cond); pthread_cond_wait(&(cond), &(mutex)); }
 #define COND_SIGNAL(cond) { DEBUG("Signalling %s", #cond); pthread_cond_signal(&(cond)); }
 
-void _buffer_thread_cleanup (void *arg)
-{
-  buf_t *buf = (buf_t *)arg;
 
-  DEBUG("Enter _buffer_thread_cleanup");
+/* -------------------- Private Functions ------------------ */
 
-  /* Cleanup thread data structures */
-  pthread_mutex_destroy (&buf->mutex);
-  pthread_cond_destroy (&buf->playback_cond);
-  pthread_cond_destroy (&buf->write_cond);
-}
-
-
-void _buffer_init_vars (buf_t *buf)
+void buffer_init_vars (buf_t *buf)
 {
   /* Initialize buffer flags */
   buf->prebuffering = buf->prebuffer_size > 0;
@@ -72,13 +67,15 @@
   
   buf->curfill = 0;
   buf->start = 0;
+  buf->position = 0;
+  buf->position_end = 0;
 }
 
-void _buffer_thread_init (buf_t *buf)
+void buffer_thread_init (buf_t *buf)
 {
   sigset_t set;
 
-  DEBUG("Enter _buffer_thread_init");
+  DEBUG("Enter buffer_thread_init");
 
   /* Block signals to this thread */
   sigfillset (&set);
@@ -87,144 +84,226 @@
   sigaddset (&set, SIGCONT);
   if (pthread_sigmask (SIG_BLOCK, &set, NULL) != 0)
     DEBUG("pthread_sigmask failed");
-      
+
+}
+
+
+void buffer_thread_cleanup (void *arg)
+{
+  buf_t *buf = (buf_t *)arg;
+
+  DEBUG("Enter buffer_thread_cleanup");
+
+  /* Cleanup thread data structures */
+  pthread_mutex_destroy (&buf->mutex);
+  pthread_cond_destroy (&buf->playback_cond);
+  pthread_cond_destroy (&buf->write_cond);
+}
+
+
+action_t *malloc_action (action_func_t action_func, void *action_arg)
+{
+  action_t *action;
+
+  action = malloc(sizeof(action_t));
   
-  /* Run the initialization function, if there is one */
-  if (buf->init_func) 
-    {
-      int ret = buf->init_func (buf->initData);
-      if (!ret)
-	pthread_exit ((void*)ret);
-    }
+  if (action == NULL) {
+    fprintf(stderr, "Error: Out of memory in malloc_action().\n");
+    exit(1);
+  }
+
+  action->position = 0;
+  action->action_func = action_func;
+  action->arg = action_arg;
+  action->next = NULL;
+
+  return action;
+}
+
+
+void in_order_append_action (action_t **action_list, action_t *action)
+{
+  while (*action_list != NULL && (*action_list)->position <= action->position)
+    action_list = &((*action_list)->next);
+
+  action->next = *action_list;
+  *action_list = action;
 }
 
 
-void *_buffer_thread_func (void *arg)
+void execute_actions (buf_t *buf, action_t **action_list, ogg_int64_t position)
 {
+  action_t *action;
+
+  while (*action_list != NULL && (*action_list)->position <= position) {
+    action = *action_list;
+    action->action_func(buf, action->arg);
+    
+    *action_list = (*action_list)->next;
+    free(action);
+  }
+}
+
+
+void free_action (action_t *action)
+{
+  free(action);
+}
+
+
+
+int compute_dequeue_size (buf_t *buf, int request_size)
+{
+  int next_action_pos;
+
+  /* 
+     For simplicity, the number of bytes played must satisfy the following
+     requirements:
+     1. Do not extract more bytes than are stored in the buffer.
+     2. Do not extract more bytes than the requested number of bytes.
+     3. Do not run off the end of the buffer.
+     4. Do not go past the next action.
+  */
+
+  if (buf->actions != NULL) {
+
+    next_action_pos = buf->actions->position;
+    
+    return MIN4(buf->curfill, request_size,
+		buf->size - buf->start, next_action_pos - buf->position);
+  } else
+    return MIN3(buf->curfill, request_size, buf->size - buf->start);
+
+}
+
+
+void *buffer_thread_func (void *arg)
+{
   buf_t *buf = (buf_t*) arg;
   size_t write_amount;
   
-  DEBUG("Enter _buffer_thread_func");
+  DEBUG("Enter buffer_thread_func");
   
-  _buffer_thread_init(buf);
+  buffer_thread_init(buf);
 
-  pthread_cleanup_push (_buffer_thread_cleanup, buf);
+  pthread_cleanup_push(buffer_thread_cleanup, buf);
   
   DEBUG("Start main play loop");
   
-  while (1) 
-    {
+  /* This test is safe since curfill will never decrease and eos will
+     never be unset. */
+  while ( !(buf->eos && buf->curfill == 0) ) {
+    
+    DEBUG("Check for something to play");
+    /* Block until we can play something */
+    LOCK_MUTEX (buf->mutex);
+    if (buf->prebuffering || 
+	buf->paused || 
+	(buf->curfill < buf->audio_chunk_size && !buf->eos)) {
 
-      DEBUG("Check for something to play");
-      /* Block until we can play something */
-      LOCK_MUTEX (buf->mutex);
-      if (buf->prebuffering || 
-	  buf->paused || 
-	  (buf->curfill < buf->audio_chunk_size && !buf->eos))
-	{
-	  DEBUG("Waiting for more data to play.");
-	  COND_WAIT(buf->playback_cond, buf->mutex);
-	}
-
-      DEBUG("Ready to play");
-
-      /* Figure out how much we can play in the buffer */
-
-      /* For simplicity, the number of bytes played must satisfy the following
-         three requirements:
-	 1. Do not play more bytes than are stored in the buffer.
-	 2. Do not play more bytes than the suggested audio chunk size.
-	 3. Do not run off the end of the buffer. */
-      write_amount = MIN3(buf->curfill, buf->audio_chunk_size, 
-			  buf->size - buf->start);
-      UNLOCK_MUTEX(buf->mutex);
-      
-      /* No need to lock mutex here because the other thread will
-         NEVER reduce the number of bytes stored in the buffer */
-      DEBUG("Sending %d bytes to the audio device", write_amount);
-      buf->write_func(buf->buffer + buf->start, sizeof(chunk), write_amount,
-		      buf->data, 
-		      /* Only set EOS if this is the last chunk */
-		      write_amount == buf->curfill ? buf->eos : 0);
-      
-      LOCK_MUTEX(buf->mutex);
-      buf->curfill -= write_amount;
-      buf->start = (buf->start + write_amount) % buf->size;
-      DEBUG("Updated buffer fill, curfill = %ld", buf->curfill);
-
-      /* If we've essentially emptied the buffer and prebuffering is enabled,
-	 we need to do another prebuffering session */
-      if (!buf->eos && (buf->curfill < buf->audio_chunk_size))
-	buf->prebuffering = buf->prebuffer_size > 0;
-
-      /* Signal a waiting decoder thread that they can put more audio into the
-	 buffer */
-      DEBUG("Signal decoder thread that buffer space is available");
-      COND_SIGNAL(buf->write_cond);
-      UNLOCK_MUTEX(buf->mutex);
+      DEBUG("Waiting for more data to play.");
+      COND_WAIT(buf->playback_cond, buf->mutex);
     }
+
+    DEBUG("Ready to play");
+    
+    /* Figure out how much we can play in the buffer */
+    write_amount = compute_dequeue_size(buf, buf->audio_chunk_size);
 
-  /* should never get here */
+    UNLOCK_MUTEX(buf->mutex);
+   
+    /* Don't need to lock buffer while running acitons since position
+       won't change */
+    execute_actions(buf, &buf->actions, buf->position);
+ 
+    /* No need to lock mutex here because the other thread will
+       NEVER reduce the number of bytes stored in the buffer */
+    DEBUG("Sending %d bytes to the audio device", write_amount);
+    buf->write_func(buf->buffer + buf->start, write_amount,
+		    /* Only set EOS if this is the last chunk */
+		    write_amount == buf->curfill ? buf->eos : 0,
+		    buf->write_arg);
+      
+    LOCK_MUTEX(buf->mutex);
+
+    buf->curfill -= write_amount;
+    buf->position += write_amount;
+    buf->start = (buf->start + write_amount) % buf->size;
+    DEBUG("Updated buffer fill, curfill = %ld", buf->curfill);
+
+    /* If we've essentially emptied the buffer and prebuffering is enabled,
+       we need to do another prebuffering session */
+    if (!buf->eos && (buf->curfill < buf->audio_chunk_size))
+      buf->prebuffering = buf->prebuffer_size > 0;
+    
+    /* Signal a waiting decoder thread that they can put more audio into the
+       buffer */
+    DEBUG("Signal decoder thread that buffer space is available");
+    COND_SIGNAL(buf->write_cond);
+    
+    UNLOCK_MUTEX(buf->mutex);
+  }
+  
   pthread_cleanup_pop(1);
   DEBUG("exiting buffer_thread_func");
 }
 
 
-void _submit_data_chunk (buf_t *buf, chunk *data, size_t size)
+void submit_data_chunk (buf_t *buf, char *data, size_t size)
 {
   long   buf_write_pos; /* offset of first available write location */
   size_t write_size;
 
-  DEBUG("Enter _submit_data_chunk, size %d", size);
+  DEBUG("Enter submit_data_chunk, size %d", size);
 
   /* Put the data into the buffer as space is made available */
-  while (size > 0)
-    {
+  while (size > 0) {
+    
+    /* Section 1: Write a chunk of data */
+    DEBUG("Obtaining lock on buffer");
+    LOCK_MUTEX(buf->mutex);
+    if (buf->size - buf->curfill > 0) {
+      
+      /* Figure how much we can write into the buffer.  Requirements:
+	 1. Don't write more data than we have.
+	 2. Don't write more data than we have room for.
+	 3. Don't write past the end of the buffer. */
+      buf_write_pos = (buf->start + buf->curfill) % buf->size;
+      write_size = MIN3(size, buf->size - buf->curfill,
+			buf->size - buf_write_pos);
+      
+      memcpy(buf->buffer + buf_write_pos, data, write_size);
+      buf->curfill += write_size;
+      data += write_size;
+      size -= write_size;
+      buf->position_end += write_size;
+      DEBUG("writing chunk into buffer, curfill = %ld", buf->curfill);
+    }
+    else {
 
-      /* Section 1: Write a chunk of data */
-      DEBUG("Obtaining lock on buffer");
-      LOCK_MUTEX(buf->mutex);
-      if (buf->size - buf->curfill > 0)
-	{
-	  /* Figure how much we can write into the buffer.  Requirements:
-	     1. Don't write more data than we have.
-	     2. Don't write more data than we have room for.
-	     3. Don't write past the end of the buffer. */
-	  buf_write_pos = (buf->start + buf->curfill) % buf->size;
-	  write_size = MIN3(size, buf->size - buf->curfill, 
-			    buf->size - buf_write_pos);
-
-	  memmove (buf->buffer + buf_write_pos, data, write_size);
-	  buf->curfill += write_size;
-	  data += write_size;
-	  size -= write_size;
-	  DEBUG("writing chunk into buffer, curfill = %ld", buf->curfill);
-	}
-      else
-	{
-	  /* No room for more data, wait until there is */
-	  DEBUG("No room for data in buffer.  Waiting.");
-	  COND_WAIT(buf->write_cond, buf->mutex);
-	}
+      /* No room for more data, wait until there is */
+      DEBUG("No room for data in buffer.  Waiting.");
+      COND_WAIT(buf->write_cond, buf->mutex);
+    }
       
-      /* Section 2: signal if we are not prebuffering, done
-         prebuffering, or paused */
-      if (buf->prebuffering && (buf->prebuffer_size <= buf->curfill))
-	{
-	  DEBUG("prebuffering done")
-	    buf->prebuffering = 0; /* done prebuffering */
-	}
-
-      if (!buf->prebuffering && !buf->paused)
-	{
-	  DEBUG("Signalling playback thread that more data is available.");
-	  COND_SIGNAL(buf->playback_cond);      
-	}
-      else
-	DEBUG("Not signalling playback thread since prebuffering or paused.");
-      UNLOCK_MUTEX(buf->mutex);
+    /* Section 2: signal if we are not prebuffering, done
+       prebuffering, or paused */
+    if (buf->prebuffering && (buf->prebuffer_size <= buf->curfill)) {
+
+      DEBUG("prebuffering done")
+	buf->prebuffering = 0; /* done prebuffering */
     }
 
+    if (!buf->prebuffering && !buf->paused) {
+
+      DEBUG("Signalling playback thread that more data is available.");
+      COND_SIGNAL(buf->playback_cond);      
+    } else
+      DEBUG("Not signalling playback thread since prebuffering or paused.");
+      
+    UNLOCK_MUTEX(buf->mutex);
+  }
+
   DEBUG("Exit submit_data_chunk");
 }
 
@@ -233,17 +312,16 @@
 
 /* --- Buffer allocation --- */
 
-buf_t *buffer_create (long size, long prebuffer_size, void *data, 
-		      pWriteFunc write_func, void *initData, 
-		      pInitFunc init_func, int audio_chunk_size)
+buf_t *buffer_create (long size, long prebuffer,
+		      buffer_write_func_t write_func, void *arg,
+		      int audio_chunk_size)
 {
-  buf_t *buf = malloc (sizeof(buf_t) + sizeof (chunk) * (size - 1));
+  buf_t *buf = malloc (sizeof(buf_t) + sizeof (char) * (size - 1));
 
-  if (buf == NULL)
-    {
+  if (buf == NULL) {
       perror ("malloc");
-      exit (1);
-    }
+      exit(1);
+  }
 
 #ifdef DEBUG_BUFFER
   debugfile = fopen ("/tmp/bufferdebug", "w");
@@ -255,12 +333,9 @@
 
   memset (buf, 0, sizeof(*buf));
 
-  buf->data = data;
   buf->write_func = write_func;
+  buf->write_arg = arg;
 
-  buf->initData = initData;
-  buf->init_func = init_func;
-
   /* Setup pthread variables */
   pthread_mutex_init (&buf->mutex, NULL);
   pthread_cond_init (&buf->write_cond, NULL);
@@ -271,18 +346,22 @@
     audio_chunk_size = size / 2;
 
   buf->audio_chunk_size = audio_chunk_size;
-  buf->prebuffer_size = prebuffer_size;
+  buf->prebuffer_size = prebuffer;
   buf->size = size;
 
+  buf->actions = 0;
+
   /* Initialize flags */
-  _buffer_init_vars(buf);
+  buffer_init_vars(buf);
 
   return buf;
 }
 
 
-void buffer_sync_reset (buf_t *buf)
+void buffer_reset (buf_t *buf)
 {
+  action_t *action;
+
   /* Cleanup pthread variables */
   pthread_mutex_destroy (&buf->mutex);
   pthread_cond_destroy (&buf->write_cond);
@@ -293,7 +372,14 @@
   pthread_cond_init (&buf->write_cond, NULL);
   pthread_cond_init (&buf->playback_cond, NULL);
 
-  _buffer_init_vars(buf);
+  /* Clear old actions */
+  while (buf->actions != NULL) {
+    action = buf->actions;
+    buf->actions = buf->actions->next;
+    free(action);
+  }
+
+  buffer_init_vars(buf);
 }
 
 
@@ -306,17 +392,18 @@
 
 /* --- Buffer thread control --- */
 
-int buffer_thread_start   (buf_t *buf)
+int buffer_thread_start (buf_t *buf)
 {
   DEBUG("Starting new thread.");
 
-  return pthread_create(&buf->thread, NULL, _buffer_thread_func, buf);
+  return pthread_create(&buf->thread, NULL, buffer_thread_func, buf);
 }
 
+
 /* WARNING: DO NOT call buffer_submit_data after you pause the
    playback thread, or you run the risk of deadlocking.  Call
    buffer_thread_unpause first. */
-void buffer_thread_pause   (buf_t *buf)
+void buffer_thread_pause (buf_t *buf)
 {
   DEBUG("Pausing playback thread");
 
@@ -337,7 +424,7 @@
 }
 
 
-void buffer_thread_kill    (buf_t *buf)
+void buffer_thread_kill (buf_t *buf)
 {
   DEBUG("Attempting to kill playback thread.");
 
@@ -349,77 +436,77 @@
 
   pthread_join (buf->thread, NULL);
 
-  _buffer_thread_cleanup(buf);
+  buffer_thread_cleanup(buf);
 
   DEBUG("Playback thread killed.");
 }
 
 
 /* --- Data buffering functions --- */
-void buffer_submit_data (buf_t *buf, chunk *data, size_t size, size_t nmemb)
+
+void buffer_submit_data (buf_t *buf, char *data, long nbytes)
 {
-  size *= nmemb;
-  _submit_data_chunk (buf, data, size);
+  submit_data_chunk (buf, data, nbytes);
 }
 
-size_t buffer_get_data (buf_t *buf, chunk *data, size_t size, size_t nmemb)
+size_t buffer_get_data (buf_t *buf, char *data, long nbytes)
 {
   int write_amount;
   int orig_size;
 
-  size *= nmemb;
-  orig_size = size;
+  orig_size = nbytes;
 
   DEBUG("Enter buffer_get_data");
 
   LOCK_MUTEX(buf->mutex);
 
   /* Put the data into the buffer as space is made available */
-  while (size > 0)
-    {
-      DEBUG("Obtaining lock on buffer");
-      /* Block until we can read something */
-      if (buf->curfill == 0)
-	{
-	  if (buf->eos)
-	    break; /* No more data to read */
-
-	  DEBUG("Waiting for more data to copy.");
-	  COND_WAIT(buf->playback_cond, buf->mutex);
-	}
-
-      /* Note: Even if curfill is still 0, nothing bad will happen here */
-
-      /* For simplicity, the number of bytes played must satisfy
-	 the following three requirements:
-	 
-	 1. Do not copy more bytes than are stored in the buffer.
-	 2. Do not copy more bytes than the reqested data size.
-	 3. Do not run off the end of the buffer. */
-      write_amount = MIN3(buf->curfill, size, buf->size - buf->start);
-      
-      /* No need to lock mutex here because the other thread will
-	 NEVER reduce the number of bytes stored in the buffer */
-      DEBUG("Copying %d bytes from the buffer", write_amount);
-      memcpy(data, buf->buffer + buf->start, write_amount);
-      
-      buf->curfill -= write_amount;
-      data += write_amount;
-      size -= write_amount;
-      buf->start = (buf->start + write_amount) % buf->size;
-      DEBUG("Updated buffer fill, curfill = %ld", buf->curfill);
+  while (nbytes > 0) {
+
+    DEBUG("Obtaining lock on buffer");
+    /* Block until we can read something */
+    if (buf->curfill == 0) {
+      if (buf->eos)
+	break; /* No more data to read */
       
-      /* Signal a waiting decoder thread that they can put more
-	 audio into the buffer */
-      DEBUG("Signal decoder thread that buffer space is available");
-      COND_SIGNAL(buf->write_cond);
+      DEBUG("Waiting for more data to copy.");
+      COND_WAIT(buf->playback_cond, buf->mutex);
     }
 
-   UNLOCK_MUTEX(buf->mutex);
+    /* Note: Even if curfill is still 0, nothing bad will happen here */
+    
+    /* For simplicity, the number of bytes played must satisfy
+       the following three requirements:
+       
+       1. Do not copy more bytes than are stored in the buffer.
+       2. Do not copy more bytes than the reqested data size.
+       3. Do not run off the end of the buffer. */
+    write_amount = compute_dequeue_size(buf, nbytes);
+
+    execute_actions(buf, &buf->actions, buf->position);
+    
+    /* No need to lock mutex here because the other thread will
+       NEVER reduce the number of bytes stored in the buffer */
+    DEBUG("Copying %d bytes from the buffer", write_amount);
+    memcpy(data, buf->buffer + buf->start, write_amount);
+    
+    buf->curfill -= write_amount;
+    data += write_amount;
+    nbytes -= write_amount;
+    buf->start = (buf->start + write_amount) % buf->size;
+    DEBUG("Updated buffer fill, curfill = %ld", buf->curfill);
+    
+    /* Signal a waiting decoder thread that they can put more
+       audio into the buffer */
+    DEBUG("Signal decoder thread that buffer space is available");
+    COND_SIGNAL(buf->write_cond);
+  }
 
-   DEBUG("Exit buffer_get_data");
+  UNLOCK_MUTEX(buf->mutex);
+  
+  DEBUG("Exit buffer_get_data");
    
-   return orig_size - size;
+  return orig_size - nbytes;
 }
 
 void buffer_mark_eos (buf_t *buf)
@@ -434,6 +521,62 @@
 }
 
 
+/* --- Action buffering functions --- */
+
+void buffer_action_now (buf_t *buf, action_func_t action_func, 
+			void *action_arg)
+{
+  action_t *action;
+  
+  action = malloc_action(action_func, action_arg);
+
+  LOCK_MUTEX(buf->mutex);
+
+  action->position = buf->position;
+
+  /* Insert this action right at the front */
+  action->next = buf->actions;
+  buf->actions = action;
+
+  UNLOCK_MUTEX(buf->mutex);
+}
+
+
+void buffer_action_at_end (buf_t *buf, action_func_t action_func, 
+			   void *action_arg)
+{
+  action_t *action;
+
+  action = malloc_action(action_func, action_arg);
+
+  LOCK_MUTEX(buf->mutex);
+
+  /* Stick after the last item in the buffer */
+  action->position = buf->position;
+
+  in_order_append_action(&buf->actions, action);
+
+  UNLOCK_MUTEX(buf->mutex);
+}
+
+
+void buffer_action_at (buf_t *buf, action_func_t action_func, 
+		       void *action_arg, ogg_int64_t position)
+{
+  action_t *action;
+
+  action = malloc_action(action_func, action_arg);
+
+  LOCK_MUTEX(buf->mutex);
+  
+  action->position = position;
+
+  in_order_append_action(&buf->actions, action);
+
+  UNLOCK_MUTEX(buf->mutex);  
+}
+
+
 /* --- Buffer status functions --- */
 
 void buffer_wait_for_empty (buf_t *buf)
@@ -443,16 +586,14 @@
   DEBUG("Enter buffer_wait_for_empty");
   
   LOCK_MUTEX(buf->mutex);
-  while (!empty)
-    {
-      if (buf->curfill > 0)
-	{
-	  DEBUG("Buffer curfill = %ld, going back to sleep.", buf->curfill);
-	  COND_WAIT(buf->write_cond, buf->mutex);
-	}
-      else 
-	empty = 1;
-    }
+  while (!empty) {
+
+    if (buf->curfill > 0) {
+      DEBUG("Buffer curfill = %ld, going back to sleep.", buf->curfill);
+      COND_WAIT(buf->write_cond, buf->mutex);
+    } else 
+      empty = 1;
+  }
   UNLOCK_MUTEX(buf->mutex);
 
   DEBUG("Exit buffer_wait_for_empty");

1.2.2.16.2.4 +47 -21    vorbis-tools/ogg123/buffer.h

Index: buffer.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/buffer.h,v
retrieving revision 1.2.2.16.2.3
retrieving revision 1.2.2.16.2.4
diff -u -r1.2.2.16.2.3 -r1.2.2.16.2.4
--- buffer.h	2001/11/21 22:57:23	1.2.2.16.2.3
+++ buffer.h	2001/12/08 23:59:24	1.2.2.16.2.4
@@ -11,30 +11,32 @@
  *                                                                  *
  ********************************************************************
  
- last mod: $Id: buffer.h,v 1.2.2.16.2.3 2001/11/21 22:57:23 volsung Exp $
+ last mod: $Id: buffer.h,v 1.2.2.16.2.4 2001/12/08 23:59:24 volsung Exp $
  
 ********************************************************************/
 
-/* A (relatively) generic circular buffer interface */
+/* A generic circular buffer interface with the ability to buffer
+   actions (conceptually) between bytes in the buffer.*/
 
-#ifndef __BUFFER_H
-#define __BUFFER_H
+#ifndef __BUFFER_H__
+#define __BUFFER_H__
 
+#include <stdlib.h>
 #include <pthread.h>
+#include <ogg/os_types.h>
 
-typedef unsigned char chunk; /* sizeof (chunk) must be 1; if you need otherwise it's not hard to fix */
-typedef size_t (*pWriteFunc) (void *, size_t, size_t, void *, char);
-typedef int (*pInitFunc) (void *);
 
-typedef struct buf_s
+struct action_t; /* forward declaration */
+
+/* buffer_write_func(void *data, int nbytes, int eos, void *arg) */
+typedef int (*buffer_write_func_t) (void *, int, int, void *);
+
+typedef struct buf_t
 {
   /* generic buffer interface */
-  void * data;
-  pWriteFunc write_func;
+  void *write_arg;
+  buffer_write_func_t write_func;
 
-  void * initData;
-  pInitFunc init_func;
-  
   /* pthread variables */
   pthread_t thread;
 
@@ -60,16 +62,32 @@
   /* buffer data */
   long curfill;     /* how much the buffer is currently filled */
   long start;       /* offset in buffer of start of available data */
-  chunk buffer[1];   /* The buffer itself. It's more than one chunk. */
+  ogg_int64_t position; /* How many bytes have we output so far */
+  ogg_int64_t position_end; /* Position right after end of data */
+
+  struct action_t *actions; /* Queue actions to perform */
+  char buffer[1];   /* The buffer itself. It's more than one byte. */
 } buf_t;
 
+
+/* action_func(buf_t *buf, void *arg) */
+typedef void (*action_func_t) (buf_t *, void *);
+
+typedef struct action_t {
+  ogg_int64_t position;
+  action_func_t action_func;
+  void *arg;
+  struct action_t *next;
+} action_t;
+
+
 /* --- Buffer allocation --- */
 
-buf_t *buffer_create (long size, long prebuffer, void *data, 
-		      pWriteFunc write_func, void *initData, 
-		      pInitFunc init_func, int audio_chunk_size);
+buf_t *buffer_create (long size, long prebuffer,
+		      buffer_write_func_t write_func, void *arg,
+		      int audio_chunk_size);
 
-void buffer_sync_reset (buf_t *buf);
+void buffer_reset (buf_t *buf);
 void buffer_destroy (buf_t *buf);
 
 /* --- Buffer thread control --- */
@@ -79,13 +97,21 @@
 void buffer_thread_kill    (buf_t *buf);
 
 /* --- Data buffering functions --- */
-void buffer_submit_data (buf_t *buf, chunk *data, size_t size, size_t nmemb);
-size_t buffer_get_data (buf_t *buf, chunk *data, size_t size, size_t nmemb);
+void buffer_submit_data (buf_t *buf, char *data, long nbytes);
+size_t buffer_get_data (buf_t *buf, char *data, long nbytes);
 
 void buffer_mark_eos (buf_t *buf);
 
+/* --- Action buffering functions --- */
+void buffer_action_now (buf_t *buf, action_func_t action_func, 
+			void *action_arg);
+void buffer_action_at_end (buf_t *buf, action_func_t action_func, 
+			   void *action_arg);
+void buffer_action_at (buf_t *buf, action_func_t action_func, 
+		       void *action_arg, ogg_int64_t position);
+
 /* --- Buffer status functions --- */
 void buffer_wait_for_empty (buf_t *buf);
 long buffer_full (buf_t *buf);
 
-#endif /* !defined (__BUFFER_H) */
+#endif /* __BUFFER_H__ */

1.39.2.30.2.12 +262 -990  vorbis-tools/ogg123/ogg123.c

Index: ogg123.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.c,v
retrieving revision 1.39.2.30.2.11
retrieving revision 1.39.2.30.2.12
diff -u -r1.39.2.30.2.11 -r1.39.2.30.2.12
--- ogg123.c	2001/12/07 20:47:39	1.39.2.30.2.11
+++ ogg123.c	2001/12/08 23:59:25	1.39.2.30.2.12
@@ -1,5 +1,5 @@
 /* ogg123.c by Kenneth Arnold <ogg123 at arnoldnet.net> */
-/* Modified to use libao by Stan Seibert <volsung at asu.edu> */
+/* Maintained by Stan Seibert <volsung at xiph.org> */
 
 /********************************************************************
  *                                                                  *
@@ -14,12 +14,10 @@
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: ogg123.c,v 1.39.2.30.2.11 2001/12/07 20:47:39 jack Exp $
+ last mod: $Id: ogg123.c,v 1.39.2.30.2.12 2001/12/08 23:59:25 volsung Exp $
 
  ********************************************************************/
 
-#define _GNU_SOURCE
-
 #include <sys/types.h>
 #include <string.h>
 #include <stdlib.h>
@@ -30,314 +28,81 @@
 #include <signal.h>
 #include <unistd.h>
 
-#include "ogg123.h"
-#include "ao_interface.h"
-#include "curl_interface.h"
+#include "audio.h"
 #include "buffer.h"
-#include "options.h"
+#include "cfgfile_options.h"
+#include "cmdline_options.h"
+#include "format.h"
+#include "transport.h"
 #include "status.h"
-
-/* take buffer out of the data segment, not the stack */
-#define BUFFER_CHUNK_SIZE 4096
-unsigned char convbuffer[BUFFER_CHUNK_SIZE];
-unsigned char convbuffer2[BUFFER_CHUNK_SIZE];
-int convsize = BUFFER_CHUNK_SIZE;
-
-/* take big options structure from the data segment */
-ogg123_options_t Options;
-
-
-/* Flags set by the signal handler to control the threads */
-signal_request_t sig_request = {0, 0, 0};
-
-pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
-
-struct {
-    const char *key;			/* includes the '=' for programming convenience */
-    const char *formatstr;		/* formatted output */
-} ogg_comment_keys[] = {
-  {"ARTIST=", "Artist: %s"},
-  {"ALBUM=", "Album: %s"},
-  {"TITLE=", "Title: %s"},
-  {"VERSION=", "Version: %s"},
-  {"TRACKNUMBER=", "Track number: %s"},
-  {"ORGANIZATION=", "Organization: %s"},
-  {"GENRE=", "Genre: %s"},
-  {"DESCRIPTION=", "Description: %s"},
-  {"DATE=", "Date: %s"},
-  {"LOCATION=", "Location: %s"},
-  {"COPYRIGHT=", "Copyright %s"},
-  {NULL, NULL}
-};
-
-struct option long_options[] = {
-  /* GNU standard options */
-    {"help", no_argument, 0, 'h'},
-    {"version", no_argument, 0, 'V'},
-    /* ogg123-specific options */
-    {"buffer", required_argument, 0, 'b'},
-    {"config", optional_argument, 0, 'c'},
-    {"device", required_argument, 0, 'd'},
-    {"file", required_argument, 0, 'f'},
-    {"skip", required_argument, 0, 'k'},
-    {"delay", required_argument, 0, 'l'},
-    {"device-option", required_argument, 0, 'o'},
-    {"prebuffer", required_argument, 0, 'p'},
-    {"quiet", no_argument, 0, 'q'},
-    {"save", required_argument, 0, 's'},
-    {"verbose", no_argument, 0, 'v'},
-    {"nth", required_argument, 0, 'x'},
-    {"ntimes", required_argument, 0, 'y'},
-    {"shuffle", no_argument, 0, 'z'},
-    {0, 0, 0, 0}
-};
-
-
-/* ------------------------ configuration interface ----------------------- */
-
-int ConfigErrorFunc (void *arg, ParseCode pcode, int lineno, 
-		       const char *filename, char *line)
-{
-  if (pcode == parse_syserr)
-    {
-      if (errno != EEXIST && errno != ENOENT)
-	perror ("System error");
-      return -1;
-    }
-  else
-    {
-      Error ("=== Parse error: %s on line %d of %s (%s)\n", ParseErr(pcode), 
-	     lineno, filename, line);
-      return 0;
-    }
-}
-
-ParseCode ReadConfig (Option_t opts[], const char *filename)
-{
-  return ParseFile (opts, filename, ConfigErrorFunc, NULL);
-}
-
-void ReadStdConfigs (Option_t opts[])
-{
-  char filename[FILENAME_MAX];
-  char *homedir = getenv("HOME");
-
-  /* Read config from files in same order as original parser */
-  ReadConfig (opts, "/etc/ogg123rc");
-  if (homedir && strlen(homedir) < FILENAME_MAX - 10) {
-    /* Try ~/.ogg123 */
-    strncpy (filename, homedir, FILENAME_MAX);
-    strcat (filename, "/.ogg123rc");
-    ReadConfig (opts, filename);
-  }
-}
-
-/* ---------------------------- status interface -----------------------  */
-
-/* string temporary data (from the data segment) */
-char TwentyBytes1[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-char TwentyBytes2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-char TwentyBytes3[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
-
-void InitStats (Stat_t stats[])
-{
-  Stat_t *cur;
-
-  cur = &stats[0]; /* currently playing file / stream */
-  cur->prio = 3; cur->enabled = 0; cur->formatstr = "File: %s"; 
-  cur->type = stat_stringarg;
-  
-  cur = &stats[1]; /* current playback time (preformatted) */
-  cur->prio = 1; cur->enabled = 1; cur->formatstr = "Time: %s"; 
-  cur->type = stat_stringarg; cur->arg.stringarg = TwentyBytes1;
-
-  cur = &stats[2]; /* remaining playback time (preformatted) */
-  cur->prio = 1; cur->enabled = 0; cur->formatstr = "%s";
-  cur->type = stat_stringarg; cur->arg.stringarg = TwentyBytes2;
-
-  cur = &stats[3]; /* total playback time (preformatted) */
-  cur->prio = 1; cur->enabled = 0; cur->formatstr = "of %s";
-  cur->type = stat_stringarg; cur->arg.stringarg = TwentyBytes3;
-
-  cur = &stats[4]; /* instantaneous bitrate */
-  cur->prio = 2; cur->enabled = 1; cur->formatstr = "Bitrate: %5.1f";
-  cur->type = stat_doublearg;
-
-  cur = &stats[5]; /* average bitrate (not yet implemented) */
-  cur->prio = 2; cur->enabled = 0; cur->formatstr = "Avg bitrate: %5.1f";
-  cur->type = stat_doublearg;
-
-  cur = &stats[6]; /* input buffer fill % */
-  cur->prio = 2; cur->enabled = 0; cur->formatstr = " Input Buffer %5.1f%%";
-  cur->type = stat_doublearg;
-
-  cur = &stats[7]; /* input buffer status */
-  cur->prio = 2; cur->enabled = 0; cur->formatstr = "%s";
-  cur->type = stat_stringarg; cur->arg.stringarg = NULL;
-
-  cur = &stats[8]; /* output buffer fill % */
-  cur->prio = 2; cur->enabled = 0; cur->formatstr = " Output Buffer %5.1f%%"; 
-  cur->type = stat_doublearg;
-
-  cur = &stats[9]; /* output buffer status */
-  cur->prio = 1; cur->enabled = 0; cur->formatstr = "%s";
-  cur->type = stat_stringarg; cur->arg.stringarg = NULL;
-
-  cur = &stats[10]; /* End flag */
-  cur->formatstr = NULL;
-}
-
-void SetTime (Stat_t stats[], ogg_int64_t sample)
-{
-  double CurTime = (double) sample / (double) Options.outputOpts.rate;
-  long c_min = (long) CurTime / (long) 60;
-  double c_sec = CurTime - 60.0f * c_min;
-  long r_min, t_min;
-  double r_sec, t_sec;
-
-  if (stats[2].enabled && Options.inputOpts.seekable) {
-    if (sample > Options.inputOpts.totalSamples) {
-      /* file probably grew while playing; update total time */
-      Options.inputOpts.totalSamples = sample;
-      Options.inputOpts.totalTime = CurTime;
-      stats[3].arg.stringarg[0] = '\0';
-      r_min = 0;
-      r_sec = 0.0f;
-    } else {
-      r_min = (long) (Options.inputOpts.totalTime - CurTime) / (long) 60;
-      r_sec = ((double) Options.inputOpts.totalTime - CurTime) - 60.0f * (double) r_min;
-    }
-    sprintf (stats[2].arg.stringarg, "[%02li:%05.2f]", r_min, r_sec);
-    if (stats[3].arg.stringarg[0] == '\0') {
-      t_min = (long) Options.inputOpts.totalTime / (long) 60;
-      t_sec = Options.inputOpts.totalTime - 60.0f * t_min;
-      sprintf (stats[3].arg.stringarg, "%02li:%05.2f", t_min, t_sec);
-    }    
-  }
-  sprintf (stats[1].arg.stringarg, "%02li:%05.2f", c_min, c_sec);
-}
-
-void SetStateString (buf_t *buf, char *strbuf)
-{
-  char *cur = strbuf;
-  char *comma = ", ";
-  char *sep = "(";
-
-  if (buf->prebuffering) {
-    cur += sprintf (cur, "%sPrebuf", sep);
-    sep = comma;
-  }
-  if (buf->paused) {
-    cur += sprintf (cur, "%sPaused", sep);
-    sep = comma;
-  }
-  if (buf->eos) {
-    cur += sprintf (cur, "%sEOS", sep);
-    sep = comma;
-  }
-  if (cur != strbuf)
-    cur += sprintf (cur, ")");
-  else
-    *cur = '\0';
-}
-
-
-void UpdateStats (void)
-{
-  char strbuf[80];
-  Stat_t *stats = Options.statOpts.stats;
-
-  /* Updating stats is not critical.  If another thread is already doing it,
-     we skip it. */
-  if (pthread_mutex_trylock(&stats_lock) == 0)
-    {
-      memset (strbuf, 0, 80);
-      if (Options.inputOpts.data) {
-	SetStateString (Options.inputOpts.data->buf, strbuf);
-	stats[6].arg.doublearg = 
-	  (double) buffer_full(Options.inputOpts.data->buf)
-	  / (double) Options.inputOpts.data->buf->size * 100.0f;
-      }
-
-      stats[7].arg.stringarg = strdup (strbuf);
-      
-      memset (strbuf, 0, 80);
-      if (Options.outputOpts.buffer)
-	{
-	  SetStateString (Options.outputOpts.buffer, strbuf);
-	  stats[8].arg.doublearg =
-	    (double) buffer_full(Options.outputOpts.buffer) 
-	    / (double) Options.outputOpts.buffer->size * 100.0f;
-	}
-
-      if (stats[9].arg.stringarg)
-	free (stats[9].arg.stringarg);
-      stats[9].arg.stringarg = strdup (strbuf);
-      
-      PrintStatsLine (stats);
 
-      pthread_mutex_unlock(&stats_lock);
-    }
-
-}
-
+#include "ogg123.h"
 
-/* ------------------------ buffer interface ---------------------------- */
+void exit_cleanup ();
+void play (char *source_string);
 
+/* take buffer out of the data segment, not the stack */
+#define AUDIO_CHUNK_SIZE 4096
+unsigned char convbuffer[AUDIO_CHUNK_SIZE];
+int convsize = AUDIO_CHUNK_SIZE;
 
-size_t OutBufferWrite(void *ptr, size_t size, size_t nmemb, void *arg,
-		      char iseos)
-{
-  size_t origSize;
+ogg123_options_t options;
+stat_t *stats;
+buf_t *audio_buffer;
 
-  origSize = size;
-  size *= nmemb;
-  
-  Options.outputOpts.cursample += 
-    Options.playOpts.nth * size / Options.outputOpts.channels / 2 
-    / Options.playOpts.ntimes; /* locked to 16-bit */
+audio_play_arg_t audio_play_arg;
 
-  devices_write (ptr, size, 1, Options.outputOpts.devices);
 
-  SetTime (Options.statOpts.stats, Options.outputOpts.cursample);
-  UpdateStats();
+/* ------------------------- config file options -------------------------- */
 
-  return origSize;
-}
-
+/* This macro is used to create some dummy variables to hold default values
+   for the options. */
+#define INIT(type, value) type type##_##value = value
+char char_n = 'n';
+float float_50f = 50.0f;
+float float_0f = 0.0f;
+INIT(int, 10000);
+INIT(int, 1);
+INIT(int, 0);
 
-/* ---------------------- signal handler functions ---------------------- */
+file_option_t file_opts[] = {
+  /* found, name, description, type, ptr, default */
+  {0, "default_device", "default output device", opt_type_string,
+   &options.default_device, NULL},
+  {0, "shuffle",        "shuffle playlist",      opt_type_bool,
+   &options.shuffle,        &int_0},
+  {0, "verbose",        "verbosity level",       opt_type_int,
+   &options.verbosity,        &int_1},
+  {0, "outbuffer",      "out buffer size (kB)",  opt_type_int,
+   &options.buffer_size, &int_0},
+  {0, "outprebuffer",   "out prebuffer (%)",     opt_type_float,
+   &options.prebuffer,   &float_0f},
+  {0, NULL,             NULL,                    0,               NULL,                NULL}
+};
 
 
-/* Two signal handlers, one for SIGINT, and the second for
- * SIGALRM.  They are de/activated on an as-needed basis by the
- * player to allow the user to stop ogg123 or skip songs.
- */
-void SignalSkipfile(int which_signal)
-{
-  sig_request.skipfile = 1;
+/* Flags set by the signal handler to control the threads */
+signal_request_t sig_request = {0, 0, 0, 0};
 
-  /* libao, when writing wav's, traps SIGINT so it correctly
-   * closes things down in the event of an interrupt.  We
-   * honour this.   libao will re-raise SIGINT once it cleans
-   * up properly, causing the application to exit.  This is 
-   * desired since we would otherwise re-open output.wav 
-   * and blow away existing "output.wav" file.
-   */
 
-  signal (SIGINT, SigHandler);
-}
+/* ------------------------------- signal handler ------------------------- */
 
-void SignalActivateSkipfile(int ignored)
-{
-  signal(SIGINT,SignalSkipfile);
-}
 
-void SigHandler (int signo)
+void signal_handler (int signo)
 {
   switch (signo) {
+  case SIGALRM:
+    sig_request.ticks++;
+    alarm(1);
+    break;
+
   case SIGINT:
-    sig_request.exit = 1;
+    if (sig_request.ticks < options.delay)
+      sig_request.exit = 1;
+    else
+      sig_request.skipfile = 1;
+    break;
+
   case SIGTSTP:
     sig_request.pause = 1;
     /* buffer_Pause (Options.outputOpts.buffer);
@@ -354,776 +119,283 @@
        }
     */
     break;
+
   case SIGCONT:
-    break;
+    break;  /* Don't need to do anything special to resume */
+
   default:
     psignal (signo, "Unknown signal caught");
   }
 }
-
-/* ------------------Miscellaneous Helper Functions --------------------- */
-
-int IsURL (char *str)
-{
-  int tmp;
-
-  tmp = strchr(str, ':') - str;
-  return tmp < 10 && tmp + 2 < strlen(str) && !strncmp(str + tmp, "://", 3);
-}
 
-/* --------------------------- Main code -------------------------------- */
+/* -------------------------- util functions ---------------------------- */
 
-
-void usage(void)
+void options_init (ogg123_options_t *opts)
 {
-  int i, driver_count;
-  ao_info **devices = ao_driver_info_list(&driver_count);
-
-  printf ( 
-         "Ogg123 from " PACKAGE " " VERSION "\n"
-	 " by Kenneth Arnold <kcarnold at arnoldnet.net> and others\n\n"
-	 "Usage: ogg123 [<options>] <input file> ...\n\n"
-	 "  -h, --help     this help\n"
-	 "  -V, --version  display Ogg123 version\n"
-	 "  -d, --device=d uses 'd' as an output device\n"
-	 "      Possible devices are ('*'=live, '@'=file):\n"
-	 "        ");
-  
-  for(i = 0; i < driver_count; i++) {
-    printf ("%s", devices[i]->short_name);
-    if (devices[i]->type == AO_TYPE_LIVE)
-      printf ("*");
-    else if (devices[i]->type == AO_TYPE_FILE)
-      printf ("@");
-    printf (" ");
-  }
-
-  printf ("\n");
-  
-  printf (
-	 "  -f, --file=filename  Set the output filename for a previously\n"
-	 "      specified file device (with -d).\n"
-	 "  -k n, --skip n  Skip the first 'n' seconds\n"
-	 "  -o, --device-option=k:v passes special option k with value\n"
-	 "      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 status information\n"
-	 "  -q, --quiet    don't display anything (no title)\n"
-	 "  -z, --shuffle  shuffle play\n"
-	 "\n"
-	 "ogg123 will skip to the next song on SIGINT (Ctrl-C) after s seconds after\n"
-	 "song start.\n"
-	 "  -l, --delay=s  set s (default 1). If s=-1, disable song skip.\n");
+  opts->verbosity = 1;
+  opts->shuffle = 0;
+  opts->delay = 2;
+  opts->nth = 1;
+  opts->ntimes = 1;
+  opts->seekpos = 0.0;
+  opts->buffer_size = 0;
+  opts->prebuffer = 0.0f;
+  opts->default_device = NULL;
+  opts->devices = NULL;
 }
 
+/* --------------------------- main code -------------------------------- */
 
-#define INIT(type, value) type type##_##value = value
-char char_n = 'n';
-float float_50f = 50.0f;
-float float_0f = 0.0f;
-INIT(int, 10000);
-INIT(int, 1);
-INIT(int, 0);
 
 
 int main(int argc, char **argv)
 {
   int ret;
-  int option_index = 1;
-  ao_option *temp_options = NULL;
-  ao_option ** current_options = &temp_options;
-  ao_info *info;
-  int temp_driver_id = -1;
-  devices_t *current;
-
-  /* Initialization */
-
-  /* *INDENT-OFF* */
-  Option_t opts[] = {
-    /* found, name, description, type, ptr, default */
-    {0, "default_device", "default output device", opt_type_string, &Options.outputOpts.default_device, NULL},
-    {0, "shuffle",        "shuffle playlist",      opt_type_char,   &Options.playOpts.shuffle,        &char_n},
-    {0, "verbose",        "verbosity level",       opt_type_int,    &Options.statOpts.verbose,        &int_1},
-    {0, "outbuffer",      "out buffer size (kB)",  opt_type_int,    &Options.outputOpts.BufferSize, &int_0},
-    {0, "outprebuffer",   "out prebuffer (%)",     opt_type_float,  &Options.outputOpts.Prebuffer,   &float_0f},
-    {0, "inbuffer",       "in buffer size (kB)",   opt_type_int,    &Options.inputOpts.BufferSize, &int_10000},
-    {0, "inprebuffer",    "in prebuffer (%)",      opt_type_float,  &Options.inputOpts.Prebuffer, &float_50f},
-    {0, "save_stream",    "append stream to file", opt_type_string, &Options.inputOpts.SaveStream, NULL},
-    {0, "delay",          "skip file delay (sec)", opt_type_int,    &Options.playOpts.delay,          &int_1},
-    {0, NULL,             NULL,                    0,               NULL,                NULL}
-  };
-  /* *INDENT-ON* */
-
-  Options.playOpts.nth = 1;
-  Options.playOpts.ntimes = 1;
-  Options.statOpts.verbose = 1;
+  int optind;
 
+  ao_initialize();
+  stats = stats_create();
+  options_init(&options);
+  file_options_init(file_opts);
 
+  parse_std_configs(file_opts);
+  optind = parse_cmdline_options(argc, argv, &options, file_opts);
 
-  ATEXIT (ExitCleanup);
-  signal (SIGINT, SigHandler);
-  signal (SIGTSTP, SigHandler);
-  signal (SIGCONT, SigHandler);
-  ao_initialize();
+  audio_play_arg.devices = options.devices;
+  audio_play_arg.stats = stats;
 
-  
-  InitStats (Options.statOpts.stats);
-  InitOpts(opts);
 
-  ReadStdConfigs (opts);
+  status_set_verbosity(options.verbosity);
 
-  /* Parse command line options */
-  
-  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qvVx:y:z",
-				  long_options, &option_index)))
-    {
-      switch (ret) 
-	{
-	case 0:
-	  Error ("Internal error: long option given when none expected.\n");
-	  exit(1);
-
-	case 'b':
-	  Options.outputOpts.BufferSize = atoi(optarg) * 1024;
-	  break;
-
-	case 'c':
-	  if (optarg)
-	    {
-	      char *tmp = strdup (optarg);
-	      ParseCode pcode = ParseLine (opts, tmp);
-	      if (pcode != parse_ok)
-		Error ("=== Error \"%s\" while parsing config option from command line.\n"
-		       "=== Option was: %s\n", ParseErr (pcode), optarg);
-	      free (tmp);
-	    }
-	  else
-	    {
-	      /* not using the status interface here */
-	      fprintf (stdout, "Available options:\n");
-	      DescribeOptions (opts, stdout);
-	      exit (0);
-	    }
-	  break;
-	  
-	case 'd':
-	    temp_driver_id = ao_driver_id(optarg);
-	    if (temp_driver_id < 0)
-	      {
-		Error ("=== No such device %s.\n", optarg);
-		exit(1);
-	      }
-	    current = append_device(Options.outputOpts.devices,
-				    temp_driver_id, 
-				    NULL, NULL);
-	    if(Options.outputOpts.devices == NULL)
-	      Options.outputOpts.devices = current;
-	    current_options = &current->options;
-	    break;
-	    
-	case 'f':
-	  if (temp_driver_id >= 0)
-	    {
-	      info = ao_driver_info(temp_driver_id);
-	      if (info->type == AO_TYPE_FILE)
-		{
-		  free(current->filename);
-		  current->filename = strdup(optarg);
-		}
-	      else
-		{
-		  Error ("=== Driver %s is not a file output driver.\n",
-			 info->short_name);
-		  exit(1);
-		}
-	    }
-	  else
-	    {
-	      Error ("=== Cannot specify output file without specifying a driver.\n");
-	      exit (1);
-	    }
-	  break;
-
-	case 'k':
-	    Options.playOpts.seekpos = atof(optarg);
-	    break;
-
-	case 'l':
-	    Options.playOpts.delay = atoi(optarg);
-	    break;
-
-	case 'o':
-	    if (optarg && !add_option(current_options, optarg))
-	      {
-		Error ("=== Incorrect option format: %s.\n", optarg);
-		exit(1);
-	      }
-	    break;
-
-	case 'h':
-	    usage();
-	    exit(0);
-	    break;
-
-	case 'p':
-	  Options.outputOpts.Prebuffer = atof (optarg);
-	  if (Options.outputOpts.Prebuffer < 0.0f || 
-	      Options.outputOpts.Prebuffer > 100.0f)
-	    {
-	      Error ("--- Prebuffer value invalid. Range is 0-100.\n");
-	      Options.outputOpts.Prebuffer = 
-		Options.outputOpts.Prebuffer < 0.0f ? 0.0f : 100.0f;
-	    }
-	  break;
-
-	case 'q':
-	  Options.statOpts.verbose = 0;
-	  break;
-
-	case 'v':
-	  Options.statOpts.verbose++;
-	  break;
-
-	case 'V':
-	  Error ("Ogg123 from " PACKAGE " " VERSION "\n");
-	  exit(0);
-	  break;
-
-	case 'x':
-	  Options.playOpts.nth = atoi (optarg);
-	  if (Options.playOpts.nth == 0)
-	    {
-	      Error ("--- Cannot play every 0th chunk!\n");
-	      Options.playOpts.nth = 1;
-	    }
-	  break;
-	  
-	case 'y':
-	  Options.playOpts.ntimes = atoi (optarg);
-	  if (Options.playOpts.ntimes == 0)
-	    {
-	      Error ("--- Cannot play every chunk 0 times.\n"
-		     "--- To do a test decode, use the null output driver.\n");
-	      Options.playOpts.ntimes = 1;
-	    }
-	  break;
-
-	case 'z':
-	  Options.playOpts.shuffle = 1;
-	  break;
-
-	case '?':
-	  break;
-
-	default:
-	  usage();
-	  exit(1);
-	}
-    }
-  
+  /* Setup signal handlers and callbacks */
+
+  ATEXIT (exit_cleanup);
+  signal (SIGINT, signal_handler);
+  signal (SIGTSTP, signal_handler);
+  signal (SIGCONT, signal_handler);
+  signal (SIGALRM, signal_handler);
+
   /* Do we have anything left to play? */
-  if (optind == argc)
-    {
-      usage();
-      exit(1);
-    }
-  
-  SetPriority (Options.statOpts.verbose);
+  if (optind == argc) {
+    cmdline_usage();
+    exit(1);
+  }
   
-  /* Add last device to device list or use the default device */
-  if (temp_driver_id < 0)
-    {
-      /* First try config file setting */
-      if (Options.outputOpts.default_device)
-	{
-	  temp_driver_id = ao_driver_id (Options.outputOpts.default_device);
-	  if (temp_driver_id < 0)
-	    Error ("--- Driver %s specified in configuration file invalid.\n",
-		   Options.outputOpts.default_device);
-	}
-      
-      /* Then try libao autodetect */
-      if (temp_driver_id < 0)
-	temp_driver_id = ao_default_driver_id();
-
-      /* Finally, give up */
-      if (temp_driver_id < 0)
-	{
-	  Error ("=== Could not load default driver and no driver specified in config file. Exiting.\n");
-	  exit(1);
-	}
+  /* Setup buffer */ 
+  if (options.buffer_size > 0) {
+    audio_buffer = buffer_create(options.buffer_size,
+				 options.buffer_size * options.prebuffer,
+				 audio_play_callback, &audio_play_arg,
+				 AUDIO_CHUNK_SIZE);
+    if (audio_buffer == NULL) {
+      status_error("Error: Could not create audio buffer.\n");
+      exit(1);
     }
+  } else
+    audio_buffer = NULL;
 
-  Options.outputOpts.devices = append_device(Options.outputOpts.devices,
-					     temp_driver_id, temp_options, 
-					     NULL);
 
-  /* Setup buffer */ 
-  if (Options.outputOpts.BufferSize)
-    {
-      Options.outputOpts.Prebuffer = Options.outputOpts.Prebuffer * 
-	(float) Options.outputOpts.BufferSize / 100.0f;
-      Options.outputOpts.buffer = buffer_create(Options.outputOpts.BufferSize, (int) Options.outputOpts.Prebuffer, NULL, (pWriteFunc) OutBufferWrite, NULL, NULL, 4096);
-      Options.statOpts.stats[8].enabled = 1;
-      Options.statOpts.stats[9].enabled = 1;
-    }
-  
   /* Shuffle playlist */
-  if (Options.playOpts.shuffle == 'n' || Options.playOpts.shuffle == 'N')
-    Options.playOpts.shuffle = 0;
-  else if (Options.playOpts.shuffle == 'y' || Options.playOpts.shuffle == 'Y')
-    Options.playOpts.shuffle = 1;
-
-  if (Options.playOpts.shuffle)
-    {
-      int i;
-      int range = argc - optind;
-      
-      srandom(time(NULL));
-      
-      for (i = optind; i < argc; i++) {
-	int j = optind + random() % range;
-	char *temp = argv[i];
-	argv[i] = argv[j];
-	argv[j] = temp;
-      }
+  if (options.shuffle) {
+    int i;
+    
+    srandom(time(NULL));
+    
+    for (i = optind; i < argc; i++) {
+      int j = optind + random() % (argc - i);
+      char *temp = argv[i];
+      argv[i] = argv[j];
+      argv[j] = temp;
     }
-
+  }
+  
 
   /* Play the files/streams */
 
-  while (optind < argc)
-    {
-      Options.playOpts.read_file = argv[optind];
-      PlayFile();
-      optind++;
-    }
-  
-    exit (0);
+  while (optind < argc) {
+    play(argv[optind]);
+    optind++;
+  }
+
+
+  exit (0);
 }
 
 
-void PlayFile()
+void play (char *source_string)
 {
-  OggVorbis_File vf;
-  int current_section = -1, eof = 0, eos = 0, ret;
-  int old_section = -1;
-  int is_big_endian = ao_is_big_endian();
-  double realseekpos = Options.playOpts.seekpos;
-  int nthc = 0, ntimesc = 0;
-  long bitrate;
-  int mono2stereo = 0;
-  ogg_int16_t *src, *dest;
-  int c;
-  int count;
-  ov_callbacks VorbisfileCallbacks;
-  
-  /* Setup callbacks and data structures for HTTP stream or file */
-  if (IsURL(Options.playOpts.read_file))
-    {
-      ShowMessage (1, 0, 1, "Stream: %s", Options.playOpts.read_file);
-      VorbisfileCallbacks.read_func = StreamBufferRead;
-      VorbisfileCallbacks.seek_func = StreamBufferSeek;
-      VorbisfileCallbacks.close_func = StreamBufferClose;
-      VorbisfileCallbacks.tell_func = StreamBufferTell;
-      
-      Options.inputOpts.URL = Options.playOpts.read_file;
-      Options.inputOpts.data = InitStream (Options.inputOpts);
-      if ((ov_open_callbacks (Options.inputOpts.data, &vf, NULL, 0, VorbisfileCallbacks)) < 0) {
-	Error ("=== Input not an Ogg Vorbis audio stream.\n");
-	return;
-      }
-      Options.statOpts.stats[6].enabled = 1;
-      Options.statOpts.stats[7].enabled = 1;
-    }
-  else
-    {
-      FILE *InStream;
-
-      if (strcmp(Options.playOpts.read_file, "-"))
-	{
-
-	  ShowMessage (1, 0, 1, "File: %s", Options.playOpts.read_file);
-	  /* Open the file. */
-	  if ((InStream = fopen(Options.playOpts.read_file, "rb")) == NULL)
-	    {
-	      perror ("=== Error opening input file");
-	      return;
-	    }
-	  
-	}
-      else
-	{
+  transport_t *transport;
+  format_t *format;
+  data_source_t *source;
+  decoder_t *decoder;
 
-	  ShowMessage (1, 0, 1, "-=( Standard Input )=- ");
-	  InStream = stdin;
+  /* Preserve between calls so we only open the audio device when we 
+     have to */
+  static audio_format_t old_audio_fmt = { 0, 0, 0, 0, 0 };
+  audio_format_t new_audio_fmt;
+  audio_reopen_arg_t *reopen_arg;
 
-	}
+  int eof = 0, eos = 0, ret;
+  long bitrate;
 
-      if ((ov_open (InStream, &vf, NULL, 0)) < 0)
-	{
-	  Error ("=== Input not an Ogg Vorbis audio stream.\n");
-	  return;
-	}
-    }
-  
-  /* Setup so that pressing ^C in the first second of playback
-   * interrupts the program, but after the first second, skips
-   * the song.  This functionality is similar to mpg123's abilities. */
+  int nthc = 0, ntimesc = 0;
+
+  new_audio_fmt.big_endian = ao_is_big_endian();
+  new_audio_fmt.signed_sample = 1;
+  new_audio_fmt.word_size = 2;
+
+  /* Locate and use transport for this data source */  
+  if ( (transport = select_transport(source_string)) == NULL ) {
+    status_error("No module could be found to read from %s.\n", source_string);
+    return;
+  }
   
-  if (Options.playOpts.delay > 0) {
-    sig_request.skipfile = 0;
-    signal(SIGALRM, SignalActivateSkipfile);
-    alarm(Options.playOpts.delay);
+  if ( (source = transport->open(source_string)) == NULL ) {
+    status_error("Cannot open %s.\n", source_string);
+    return;
   }
+
+  /* Detect the file format and initialize a decoder */
+  if ( (format = select_format(source)) == NULL ) {
+    status_error("The file format of %s is not supported.\n", source_string);
+    return;
+  }
   
-  sig_request.exit = 0;
-  sig_request.pause = 0;
+  if ( (decoder = format->init(source, &new_audio_fmt, NULL, NULL)) == NULL ) {
+    status_error("Error opening %s using the %s module."
+		 "  The file may be corrupted.\n", source_string,
+		 format->name);
+    return;
+  }
+
 
+  /* Reset all of the signal flags and setup the timer */
+  sig_request.skipfile = 0;
+  sig_request.exit     = 0;
+  sig_request.pause    = 0;
+  sig_request.ticks    = 0;
+  alarm(1); /* Count seconds */
+
+
+  /* Start the audio playback thread before we begin sending data */    
+  if (audio_buffer != NULL) {
+    
+    /* First reset mutexes and other synchronization variables */
+    buffer_reset (audio_buffer);
+    buffer_thread_start (audio_buffer);
+  }
+
+
   /* Main loop:  Iterates over all of the logical bitstreams in the file */
   while (!eof && !sig_request.exit) {
     int i;
-    vorbis_comment *vc;
-    vorbis_info *vi;
     
-    /* Read header info */
-    vc = ov_comment(&vf, -1);
-    vi = ov_info(&vf, -1);
-    Options.outputOpts.rate = vi->rate;
-    Options.outputOpts.channels = vi->channels;
-
-    if (Options.outputOpts.channels == 1) {
-	    mono2stereo = 1;
-	    Options.outputOpts.channels = 2;
-    }
-
-    /* Open audio device before we read and output comments.  We have
-       to do this inside the loop in order to deal with chained
-       bitstreams with different sample rates */
-    if(OpenAudioDevices() < 0)
-      exit(1);
-    
-    /* Read and display comments */
-    for (i = 0; i < vc->comments; i++)
-      {
-	char *cc = vc->user_comments[i];	/* current comment */
-	int j;
-	
-	for (j = 0; ogg_comment_keys[j].key != NULL; j++)
-	  if ( !strncasecmp (ogg_comment_keys[j].key, cc,
-			     strlen(ogg_comment_keys[j].key)) )
-	    {
-	      ShowMessage (1, 0, 1, ogg_comment_keys[j].formatstr,
-			   cc + strlen(ogg_comment_keys[j].key));
-	      break;
-	    }
-	
-	if (ogg_comment_keys[j].key == NULL)
-	  ShowMessage (1, 0, 1, "Unrecognized comment: '%s'", cc);
+    /* Loop through data within a logical bitstream */
+    eos = 0;    
+    while (!eos && !sig_request.exit) {
+      
+      /* Check signals */
+      if (sig_request.skipfile) {
+	eof = eos = 1;
+	break;
       }
-    
-    ShowMessage (3, 0, 1, "Version is %d", vi->version);
-    ShowMessage (3, 0, 1, 
-		 "Bitrate Hints: upper=%ld nominal=%ld lower=%ld window=%ld",
-		 vi->bitrate_upper, vi->bitrate_nominal, vi->bitrate_lower,
-		 vi->bitrate_window);
-    ShowMessage (2, 0, 1, "Bitstream is %d channel, %ldHz",
-		 vi->channels, vi->rate);
-    ShowMessage (2, 0, 1, "Encoded by: %s", vc->vendor);
-    
 
-    /* If we're seekable, then setup stats accordingly */
-    if (ov_seekable (&vf)) {
-      if ((realseekpos > ov_time_total(&vf, -1)) || (realseekpos < 0))
-	/* If we're out of range set it to right before the end. If we
-	 * set it right to the end when we seek it will go to the
-	 * beginning of the song */
-	realseekpos = ov_time_total(&vf, -1) - 0.01;
-      
-	Options.inputOpts.seekable = 1;
-	Options.inputOpts.totalTime = ov_time_total(&vf, -1);
-	Options.inputOpts.totalSamples = ov_pcm_total(&vf, -1);
-	Options.statOpts.stats[2].enabled = 1;
-	Options.statOpts.stats[3].enabled = 1;
-	if (Options.statOpts.verbose > 0)
-	  SetTime (Options.statOpts.stats, realseekpos / vi->rate);
+      if (sig_request.pause) {
+	buffer_thread_pause (audio_buffer);
+	kill (getpid(), SIGSTOP); /* We block here until we unpause */
         
-	if (realseekpos > 0)
-	  ov_time_seek(&vf, realseekpos);
-    }
-    else
-      Options.inputOpts.seekable = 0;
-    
+	/* Done pausing */
+	buffer_thread_unpause (audio_buffer);
+	sig_request.pause = 0;
+      }
 
-    /* Reset the sample counter */
-    Options.outputOpts.cursample = 0;
 
-    /* Start the audio playback thread before we begin sending data */    
-    if (Options.outputOpts.buffer) {
+      /* Read another block of audio data */
+      ret = format->read(decoder, convbuffer, convsize, &eos, &new_audio_fmt);
 
-      /* First reset mutexes and other synchronization variables */
-      buffer_sync_reset (Options.outputOpts.buffer);
-      pthread_mutex_destroy (&stats_lock);
-      pthread_mutex_init (&stats_lock, NULL);
+      /* Bail if we need to */
+      if (ret == 0) {
+	eof = eos = 1;
+	break;
+      } else if (ret < 0) {
+	status_error("Error: Decoding failure.\n");
+	break;
+      }
 
-      buffer_thread_start (Options.outputOpts.buffer);
-    }
-    
+      
+      /* Check to see if the audio format has changed */
+      if (!audio_format_equal(&new_audio_fmt, &old_audio_fmt)) {
+	audio_format_copy(&old_audio_fmt, &new_audio_fmt);
+	reopen_arg = new_audio_reopen_arg(options.devices, &new_audio_fmt);
+
+	if (audio_buffer)	  
+	  buffer_action_at_end(audio_buffer, &audio_reopen_callback,
+			       reopen_arg);
+	else
+	  audio_reopen_callback(NULL, reopen_arg);
+      }
+      
 
-    /* Loop through data within a logical bitstream */
-    eos = 0;    
-    while (!eos && !sig_request.exit)
-      {
+      /* Write audio data block to output, skipping or repeating chunks
+	 as needed */
+      do {
         
-	/* Check signals */
-	if (sig_request.skipfile) 
-	  {
-	    eof = eos = 1;
-	    signal(SIGALRM, SignalActivateSkipfile);
-	    alarm(Options.playOpts.delay);
-	    break;
-	  }
-
-	if (sig_request.pause)
-	  {
-	    buffer_thread_pause (Options.outputOpts.buffer);
-	    kill (getpid(), SIGSTOP); /* We block here until we unpause */
-
-	    /* Done pausing */
-	    buffer_thread_unpause (Options.outputOpts.buffer);
-	    sig_request.pause = 0;
-	  }
-
-
-	/* Read another block of audio data */
-	old_section = current_section;
-	if (mono2stereo)
-	  ret =
-	    ov_read(&vf, (char *) convbuffer, sizeof(convbuffer) / 2, is_big_endian,
-		    2, 1, &current_section);
-        else
-	  ret =
-	    ov_read(&vf, (char *) convbuffer, sizeof(convbuffer), is_big_endian,
-		    2, 1, &current_section);
-
-	if (ret == 0) 
-	  {
-	    /* End of file */
-	    eof = eos = 1;
-	  }
-	else if (ret == OV_HOLE)
-	  {
-	    if (Options.statOpts.verbose > 1)
-	      /* we should be able to resync silently; if not there are 
-		 bigger problems. */
-	      Error ("--- Hole in the stream; probably harmless\n");
-	  } 
-	else if (ret < 0) 
-	  {
-	    /* Stream error */
-	    Error ("=== Vorbis library reported a stream error.\n");
-	  } 
-	else 
-	  {
-	    /* did we enter a new logical bitstream */
-	    if (old_section != current_section && old_section != -1)
-	      eos = 1;
-	  }
-	
-
-	/* Update bitrate display */
-	bitrate = ov_bitrate_instant (&vf);
-	if (bitrate == OV_FALSE) {
-	  /* Not playing */
-	  Options.statOpts.stats[4].arg.doublearg = 0.0f;
-	} else if (bitrate > 0) {
-	  /* New bitrate information to report */
-	  Options.statOpts.stats[4].arg.doublearg = 
-	    (double) bitrate  / 1000.0f;
+	if (nthc-- == 0) {
+	  if (audio_buffer) {
+	    buffer_submit_data(audio_buffer, convbuffer, ret);
+	    status_print_statistics(stats);
+	  } else
+	    audio_play_callback(convbuffer, ret, eos, &audio_play_arg);
+	  
+	  nthc = options.nth - 1;
         }
+	
+      } while (++ntimesc < options.ntimes);
 
+      ntimesc = 0;
+      
+      /* Update bitrate display */
+      bitrate = format->instant_bitrate (decoder);
+      if (bitrate > 0)
+	stats[4].arg.doublearg = (double) bitrate  / 1000.0f;
+      
+    } /* End of data loop */
+    
+  
+    if (audio_buffer != NULL)
+      buffer_mark_eos(audio_buffer);
+  } /* End of logical bitstream loop */
+  
 
-	/* Write audio data block to output, skipping or repeating chunks
-	   as needed */
-	if (ret > 0)
-	  {
-	    if (mono2stereo) {
-	      count = sizeof(convbuffer) / 4;
-	      src = (ogg_int16_t *)convbuffer;
-	      dest = (ogg_int16_t *)convbuffer2;
-	      for (c = 0; c < count; c++) {
-		*dest++ = *src;
-		*dest++ = *src++;
-	      }
-	      memcpy(convbuffer, convbuffer2, sizeof(convbuffer));
-	    }
-
-	    do
-	      {
-		if (nthc-- == 0) 
-		  {
-		    if (Options.outputOpts.buffer)
-		      {
-		        if (mono2stereo)
-			  buffer_submit_data (Options.outputOpts.buffer, 
-				       	      convbuffer, ret*2, 1);
-			else
-			  buffer_submit_data (Options.outputOpts.buffer, 
-				       	      convbuffer, ret, 1);
-			UpdateStats();
-		      }
-		    else
-		      if (mono2stereo)
-		        OutBufferWrite (convbuffer, ret*2, 1, &Options, 0);
-                      else
-			OutBufferWrite (convbuffer, ret, 1, &Options, 0);
-		    
-		    nthc = Options.playOpts.nth - 1;
-		  }
-	      } 
-	    while (++ntimesc < Options.playOpts.ntimes);
-	    ntimesc = 0;
-	  }
-      } /* End of data loop */
+  /* Done playing this logical bitstream.  Now we cleanup output buffer. */
+  if (audio_buffer) {
     
-    /* Done playing this logical bitstream.  Now we cleanup output buffer. */
-    if (Options.outputOpts.buffer)
-      {
-	if (!sig_request.exit && !sig_request.skipfile)
-	  {
-	    buffer_mark_eos (Options.outputOpts.buffer);
-	    buffer_wait_for_empty (Options.outputOpts.buffer);
-	  }
-	
-	buffer_thread_kill (Options.outputOpts.buffer);
-      }
+    if (!sig_request.exit && !sig_request.skipfile)
+      buffer_wait_for_empty(audio_buffer);
     
-  } /* End of logical bitstream loop */
     
-  /* Clean up signals and vorbisfile structures */
-  alarm(0);
-  signal(SIGALRM,SIG_DFL);
-  signal(SIGINT,SigHandler);
+    buffer_thread_kill(audio_buffer);
+  }
   
-  ov_clear(&vf);
+
+  alarm(0);  
+  format->cleanup(decoder);
+  transport->close(source);
   
-  ShowMessage (1, 1, 1, "Done.");
+  status_message(1, "Done.");
   
   if (sig_request.exit)
     exit (0);
 }
 
-
-int OpenAudioDevices()
-{
-  static int prevrate=0, prevchan=0;
-  devices_t *current;
-  ao_sample_format format;
-
-  /* Don't close and reopen devices unless necessary */
-  if (Options.outputOpts.devicesOpen)
-    {
-      if(prevrate == Options.outputOpts.rate && 
-	 prevchan == Options.outputOpts.channels)
-	{
-	  return 0;
-	}
-      else
-	{
-	  close_audio_devices (Options.outputOpts.devices);
-	  Options.outputOpts.devicesOpen = 0;
-	}
-    }
-  
-  /* Record audio device settings and open the devices */
-  format.rate = prevrate = Options.outputOpts.rate;
-  format.channels = prevchan = Options.outputOpts.channels;
-  format.bits = 16;
-  format.byte_format = AO_FMT_NATIVE;
-
-  current = Options.outputOpts.devices;
-  while (current != NULL)
-    {
-      ao_info *info = ao_driver_info(current->driver_id);
-      
-      if (Options.statOpts.verbose > 0)
-	{
-	  ShowMessage (1, 0, 1, "Device:   %s", info->name);
-	  ShowMessage (1, 0, 1, "Author:   %s", info->author);
-	  ShowMessage (1, 0, 1, "Comments: %s\n", info->comment);
-	}
-      
-      if (current->filename == NULL)
-	current->device = ao_open_live(current->driver_id, &format,
-				       current->options);
-      else
-	current->device = ao_open_file(current->driver_id, current->filename,
-				       0, &format, current->options);
-      
-      /* Report errors */
-      if (current->device == NULL)
-	{
-	  switch (errno)
-	    {
-	    case AO_ENODRIVER:
-	      Error ("Error: Device not available.\n");
-	      break;
-	    case AO_ENOTLIVE:
-	      Error ("Error: %s requires an output filename to be specified with -f.\n", info->short_name);
-	      break;
-	    case AO_EBADOPTION:
-	      Error ("Error: Unsupported option value to %s device.\n",
-		     info->short_name);
-	      break;
-	    case AO_EOPENDEVICE:
-	      Error ("Error: Cannot open device %s.\n",
-		     info->short_name);
-	      break;
-	    case AO_EFAIL:
-	      Error ("Error: Device failure.\n");
-	      break;
-	    case AO_ENOTFILE:
-	      Error ("Error: An output file cannot be given for %s device.\n", info->short_name);
-	      break;
-	    case AO_EOPENFILE:
-	      Error ("Error: Cannot open file %s for writing.\n",
-		     current->filename);
-	      break;
-	    case AO_EFILEEXISTS:
-	      Error ("Error: File %s already exists.\n", current->filename);
-	      break;
-	    default:
-	      Error ("Error: This error should never happen.  Panic!\n");
-	      break;
-	    }
-	 
-	  /* We cannot recover from any of these errors */
-	  return -1;
-	}
-
-      current = current->next_device;
-    }
-  
-  Options.outputOpts.devicesOpen = 1;
-  return 0;
-}
 
-void ExitCleanup ()
+void exit_cleanup ()
 {
-  if (Options.inputOpts.data)
-    {
-      StreamCleanup (Options.inputOpts.data);
-      Options.inputOpts.data = NULL;
-    }
       
-  if (Options.outputOpts.buffer)
-    {
-      buffer_destroy (Options.outputOpts.buffer);
-      Options.outputOpts.buffer = NULL;
-    }
+  if (audio_buffer != NULL) {
+    buffer_destroy (audio_buffer);
+    audio_buffer = NULL;
+  }
 
-  ao_onexit (Options.outputOpts.devices);
-  Options.outputOpts.devicesOpen = 0;
+  ao_onexit (options.devices);
 }

1.7.2.12.2.5 +24 -57    vorbis-tools/ogg123/ogg123.h

Index: ogg123.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.h,v
retrieving revision 1.7.2.12.2.4
retrieving revision 1.7.2.12.2.5
diff -u -r1.7.2.12.2.4 -r1.7.2.12.2.5
--- ogg123.h	2001/11/23 05:15:30	1.7.2.12.2.4
+++ ogg123.h	2001/12/08 23:59:25	1.7.2.12.2.5
@@ -11,27 +11,23 @@
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: ogg123.h,v 1.7.2.12.2.4 2001/11/23 05:15:30 volsung Exp $
+ last mod: $Id: ogg123.h,v 1.7.2.12.2.5 2001/12/08 23:59:25 volsung Exp $
 
  ********************************************************************/
 
-#ifndef __OGG123_H
-#define __OGG123_H
+#ifndef __OGG123_H__
+#define __OGG123_H__
 
-/* Common includes */
-#include <ogg/ogg.h>
-#include <vorbis/codec.h>
-#include <vorbis/vorbisfile.h>
-#include <ao/ao.h>
+#include <ogg/os_types.h>
+#include "audio.h"
+#include "status.h"
+
+/* Compatibility fixes */
 
 #ifdef __sun
 #include <alloca.h>
 #endif
 
-#include "ao_interface.h"
-#include "curl_interface.h"
-#include "status.h"
-
 /* SunOS 4 does on_exit() and everything else does atexit() */
 #ifdef HAVE_ATEXIT
 #define ATEXIT(x) (atexit(x))
@@ -40,61 +36,32 @@
 #define ATEXIT(x) (on_exit( (void (*)(int, void*))(x) , NULL)
 #else
 #define ATEXIT(x)
-#warning "at_exit() and on_exit() not present.  Bad things may happen when the application terminates."
+#warning "Neither atexit() nor on_exit() is present.  Bad things may happen when the application terminates."
 #endif
 #endif
  
+
+typedef struct ogg123_options_t {
+  long int verbosity;         /* Verbose output if > 1, quiet if 0 */
 
-typedef struct ogg123_options_s {
-  struct {
-    char *read_file;            /* File to decode */
-    char shuffle;               /* Should we shuffle playing? */
-    double seekpos;             /* Amount to seek by */
-    int delay;                  /* delay for skip to next song */
-    int nth;                    /* Play every nth chunk */
-    int ntimes;                 /* Play every chunk n times */
-  } playOpts;
-  struct {
-    long int verbose;           /* Verbose output if > 1, quiet if 0 */
-    
-    /* Status options:
-     * stats[0] - currently playing file / stream
-     * stats[1] - current playback time
-     * stats[2] - remaining playback time
-     * stats[3] - total playback time
-     * stats[4] - instantaneous bitrate
-     * stats[5] - average bitrate (not yet implemented)
-     * stats[6] - input buffer fill %
-     * stats[7] - input buffer status
-     * stats[8] - output buffer fill %
-     * stats[9] - output buffer status
-     * stats[10] - Null format string to mark end of array
-     */
-    Stat_t stats[11];
-  } statOpts;
-  InputOpts_t inputOpts;
-  struct {
-    buf_t *buffer;
-    long BufferSize;
-    float Prebuffer;
-    ogg_int64_t cursample;
-    int rate, channels;         /* playback params for opening audio devices */
-    char devicesOpen;
-    devices_t *devices;
-    char *default_device;
-  } outputOpts;
+  int shuffle;               /* Should we shuffle playing? */
+  int delay;                  /* delay for skip to next song */
+  int nth;                    /* Play every nth chunk */
+  int ntimes;                 /* Play every chunk n times */
+  double seekpos;             /* Position in file to seek to */
+
+  long buffer_size;           /* Size of audio buffer */
+  float prebuffer;            /* Percent of buffer to fill before playing */
+  char *default_device;       /* Name of default driver to use */
+  audio_device_t *devices;    /* Audio devices to playback to */
+  
 } ogg123_options_t;
 
 typedef struct signal_request_t {
   int skipfile;
   int exit;
   int pause;
+  ogg_int64_t ticks;
 } signal_request_t;
-
-void usage();
-void PlayFile();
-int OpenAudioDevices();
-void SigHandler(int ignored);
-void ExitCleanup();
 
 #endif /* !defined(__OGG123_H) */

1.1.2.7.2.2 +328 -98   vorbis-tools/ogg123/Attic/status.c

Index: status.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/Attic/status.c,v
retrieving revision 1.1.2.7.2.1
retrieving revision 1.1.2.7.2.2
diff -u -r1.1.2.7.2.1 -r1.1.2.7.2.2
--- status.c	2001/10/31 05:38:56	1.1.2.7.2.1
+++ status.c	2001/12/08 23:59:25	1.1.2.7.2.2
@@ -11,142 +11,372 @@
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: status.c,v 1.1.2.7.2.1 2001/10/31 05:38:56 volsung Exp $
+ last mod: $Id: status.c,v 1.1.2.7.2.2 2001/12/08 23:59:25 volsung Exp $
 
  ********************************************************************/
 
-/* status interface */
-
-#define _GNU_SOURCE
-
 #include <stdio.h>
 #include <stdlib.h>
+#include <pthread.h>
+
+#include "buffer.h"
 #include "status.h"
+
+
+int last_line_len = 0;
+int max_verbosity = 0;
+
+pthread_mutex_t output_lock = PTHREAD_MUTEX_INITIALIZER;
+
 
-/* a few globals */
-/* stderr is thread-global, so status vars should be also; caller must
- * ensure that status functions are not called concurrently */
-size_t buflen = 100; /* guess max length to be 100 */
-char *tmpbuf = NULL; /* global so updating quick after size determined */
-int LastLineLen = 0;
-int MaxPrio = 0;
+/* ------------------- Private functions ------------------ */
 
-void ClearLine ()
+void set_buffer_state_string (buf_t *buf, char *strbuf)
 {
-  int c = LastLineLen;
+  char *cur = strbuf;
+  char *comma = ", ";
+  char *sep = "(";
+
+  if (buf->prebuffering) {
+    cur += sprintf (cur, "%sPrebuf", sep);
+    sep = comma;
+  }
+  if (buf->paused) {
+    cur += sprintf (cur, "%sPaused", sep);
+    sep = comma;
+  }
+  if (buf->eos) {
+    cur += sprintf (cur, "%sEOS", sep);
+    sep = comma;
+  }
+  if (cur != strbuf)
+    cur += sprintf (cur, ")");
+  else
+    *cur = '\0';
+}
+
+
+#if 0
+void SetTime (stat_t stats[], ogg_int64_t sample)
+{
+  double CurTime = (double) sample / (double) Options.outputOpts.rate;
+  long c_min = (long) CurTime / (long) 60;
+  double c_sec = CurTime - 60.0f * c_min;
+  long r_min, t_min;
+  double r_sec, t_sec;
+
+  if (stats[2].enabled && Options.inputOpts.seekable) {
+    if (sample > Options.inputOpts.totalSamples) {
+      /* file probably grew while playing; update total time */
+      Options.inputOpts.totalSamples = sample;
+      Options.inputOpts.totalTime = CurTime;
+      stats[3].arg.stringarg[0] = '\0';
+      r_min = 0;
+      r_sec = 0.0f;
+    } else {
+      r_min = (long) (Options.inputOpts.totalTime - CurTime) / (long) 60;
+      r_sec = ((double) Options.inputOpts.totalTime - CurTime) - 60.0f * (double) r_min;
+    }
+    sprintf (stats[2].arg.stringarg, "[%02li:%05.2f]", r_min, r_sec);
+    if (stats[3].arg.stringarg[0] == '\0') {
+      t_min = (long) Options.inputOpts.totalTime / (long) 60;
+      t_sec = Options.inputOpts.totalTime - 60.0f * t_min;
+      sprintf (stats[3].arg.stringarg, "%02li:%05.2f", t_min, t_sec);
+    }    
+  }
+  sprintf (stats[1].arg.stringarg, "%02li:%05.2f", c_min, c_sec);
+}
+#endif
+
+
+void clear_line ()
+{
+  int len;
+
+  len = last_line_len;
+
   fputc('\r', stderr);
-  while (c-- > 0)
+
+  while (len > 0) {
     fputc (' ', stderr);
+    len--;
+  }
+
   fputc ('\r', stderr);
 }
 
-int AppendString (int len, const char *fmt, ...) {
-  va_list ap;
-  int n = -1;
-  size_t size;
 
-  while (n == -1) {
-    size = buflen - len - 1;
-    va_start (ap, fmt);
-    n = vsnprintf (tmpbuf + len, size, fmt, ap);
-    va_end (ap);
-
-    if (n > -1 && n < size)
-      return n;
-    /* otherwise, double the size (likely more efficient) */
-    buflen *= 2;
-    if (!(tmpbuf = realloc (tmpbuf, buflen))) {
-      ClearLine();
-      perror ("malloc");
-      exit (1);
+void print_statistics_line (stat_t stats[])
+{
+  int len = 0;
+  
+  status_clear_line(last_line_len);
+  
+  while (stats->formatstr != NULL) {
+    
+    if (stats->verbosity > max_verbosity || !stats->enabled) {
+      stats++;
+      continue;
+    }
+
+    if (len != 0)
+      len += fprintf(stderr, " ");
+
+    switch (stats->type) {
+    case stat_noarg:
+      len += fprintf(stderr, stats->formatstr);
+      break;
+    case stat_intarg:
+      len += fprintf(stderr, stats->formatstr, stats->arg.intarg);
+      break;
+    case stat_stringarg:
+      len += fprintf(stderr, stats->formatstr, stats->arg.stringarg);
+      break;
+    case stat_floatarg:
+      len += fprintf(stderr, stats->formatstr, stats->arg.floatarg);
+      break;
+    case stat_doublearg:
+      len += fprintf(stderr, stats->formatstr, stats->arg.doublearg);
+      break;
     }
-    n = -1;
+
+    stats++;
   }
-  return 0; /* makes dumb compilers happy */
+
+  last_line_len = len;
 }
+
+
+/* ------------------- Public interface -------------------- */
 
-void PrintStatsLine (Stat_t stats[])
+#define TIME_STR_SIZE 20
+#define STATE_STR_SIZE 25
+#define NUM_STATS 10
+
+stat_t *stats_create ()
 {
-  int len = 0, left;
-  
-  if (tmpbuf == NULL) {
-    tmpbuf = malloc (buflen);
-    if (!tmpbuf) {
-      ClearLine();
-      perror ("malloc");
-      exit (1);
-    }
-    *tmpbuf = '\0';
+  stat_t *stats;
+  stat_t *cur;
+
+  stats = calloc(NUM_STATS + 1, sizeof(stat_t));  /* One extra for end flag */
+  if (stats == NULL) {
+    fprintf(stderr, "Memory allocation error in stats_init()\n");
+    exit(1);
   }
+
+  cur = stats + 0; /* currently playing file / stream */
+  cur->verbosity = 3; 
+  cur->enabled = 0;
+  cur->formatstr = "File: %s"; 
+  cur->type = stat_stringarg;
   
-  while (stats->formatstr != NULL)
-    {
-      if (stats->prio > MaxPrio || !stats->enabled) {
-	stats++;
-	continue;
-      }
-      if (len != 0)
-	len += AppendString(len, " ");
-      else
-	fputc ('\r', stderr);
-      switch (stats->type) {
-      case stat_noarg:
-	len += AppendString (len, stats->formatstr);
-	break;
-      case stat_intarg:
-	len += AppendString (len, stats->formatstr, stats->arg.intarg);
-	break;
-      case stat_stringarg:
-	len += AppendString (len, stats->formatstr, stats->arg.stringarg);
-	break;
-      case stat_floatarg:
-	len += AppendString (len, stats->formatstr, stats->arg.floatarg);
-	break;
-      case stat_doublearg:
-	len += AppendString (len, stats->formatstr, stats->arg.doublearg);
-	break;
-      }
-      stats++;
+  cur = stats + 1; /* current playback time (preformatted) */
+  cur->verbosity = 1;
+  cur->enabled = 1;
+  cur->formatstr = "Time: %s"; 
+  cur->type = stat_stringarg;
+  cur->arg.stringarg = calloc(TIME_STR_SIZE, sizeof(char));
+
+  if (cur->arg.stringarg == NULL) {
+    fprintf(stderr, "Memory allocation error in stats_init()\n");
+    exit(1);
+  }
+
+    
+  cur = stats + 2; /* remaining playback time (preformatted) */
+  cur->verbosity = 1;
+  cur->enabled = 0;
+  cur->formatstr = "%s";
+  cur->type = stat_stringarg;
+  cur->arg.stringarg = calloc(TIME_STR_SIZE, sizeof(char));
+
+  if (cur->arg.stringarg == NULL) {
+    fprintf(stderr, "Memory allocation error in stats_init()\n");
+    exit(1);
+  }
+
+
+  cur = stats + 3; /* total playback time (preformatted) */
+  cur->verbosity = 1;
+  cur->enabled = 0;
+  cur->formatstr = "of %s";
+  cur->type = stat_stringarg;
+  cur->arg.stringarg = calloc(TIME_STR_SIZE, sizeof(char));
+
+  if (cur->arg.stringarg == NULL) {
+    fprintf(stderr, "Memory allocation error in stats_init()\n");
+    exit(1);
+  }
+
+
+  cur = stats + 4; /* instantaneous bitrate */
+  cur->verbosity = 2;
+  cur->enabled = 1;
+  cur->formatstr = "Bitrate: %5.1f";
+  cur->type = stat_doublearg;
+
+  cur = stats + 5; /* average bitrate (not yet implemented) */
+  cur->verbosity = 2;
+  cur->enabled = 0;
+  cur->formatstr = "Avg bitrate: %5.1f";
+  cur->type = stat_doublearg;
+
+  cur = stats + 6; /* input buffer fill % */
+  cur->verbosity = 2;
+  cur->enabled = 0;
+  cur->formatstr = " Input Buffer %5.1f%%";
+  cur->type = stat_doublearg;
+
+  cur = stats + 7; /* input buffer status */
+  cur->verbosity = 2;
+  cur->enabled = 0;
+  cur->formatstr = "%s";
+  cur->type = stat_stringarg;
+  cur->arg.stringarg = calloc(STATE_STR_SIZE, sizeof(char));
+
+  if (cur->arg.stringarg == NULL) {
+    fprintf(stderr, "Memory allocation error in stats_init()\n");
+    exit(1);
+  }
+
+
+  cur = stats + 8; /* output buffer fill % */
+  cur->verbosity = 2;
+  cur->enabled = 0;
+  cur->formatstr = " Output Buffer %5.1f%%"; 
+  cur->type = stat_doublearg;
+
+  cur = stats + 9; /* output buffer status */
+  cur->verbosity = 1;
+  cur->enabled = 0;
+  cur->formatstr = "%s";
+  cur->type = stat_stringarg;
+  cur->arg.stringarg = calloc(STATE_STR_SIZE, sizeof(char));
+
+  if (cur->arg.stringarg == NULL) {
+    fprintf(stderr, "Memory allocation error in stats_init()\n");
+    exit(1);
+  }
+
+
+  cur = stats + 10; /* End flag */
+  cur->formatstr = NULL;
+
+  return stats;
+}
+
+
+void stats_cleanup (stat_t *stats)
+{
+  free(stats[1].arg.stringarg);
+  free(stats[2].arg.stringarg);
+  free(stats[3].arg.stringarg);
+  free(stats[7].arg.stringarg);
+  free(stats[9].arg.stringarg);
+  free(stats);
+}
+
+
+void status_set_verbosity (int verbosity)
+{
+  max_verbosity = verbosity;
+}
+
+
+void status_reset_output_lock ()
+{
+  pthread_mutex_unlock(&output_lock);
+}
+
+
+void status_clear_line ()
+{
+  pthread_mutex_lock(&output_lock);
+
+  clear_line();
+
+  pthread_mutex_unlock(&output_lock);
+}
+
+void status_print_statistics (stat_t *stats)
+{
+
+  /* Updating statistics is not critical.  If another thread is
+     already doing output, we skip it. */
+  if (pthread_mutex_trylock(&output_lock) == 0) {
+
+#if 0
+    if () {
+      set_buffer_state_string(Options.inputOpts.data->buf,
+			      stats[7].arg.stringarg);
+      stats[6].arg.doublearg = 
+	(double) buffer_full(Options.inputOpts.data->buf)
+	/ (double) Options.inputOpts.data->buf->size * 100.0f;
     }
-  left = LastLineLen - fprintf (stderr, "%s", tmpbuf);
-  while (left-- > 0)
-    fputc (' ', stderr);
-  if (LastLineLen || len)
-    fputc ('\r', stderr);
-  LastLineLen = len;
+
+    if (Options.outputOpts.buffer) {
+      set_buffer_state_string(Options.inputOpts.data->buf,
+			      stats[9].arg.stringarg);
+      
+      stats[8].arg.doublearg =
+	(double) buffer_full(Options.outputOpts.buffer) 
+	/ (double) Options.outputOpts.buffer->size * 100.0f;
+    }
+
+    
+    print_statistics_line(stats);
+#endif
+
+    clear_line();
+    last_line_len = fprintf(stderr, "Boing!");
+    
+    pthread_mutex_unlock(&output_lock);
+  }
 }
+
+
 
-void ShowMessage (int prio, char keepLastLine, char addNewline, const char *fmt, ...)
+void status_message (int verbosity, const char *fmt, ...)
 {
   va_list ap;
 
-  if (prio > MaxPrio)
+  if (verbosity > max_verbosity)
     return;
-  if (!keepLastLine)
-    ClearLine();
-  else
-    if (LastLineLen)
-      fputc ('\n', stderr);
+
+  pthread_mutex_lock(&output_lock);
+
+  if (last_line_len != 0)
+    fputc ('\n', stderr);
+
   va_start (ap, fmt);
   vfprintf (stderr, fmt, ap);
   va_end (ap);
-  if (addNewline)
-    fputc ('\n', stderr);
-  LastLineLen = 0;
+
+  fputc ('\n', stderr);
+
+  last_line_len = 0;
+
+  pthread_mutex_unlock(&output_lock);
+
 }
 
-/* a degenerate ShowMessage specifically for spitting out an error. fmt has a newline. */
-void Error (const char *fmt, ...)
+
+void status_error (const char *fmt, ...)
 {
   va_list ap;
+
+  pthread_mutex_lock(&output_lock);
+
+  fputc ('\n', stderr);
+
   va_start (ap, fmt);
-  ClearLine();
   vfprintf (stderr, fmt, ap);
   va_end (ap);
-  LastLineLen = 0;
-}
 
-void SetPriority (int prio)
-{
-  MaxPrio = prio;
+  fputc ('\n', stderr);
+
+  last_line_len = 0;
+
+  pthread_mutex_unlock(&output_lock);
 }
+

1.1.2.4.2.2 +30 -13    vorbis-tools/ogg123/Attic/status.h

Index: status.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/Attic/status.h,v
retrieving revision 1.1.2.4.2.1
retrieving revision 1.1.2.4.2.2
diff -u -r1.1.2.4.2.1 -r1.1.2.4.2.2
--- status.h	2001/10/31 05:38:56	1.1.2.4.2.1
+++ status.h	2001/12/08 23:59:25	1.1.2.4.2.2
@@ -11,19 +11,17 @@
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: status.h,v 1.1.2.4.2.1 2001/10/31 05:38:56 volsung Exp $
+ last mod: $Id: status.h,v 1.1.2.4.2.2 2001/12/08 23:59:25 volsung Exp $
 
  ********************************************************************/
 
-#ifndef __STATUS_H
-#define __STATUS_H
+#ifndef __STATUS_H__
+#define __STATUS_H__
 
 #include <stdarg.h>
 
-/* status interface */
-
 typedef struct {
-  int prio;
+  int verbosity;
   char enabled;
   const char *formatstr;
   enum {
@@ -39,12 +37,31 @@
     float floatarg;
     double doublearg;
   } arg;
-} Stat_t;
+} stat_t;
+
 
-void PrintStatsLine (Stat_t stats[]);
-void ShowMessage (int prio, char keepStatusLine, char addNewline, 
-		  const char *fmt, ...);
-void Error (const char *fmt, ...);
-void SetPriority (int prio);
+/* Status options:
+ * stats[0] - currently playing file / stream
+ * stats[1] - current playback time
+ * stats[2] - remaining playback time
+ * stats[3] - total playback time
+ * stats[4] - instantaneous bitrate
+ * stats[5] - average bitrate (not yet implemented)
+ * stats[6] - input buffer fill %
+ * stats[7] - input buffer status
+ * stats[8] - output buffer fill %
+ * stats[9] - output buffer status
+ * stats[10] - Null format string to mark end of array
+ */
+
+stat_t *stats_create ();
+void stats_cleanup (stat_t *stats);
+
+void status_set_verbosity (int verbosity);
+void status_reset_output_lock ();
+void status_clear_line ();
+void status_print_statistics (stat_t *stats);
+void status_message (int verbosity, const char *fmt, ...);
+void status_error (const char *fmt, ...);
 
-#endif /* __STATUS_H */
+#endif /* __STATUS_H__ */

No                   revision

No                   revision

1.1.2.1   +242 -0    vorbis-tools/ogg123/Attic/audio.c

1.1.2.1   +67 -0     vorbis-tools/ogg123/Attic/audio.h

1.1.2.1   +430 -0    vorbis-tools/ogg123/Attic/cfgfile_options.c

1.1.2.1   +59 -0     vorbis-tools/ogg123/Attic/cfgfile_options.h

1.1.2.1   +276 -0    vorbis-tools/ogg123/Attic/cmdline_options.c

1.1.2.1   +30 -0     vorbis-tools/ogg123/Attic/cmdline_options.h

1.1.2.1   +140 -0    vorbis-tools/ogg123/Attic/file_transport.c

1.1.2.1   +67 -0     vorbis-tools/ogg123/Attic/format.c

1.1.2.1   +70 -0     vorbis-tools/ogg123/Attic/format.h

1.1.2.1   +330 -0    vorbis-tools/ogg123/Attic/oggvorbis_format.c

1.1.2.1   +46 -0     vorbis-tools/ogg123/Attic/transport.c

1.1.2.1   +46 -0     vorbis-tools/ogg123/Attic/transport.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