[vorbis-dev] ogg123 remote interface

Joost Remijn remijnj at eidetica.com
Sat Oct 5 10:44:42 PDT 2002


Hi,

I've been working on ogg123 to see if i could add a remote interface
compatible with the one in mpg123. This remote interface is used mainly by
mp3 players which use mpg123 as their backend.

The reason i did this was that i have recently encoded some of my cd's in
ogg vorbis format but wanted to keep using my favourite player (playmp3list,
http://rucus.ru.ac.za/~urban/projects/playmp3list/).

It took quite a while to get this all done. I took the time to split the
patch up into 18 separate patches. There are some fixes and cleanups
included in all this. I list all patches at the bottom of this e-mail in
the order that they should be used/applies (you might be able to vary the
order but there are no guarantees that this will work). The patches are
all attached to this mail.

There are probably still some bugs in the code but those should only
affect the new features. This should not introduce any bugs in the
current code.

The mpg123 code is under a different license but i asked Michael Hipp (the
author and maintainer) if it was ok to reuse some of his code and put it
under the GPL and he was ok with that. So there should be no problems
there.

If there are any questions i'd be happy to answer them,

Joost Remijn

---------------------------------

remove-globals.patch
  remove the many global variables from ogg123.c
  This only leaves the convbuffer which will be removed by a later
  patch

make-character-pointers-const.patch
  Character pointers which are used as const should be const.
  This was done from gcc warnings which turn up when compiling with
  -Wwrite-strings.

remove-some-compiler-warnings.patch
  Fixes for some compiler warnings. Unused variable buf commented out.
  char * initialized to 0 instead of "". convbuffer made signed (figure
  out if we need to change the function or the variable, i chose the easy
  path here).

remove-shadowed-variables.patch
  Fix some places in the code where variables are "shadow"ed. These
  show up as compiler warnings when compiling with -Wshadow.

buffer-code-cleanup.patch
  Remove unused function free_action. Remove duplicate comment.

make-playlist-array-null-terminated.patch
  Make the playlist NULL terminated. This prevents us from carrying around
  the number of items in the playlist later on.

cfgfile-comment-fix.patch
  fix up a comment

add-pcm-samples-to-decoder-stats.patch
  This adds two fields to decoder_stats_t, total_pcm_samples and
  current_pcm_sample. These fields will be used by the remote interface
  to do (somewhat) reliable seeking.

add-remote-write-action.patch
  This adds two functions, remote_write_action and
  remote_write_action_const, to callbacks.[ch]. These functions are used
  to print out information from the remote interface. The messages are
  then used by the process that started ogg123 with the remote interface.

buffer-execute-last-actions.patch
  execute the last actions which are at the position right after the
  audio data.
  This patch was needed for the remote interface because the "@P 0 EOF"
  message wasn't printed otherwise. I'm not sure if this is safe because
  i don't really understand the intricacies of the buffer code.

ugly-id3-hack.patch
  This is a hack to get id3 information from the oggvorbis comments. This
  code is enclosed in a #if and is by default enabled for now. I did this
  because it really is a bit of an ugly hack and could be done nicer
  (maybe a function which gives acces to the comments, and then the id3
  code could go where it is needed and not in the format).

play-becomes-control-default.patch
  This patch reworks ogg123.c. It moves the play function into the
  function control_default which takes in a playlist instead of one
  source_string. This patch also removes the convbuffer global and
  allocated it with malloc in control_default().
  It looks like a big patch but that's just because the play() function is
  now indented and restructured. Not much real changes here.

add-remote-interface.patch
  This patch adds the remote interface to ogg123. It adds the files
  control_generic.c, control_generic.h and README.remote. It also adds
  them to Makefile.am.

cleanup-cmdline-usage.patch
  This patch makes the cmdline_usage() function print it out nicer.

add-remote-interface-usage.patch
  This patch adds the usage of control_generic() to ogg123.c
  It adds the command line option (-R) (and adds it to the manpage).
  This patch also fixes a small memleak by adding calls to
  stat_format_cleanup() and free(playlist_array) at the end of main().

cmdline-fix-indent.patch
  This patch fixes the broken indentation in the command line handling
  switch.

add-title-option.patch
  This patch adds the --title (-T) command line option. This sets the
  xterm title to currently playing filename (source_string actually).
  This option is also available in mpg123, so this adds compatibility.

add-test-option.patch
  This patch adds the -t option. This is identical to '-d null' but this
  makes it more compatible to mpg123.

-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/file_transport.c ogg123/file_transport.c
--- ogg123.orig/file_transport.c	Sat Jan 26 12:06:37 2002
+++ ogg123/file_transport.c	Sat Oct  5 13:48:31 2002
@@ -21,7 +21,7 @@
 
 #include "transport.h"
 #include "i18n.h"
-
+#include "status.h"
 
 typedef struct file_private_t {
   FILE *fp;
@@ -37,7 +37,7 @@
   return 1;  /* The file transport is tested last, so always try it */
 }
 
-data_source_t* file_open (char *source_string, ogg123_options_t *ogg123_opts)
+data_source_t* file_open (char *source_string, ogg123_options_t *ogg123_opts, stat_format_t *stat_format)
 {
   data_source_t *source;
   file_private_t *private;
diff -urN -X diff.ignore ogg123.orig/http_transport.c ogg123/http_transport.c
--- ogg123.orig/http_transport.c	Wed Jul 17 14:12:18 2002
+++ ogg123/http_transport.c	Sat Oct  5 13:52:40 2002
@@ -35,7 +35,6 @@
 
 #define INPUT_BUFFER_SIZE 32768
 
-extern stat_format_t *stat_format;  /* Bad hack!  Will fix after RC3! */
 extern signal_request_t sig_request;  /* Need access to global cancel flag */
 
 typedef struct http_private_t {
@@ -50,6 +49,7 @@
 
   data_source_t *data_source;
   data_source_stats_t stats;
+  stat_format_t *stat_format;
 } http_private_t;
 
 
@@ -82,7 +82,7 @@
   if (myarg->cancel_flag || sig_request.cancel)
     return -1;
 
-  pstats_arg = new_print_statistics_arg(stat_format,
+  pstats_arg = new_print_statistics_arg(myarg->stat_format,
 					source->transport->statistics(source),
 					NULL);
 
@@ -167,7 +167,7 @@
 }
 
 
-data_source_t* http_open (char *source_string, ogg123_options_t *ogg123_opts)
+data_source_t* http_open (char *source_string, ogg123_options_t *ogg123_opts, stat_format_t *stat_format)
 {
   data_source_t *source;
   http_private_t *private;
@@ -198,6 +198,7 @@
     private->stats.bytes_read = 0;
     private->stats.input_buffer_used = 0;
     private->cancel_flag = 0;
+    private->stat_format = stat_format;
 
   } else {
     fprintf(stderr, _("Error: Out of memory.\n"));
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Jul  6 21:12:18 2002
+++ ogg123/ogg123.c	Sat Oct  5 14:22:06 2002
@@ -48,19 +48,12 @@
 
 
 void exit_cleanup ();
-void play (char *source_string);
+void play (audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, 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;
-
 ogg123_options_t options;
-stat_format_t *stat_format;
-buf_t *audio_buffer;
-
-audio_play_arg_t audio_play_arg;
-
+buf_t *audio_buffer_to_cleanup = NULL;
 
 /* ------------------------- config file options -------------------------- */
 
@@ -280,13 +273,14 @@
   int items;
   struct stat stat_buf;
   int i;
+  buf_t *audio_buffer;
+  audio_play_arg_t audio_play_arg;
 
   setlocale(LC_ALL, "");
   bindtextdomain(PACKAGE, LOCALEDIR);
   textdomain(PACKAGE);
 
   ao_initialize();
-  stat_format = stat_format_create();
   options_init(&options);
   file_options_init(file_opts);
 
@@ -295,7 +289,7 @@
   optind = parse_cmdline_options(argc, argv, &options, file_opts);
 
   audio_play_arg.devices = options.devices;
-  audio_play_arg.stat_format = stat_format;
+  audio_play_arg.stat_format = stat_format_create();
 
   /* Add remaining arguments to playlist */
   for (i = optind; i < argc; i++) {
@@ -341,6 +335,7 @@
       status_error(_("Error: Could not create audio buffer.\n"));
       exit(1);
     }
+    audio_buffer_to_cleanup = audio_buffer;
   } else
     audio_buffer = NULL;
 
@@ -370,7 +365,7 @@
   /* Play the files/streams */
   i = 0;
   while (i < items && !sig_request.exit) {
-    play(playlist_array[i]);
+    play(&audio_play_arg, audio_buffer, playlist_array[i]);
     i++;
   }
 
@@ -380,7 +375,7 @@
 }
 
 
-void play (char *source_string)
+void play (audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, char *source_string)
 {
   transport_t *transport;
   format_t *format;
@@ -430,7 +425,7 @@
     return;
   }
   
-  if ( (source = transport->open(source_string, &options)) == NULL ) {
+  if ( (source = transport->open(source_string, &options, audio_play_arg->stat_format)) == NULL ) {
     status_error(_("Cannot open %s.\n"), source_string);
     return;
   }
@@ -454,7 +449,8 @@
   }
 
   /* Decide which statistics are valid */
-  select_stats(stat_format, &options, source, decoder, audio_buffer);
+  select_stats(audio_play_arg->stat_format, &options, 
+               source, decoder, audio_buffer);
 
   /* Start the audio playback thread before we begin sending data */    
   if (audio_buffer != NULL) {
@@ -535,7 +531,8 @@
 
       /* Update statistics display if needed */
       if (next_status <= 0) {
-	display_statistics(stat_format, audio_buffer, source, decoder); 
+	display_statistics(audio_play_arg->stat_format, audio_buffer, 
+	                   source, decoder); 
 	next_status = status_interval;
       } else
 	next_status -= ret;
@@ -575,7 +572,8 @@
   }
 
   /* Print final stats */
-  display_statistics_quick(stat_format, audio_buffer, source, decoder); 
+  display_statistics_quick(audio_play_arg->stat_format, audio_buffer, 
+                           source, decoder); 
    
   
   format->cleanup(decoder);
@@ -592,9 +590,9 @@
 void exit_cleanup ()
 {
       
-  if (audio_buffer != NULL) {
-    buffer_destroy (audio_buffer);
-    audio_buffer = NULL;
+  if (audio_buffer_to_cleanup != NULL) {
+    buffer_destroy(audio_buffer_to_cleanup);
+    audio_buffer_to_cleanup = NULL;
   }
 
   ao_onexit (options.devices);
diff -urN -X diff.ignore ogg123.orig/ogg123.h ogg123/ogg123.h
--- ogg123.orig/ogg123.h	Sat Jul  6 21:12:18 2002
+++ ogg123/ogg123.h	Sat Oct  5 14:16:43 2002
@@ -22,6 +22,8 @@
 #include "audio.h"
 #include "playlist.h"
 
+#define AUDIO_CHUNK_SIZE    4096
+
 typedef struct ogg123_options_t {
   long int verbosity;         /* Verbose output if > 1, quiet if 0 */
 
diff -urN -X diff.ignore ogg123.orig/status.h ogg123/status.h
--- ogg123.orig/status.h	Wed Dec 19 03:52:54 2001
+++ ogg123/status.h	Sat Oct  5 13:48:31 2002
@@ -23,7 +23,7 @@
 #include "transport.h"
 #include "format.h"
 
-typedef struct {
+typedef struct stat_format_t {
   int verbosity;
   char enabled;
   const char *formatstr;
diff -urN -X diff.ignore ogg123.orig/transport.h ogg123/transport.h
--- ogg123.orig/transport.h	Wed Dec 19 03:52:54 2001
+++ ogg123/transport.h	Sat Oct  5 14:24:27 2002
@@ -42,10 +42,12 @@
   void *private;
 } data_source_t;
 
+struct stat_format_t;
+
 typedef struct transport_t {
   char *name;
   int (* can_transport)(char *source_string);
-  data_source_t* (* open) (char *source_string, ogg123_options_t *ogg123_opts);
+  data_source_t* (* open) (char *source_string, ogg123_options_t *ogg123_opts, struct stat_format_t *stat_format);
   int (* peek) (data_source_t *source, void *ptr, size_t size, size_t nmemb);
   int (* read) (data_source_t *source, void *ptr, size_t size, size_t nmemb);
   int (* seek) (data_source_t *source, long offset, int whence);
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/callbacks.c ogg123/callbacks.c
--- ogg123.orig/callbacks.c	Sat Jul  6 06:27:19 2002
+++ ogg123/callbacks.c	Sat Oct  5 14:28:02 2002
@@ -180,7 +180,7 @@
 
 /* Decoder callbacks */
 
-void decoder_error_callback (void *arg, int severity, char *message, ...)
+void decoder_error_callback (void *arg, int severity, const char *message, ...)
 {
   va_list ap;
 
@@ -200,7 +200,7 @@
 }
 
 
-void decoder_metadata_callback (void *arg, int verbosity, char *message, ...)
+void decoder_metadata_callback (void *arg, int verbosity, const char *message, ...)
 {
   va_list ap;
 
@@ -258,8 +258,8 @@
 
 /* -------------------------------------------------------------- */
 
-void decoder_buffered_error_callback (void *arg, int severity, 
-				      char *message, ...)
+void decoder_buffered_error_callback (void *arg, int severity,
+				      const char *message, ...)
 {
   va_list ap;
   buf_t *buf = (buf_t *)arg;
@@ -314,8 +314,8 @@
 }
 
 
-void decoder_buffered_metadata_callback (void *arg, int verbosity, 
-					 char *message, ...)
+void decoder_buffered_metadata_callback (void *arg, int verbosity,
+					 const char *message, ...)
 {
   va_list ap;
   buf_t *buf = (buf_t *)arg;
diff -urN -X diff.ignore ogg123.orig/callbacks.h ogg123/callbacks.h
--- ogg123.orig/callbacks.h	Wed Dec 19 03:52:53 2001
+++ ogg123/callbacks.h	Sat Oct  5 14:28:02 2002
@@ -60,13 +60,13 @@
 
 
 /* Decoder callbacks */
-void decoder_error_callback (void *arg, int severity, char *message, ...);
-void decoder_metadata_callback (void *arg, int verbosity, char *message, ...);
+void decoder_error_callback (void *arg, int severity, const char *message, ...);
+void decoder_metadata_callback (void *arg, int verbosity, const char *message, ...);
 
 void decoder_buffered_error_callback (void *arg, int severity, 
-				      char *message, ...);
+				      const char *message, ...);
 void decoder_buffered_metadata_callback (void *arg, int verbosity, 
-					 char *message, ...);
+					 const char *message, ...);
 
 
 #endif /* __CALLBACKS_H__ */
diff -urN -X diff.ignore ogg123.orig/format.h ogg123/format.h
--- ogg123.orig/format.h	Wed Dec 19 03:52:53 2001
+++ ogg123/format.h	Sat Oct  5 14:28:02 2002
@@ -35,8 +35,8 @@
 enum { ERROR, WARNING, INFO };
 
 typedef struct decoder_callbacks_t {
-  void (* printf_error) (void *arg, int severity, char *message, ...);
-  void (* printf_metadata) (void *arg, int verbosity, char *message, ...);
+  void (* printf_error) (void *arg, int severity, const char *message, ...);
+  void (* printf_metadata) (void *arg, int verbosity, const char *message, ...);
 } decoder_callbacks_t;
 
 
@@ -57,7 +57,7 @@
 #define DECODER_SEEK_CUR   2
 
 typedef struct format_t {
-  char *name;
+  const char *name;
 
   int (* can_decode) (data_source_t *source);
   decoder_t* (* init) (data_source_t *source, ogg123_options_t *ogg123_opts,
diff -urN -X diff.ignore ogg123.orig/oggvorbis_format.c ogg123/oggvorbis_format.c
--- ogg123.orig/oggvorbis_format.c	Fri Jul 19 12:31:53 2002
+++ ogg123/oggvorbis_format.c	Sat Oct  5 14:28:02 2002
@@ -44,9 +44,9 @@
 
 
 /* Vorbis comment keys that need special formatting. */
-struct {
-  char *key;         /* includes the '=' for programming convenience */
-  char *formatstr;   /* formatted output */
+struct comment_key {
+  const char *key;         /* includes the '=' for programming convenience */
+  const char *formatstr;   /* formatted output */
 } vorbis_comment_keys[] = {
   {"TRACKNUMBER=", N_("Track number:")},
   {"REPLAYGAIN_TRACK_GAIN=", N_("ReplayGain (Track):")},
diff -urN -X diff.ignore ogg123.orig/status.c ogg123/status.c
--- ogg123.orig/status.c	Tue Mar 19 10:50:09 2002
+++ ogg123/status.c	Sat Oct  5 14:28:02 2002
@@ -40,8 +40,8 @@
 void write_buffer_state_string (char *dest, buffer_stats_t *buf_stats)
 {
   char *cur = dest;
-  char *comma = ", ";
-  char *sep = "(";
+  const char *comma = ", ";
+  const char *sep = "(";
 
   if (buf_stats->prebuffering) {
     cur += sprintf (cur, _("%sPrebuf to %.1f%%"), sep, 
diff -urN -X diff.ignore ogg123.orig/transport.h ogg123/transport.h
--- ogg123.orig/transport.h	Sat Oct  5 14:32:34 2002
+++ ogg123/transport.h	Sat Oct  5 14:31:04 2002
@@ -45,7 +45,7 @@
 struct stat_format_t;
 
 typedef struct transport_t {
-  char *name;
+  const char *name;
   int (* can_transport)(char *source_string);
   data_source_t* (* open) (char *source_string, ogg123_options_t *ogg123_opts, struct stat_format_t *stat_format);
   int (* peek) (data_source_t *source, void *ptr, size_t size, size_t nmemb);
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/buffer.c ogg123/buffer.c
--- ogg123.orig/buffer.c	Sun Jul 14 07:38:00 2002
+++ ogg123/buffer.c	Sat Oct  5 14:34:32 2002
@@ -87,7 +87,7 @@
 
 void buffer_thread_cleanup (void *arg)
 {
-  buf_t *buf = (buf_t *)arg;
+  //buf_t *buf = (buf_t *)arg;
 
   DEBUG("Enter buffer_thread_cleanup");
 }
diff -urN -X diff.ignore ogg123.orig/cfgfile_options.c ogg123/cfgfile_options.c
--- ogg123.orig/cfgfile_options.c	Sat Jan 26 12:06:37 2002
+++ ogg123/cfgfile_options.c	Sat Oct  5 14:35:29 2002
@@ -221,7 +221,7 @@
 
 parse_code_t parse_line (file_option_t opts[], char *line)
 {
-  char *equals, *value = "";
+  char *equals, *value = 0;
   file_option_t *opt;
   int len;
 
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 14:32:34 2002
+++ ogg123/ogg123.c	Sat Oct  5 14:36:55 2002
@@ -50,7 +50,7 @@
 void exit_cleanup ();
 void play (audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, char *source_string);
 
-unsigned char convbuffer[AUDIO_CHUNK_SIZE];
+char convbuffer[AUDIO_CHUNK_SIZE];
 int convsize = AUDIO_CHUNK_SIZE;
 ogg123_options_t options;
 buf_t *audio_buffer_to_cleanup = NULL;
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 14:40:18 2002
+++ ogg123/ogg123.c	Sat Oct  5 14:42:01 2002
@@ -268,7 +268,7 @@
 
 int main(int argc, char **argv)
 {
-  int optind;
+  int option_index;
   char **playlist_array;
   int items;
   struct stat stat_buf;
@@ -286,13 +286,13 @@
 
   parse_std_configs(file_opts);
   options.playlist = playlist_create();
-  optind = parse_cmdline_options(argc, argv, &options, file_opts);
+  option_index = parse_cmdline_options(argc, argv, &options, file_opts);
 
   audio_play_arg.devices = options.devices;
   audio_play_arg.stat_format = stat_format_create();
 
   /* Add remaining arguments to playlist */
-  for (i = optind; i < argc; i++) {
+  for (i = option_index; i < argc; i++) {
     if (stat(argv[i], &stat_buf) == 0) {
 
       if (S_ISDIR(stat_buf.st_mode)) {
@@ -342,8 +342,6 @@
 
   /* Shuffle playlist */
   if (options.shuffle) {
-    int i;
-    
     srandom(time(NULL));
     
     for (i = 0; i < items; i++) {
diff -urN -X diff.ignore ogg123.orig/status.c ogg123/status.c
--- ogg123.orig/status.c	Sat Oct  5 14:33:28 2002
+++ ogg123/status.c	Sat Oct  5 14:40:53 2002
@@ -66,10 +66,10 @@
 /* Write a min:sec.msec style string to dest corresponding to time.
    The time parameter is in seconds.  Returns the number of characters
    written */
-int write_time_string (char *dest, double time)
+int write_time_string (char *dest, double timeval)
 {
-  long min = (long) time / (long) 60;
-  double sec = time - 60.0f * min;
+  long min = (long) timeval / (long) 60;
+  double sec = timeval - 60.0f * min;
 
   return sprintf (dest, "%02li:%05.2f", min, sec);
 }
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/buffer.c ogg123/buffer.c
--- ogg123.orig/buffer.c	Sat Oct  5 14:40:18 2002
+++ ogg123/buffer.c	Sat Oct  5 14:52:04 2002
@@ -153,13 +153,6 @@
 }
 
 
-void free_action (action_t *action)
-{
-  free(action);
-}
-
-
-
 int compute_dequeue_size (buf_t *buf, int request_size)
 {
   int next_action_pos;
@@ -542,12 +535,6 @@
 
     /* 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);
 
     UNLOCK_MUTEX(buf->mutex);
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 14:44:04 2002
+++ ogg123/ogg123.c	Sat Oct  5 14:58:50 2002
@@ -270,7 +270,6 @@
 {
   int option_index;
   char **playlist_array;
-  int items;
   struct stat stat_buf;
   int i;
   buf_t *audio_buffer;
@@ -314,7 +313,7 @@
     cmdline_usage();
     exit(1);
   } else {
-    playlist_array = playlist_to_array(options.playlist, &items);
+    playlist_array = playlist_to_array(options.playlist);
     playlist_destroy(options.playlist);
     options.playlist = NULL;
   }
@@ -342,10 +341,16 @@
 
   /* Shuffle playlist */
   if (options.shuffle) {
-    srandom(time(NULL));
+    int n_items = 0;
+
+    for (i = 0; playlist_array[i]; i++) {
+      n_items++; 
+    }
     
-    for (i = 0; i < items; i++) {
-      int j = i + random() % (items - i);
+    srandom(time(NULL));
+
+    for (i = 0; playlist_array[i]; i++) {
+      int j = i + random() % (n_items - i);
       char *temp = playlist_array[i];
       playlist_array[i] = playlist_array[j];
       playlist_array[j] = temp;
@@ -362,12 +367,12 @@
 
   /* Play the files/streams */
   i = 0;
-  while (i < items && !sig_request.exit) {
+  while (playlist_array[i] && !sig_request.exit) {
     play(&audio_play_arg, audio_buffer, playlist_array[i]);
     i++;
   }
 
-  playlist_array_destroy(playlist_array, items);
+  playlist_array_destroy(playlist_array);
 
   exit (0);
 }
diff -urN -X diff.ignore ogg123.orig/playlist.c ogg123/playlist.c
--- ogg123.orig/playlist.c	Sat Jul 13 19:57:10 2002
+++ ogg123/playlist.c	Sat Oct  5 14:55:38 2002
@@ -245,14 +245,15 @@
 
 /* Convert the playlist to an array of strings.  Strings are deep copied. 
    Size will be set to the number of elements in the array. */
-char **playlist_to_array(playlist_t *list, int *size)
+char **playlist_to_array(playlist_t *list)
 {
   char **array;
   int i;
   playlist_element_t *element;
-
-  *size = playlist_length(list);
-  array = calloc(*size, sizeof(char *));
+  int size;
+  
+  size = playlist_length(list);
+  array = calloc(size + 1, sizeof(char *));
 
   if (array == NULL) {
     fprintf(stderr,
@@ -261,7 +262,7 @@
   }
 
   for (i = 0, element = list->head->next; 
-       i < *size; 
+       i < size; 
        i++, element = element->next) {
 
     array[i] = strdup(element->filename);
@@ -272,19 +273,20 @@
       exit(1);
     }
   }
-
+  
+  array[size] = NULL;  /* null terminate the array */
 
   return array;
 }
 
 
 /* Deallocate array and all contained strings created by playlist_to_array. */
-void playlist_array_destroy(char **array, int size)
+void playlist_array_destroy(char **array)
 {
   int i;
 
-  for (i = 0; i < size; i++)
+  for (i = 0; array[i]; i++)
     free(array[i]);
-
+  
   free(array);
 }
diff -urN -X diff.ignore ogg123.orig/playlist.h ogg123/playlist.h
--- ogg123.orig/playlist.h	Sat Jul  6 05:23:13 2002
+++ ogg123/playlist.h	Sat Oct  5 14:55:38 2002
@@ -58,11 +58,11 @@
 int playlist_length(playlist_t *list);
 
 /* Convert the playlist to an array of strings.  Strings are deep copied. 
-   Size will be set to the number of elements in the array. */
-char **playlist_to_array(playlist_t *list, int *size);
+   The last entry will be NULL */
+char **playlist_to_array(playlist_t *list);
 
 /* Deallocate array and all contained strings created by playlist_to_array. */
-void playlist_array_destroy(char **array, int size);
+void playlist_array_destroy(char **array);
 
 
 #endif /* __PLAYLIST_H__ */
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cfgfile_options.c ogg123/cfgfile_options.c
--- ogg123.orig/cfgfile_options.c	Sat Oct  5 14:40:18 2002
+++ ogg123/cfgfile_options.c	Sat Oct  5 16:00:26 2002
@@ -443,7 +443,7 @@
 
   parse_config_file(opts, "/etc/ogg123rc");
   if (homedir && strlen(homedir) < FILENAME_MAX - 10) {
-    /* Try ~/.ogg123 */
+    /* Try ~/.ogg123rc */
     strncpy(filename, homedir, FILENAME_MAX);
     strcat(filename, "/.ogg123rc");
     parse_config_file(opts, filename);
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/format.h ogg123/format.h
--- ogg123.orig/format.h	Sat Oct  5 14:33:28 2002
+++ ogg123/format.h	Sat Oct  5 16:20:55 2002
@@ -24,6 +24,8 @@
 
 
 typedef struct decoder_stats_t {
+  ogg_int64_t total_pcm_samples;
+  ogg_int64_t current_pcm_sample;
   double total_time;  /* seconds */
   double current_time;   /* seconds */
   long   instant_bitrate;
diff -urN -X diff.ignore ogg123.orig/oggvorbis_format.c ogg123/oggvorbis_format.c
--- ogg123.orig/oggvorbis_format.c	Sat Oct  5 14:33:28 2002
+++ ogg123/oggvorbis_format.c	Sat Oct  5 16:17:35 2002
@@ -98,6 +98,8 @@
     private->bos = 1;
     private->current_section = -1;
 
+    private->stats.current_pcm_sample = 0;
+    private->stats.total_pcm_samples = 0;
     private->stats.total_time = 0.0;
     private->stats.current_time = 0.0;
     private->stats.instant_bitrate = 0;
@@ -221,12 +223,16 @@
   long instant_bitrate;
   long avg_bitrate;
 
+  priv->stats.total_pcm_samples   = ov_pcm_total(&priv->vf, -1);
+  priv->stats.current_pcm_sample  = ov_pcm_tell(&priv->vf);
+
+  //fprintf(stderr, "%lld\n", priv->stats.total_pcm_samples);
   /* ov_time_tell() doesn't work on non-seekable streams, so we use
      ov_pcm_tell()  */
-  priv->stats.total_time = (double) ov_pcm_total(&priv->vf, -1) /
-    (double) decoder->actual_fmt.rate;
-  priv->stats.current_time = (double) ov_pcm_tell(&priv->vf) / 
-    (double) decoder->actual_fmt.rate;
+  priv->stats.total_time    = priv->stats.total_pcm_samples /
+                              (double)decoder->actual_fmt.rate;
+  priv->stats.current_time  = priv->stats.current_pcm_sample /
+                              (double)decoder->actual_fmt.rate;
 
   /* vorbisfile returns 0 when no bitrate change has occurred */
   instant_bitrate = ov_bitrate_instant(&priv->vf);
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/callbacks.c ogg123/callbacks.c
--- ogg123.orig/callbacks.c	Sat Oct  5 14:33:28 2002
+++ ogg123/callbacks.c	Sat Oct  5 16:23:42 2002
@@ -136,6 +136,36 @@
 
 /* Statistics callbacks */
 
+/*
+// remote_write_action* functions :
+//  
+//   These functions are used from control_generic.c which is the ogg123
+//   remote interface.
+//   
+//   Maybe add locking by calling a seperate (new) function in status.c
+//   which holds its own output_lock for stdout.
+//
+*/
+void remote_write_action(buf_t *buf, void *arg)
+{
+  char *message = (char *)arg;
+
+  //status_message(0, "@%s", message);
+  fwrite(message, strlen(message), 1, stdout);
+  fwrite("\n", 1, 1, stdout);
+
+  free(message);
+  message = 0;
+}
+void remote_write_action_const(buf_t *buf, void *arg)
+{
+  char *message = (char *)arg;
+
+  //status_message(0, "@%s\n", message);
+  fwrite(message, strlen(message), 1, stdout);
+  fwrite("\n", 1, 1, stdout);
+}
+
 void print_statistics_action (buf_t *buf, void *arg)
 {
   print_statistics_arg_t *stats_arg = (print_statistics_arg_t *) arg;
diff -urN -X diff.ignore ogg123.orig/callbacks.h ogg123/callbacks.h
--- ogg123.orig/callbacks.h	Sat Oct  5 14:33:28 2002
+++ ogg123/callbacks.h	Sat Oct  5 16:23:42 2002
@@ -44,7 +44,10 @@
 					  audio_format_t *fmt);
 
 
-/* Statisitics callbacks */
+/* Statistics callbacks */
+
+void remote_write_action(buf_t *buf, void *arg);
+void remote_write_action_const(buf_t *buf, void *arg);
 
 typedef struct print_statistics_arg_t {
   stat_format_t *stat_format;
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/buffer.c ogg123/buffer.c
--- ogg123.orig/buffer.c	Sat Oct  5 14:54:18 2002
+++ ogg123/buffer.c	Sat Oct  5 16:25:43 2002
@@ -256,6 +256,10 @@
     
     UNLOCK_MUTEX(buf->mutex);
   }
+
+  /* execute the last pending actions (after the data positions) */
+  /* THIS COULD BE UNSAFE, I REALLY DON'T KNOW */
+  execute_actions(buf, &buf->actions, buf->position);
   
   pthread_cleanup_pop(1);
   DEBUG("exiting buffer_thread_func");
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/format.h ogg123/format.h
--- ogg123.orig/format.h	Sat Oct  5 16:22:17 2002
+++ ogg123/format.h	Sat Oct  5 16:28:39 2002
@@ -22,6 +22,7 @@
 #include "transport.h"
 #include "ogg123.h"
 
+#define UGLY_ID3_HACK 1
 
 typedef struct decoder_stats_t {
   ogg_int64_t total_pcm_samples;
@@ -70,6 +71,9 @@
   int (* seek) (decoder_t *decoder, double offset, int whence);
   decoder_stats_t* (* statistics) (decoder_t *decoder);
   void (* cleanup) (decoder_t *decoder);
+#if UGLY_ID3_HACK
+  char* (*id3string) (decoder_t *decoder);
+#endif  
 } format_t;
 
 format_t *get_format_by_name (char *name);
diff -urN -X diff.ignore ogg123.orig/oggvorbis_format.c ogg123/oggvorbis_format.c
--- ogg123.orig/oggvorbis_format.c	Sat Oct  5 16:22:17 2002
+++ ogg123/oggvorbis_format.c	Sat Oct  5 16:27:16 2002
@@ -258,6 +258,56 @@
   free(decoder);
 }
 
+#if UGLY_ID3_HACK
+
+struct {
+  const char *key;
+  int max_length;
+} id3tags[] = {
+  {"TITLE=", 30},
+  {"ARTIST=", 30},
+  {"ALBUM=", 30},
+  {"YEAR=", 4},
+  {"COMMENT=", 30},
+  {"GENRE=", 20},
+  {NULL, 0}
+};
+
+char *id3string (decoder_t *decoder)
+{
+  ovf_private_t *priv = decoder->private;
+  int i;
+  int tag_idx;
+  char id3_string[30+30+30+4+30+20 + 1] = "";
+  int id3_offset = 0;
+
+  // fill with spaces.
+  for (i = 0; i < sizeof(id3_string); i++) {
+    id3_string[i] = ' ';
+  }
+  id3_string[i-1] = '\0';
+
+  for (tag_idx = 0; id3tags[tag_idx].key != NULL; tag_idx++) {
+    const char *tag = id3tags[tag_idx].key;
+    int max_length  = id3tags[tag_idx].max_length;
+
+    for (i = 0; i < priv->vc->comments; i++) {
+      char *comment = priv->vc->user_comments[i];
+      if (!strncasecmp(comment, tag, strlen(tag))) {
+        char *comment_offset = strchr(comment, '=');
+        char formatstr[10];
+
+        sprintf(formatstr, "%%-%ds\n", max_length);
+        sprintf(id3_string + id3_offset, formatstr, comment_offset+1);
+
+        id3_offset += max_length;
+      }
+    }
+  }
+
+  return strdup(id3_string);
+}
+#endif
 
 format_t oggvorbis_format = {
   "oggvorbis",
@@ -267,6 +317,9 @@
   &ovf_seek,
   &ovf_statistics,
   &ovf_cleanup,
+#if UGLY_ID3_HACK
+  &id3string
+#endif
 };
 
 
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 15:01:36 2002
+++ ogg123/ogg123.c	Sat Oct  5 17:02:19 2002
@@ -48,10 +48,8 @@
 
 
 void exit_cleanup ();
-void play (audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, char *source_string);
+void control_default(audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, char **playlist);
 
-char convbuffer[AUDIO_CHUNK_SIZE];
-int convsize = AUDIO_CHUNK_SIZE;
 ogg123_options_t options;
 buf_t *audio_buffer_to_cleanup = NULL;
 
@@ -334,7 +332,6 @@
       status_error(_("Error: Could not create audio buffer.\n"));
       exit(1);
     }
-    audio_buffer_to_cleanup = audio_buffer;
   } else
     audio_buffer = NULL;
 
@@ -359,53 +356,40 @@
 
   /* Setup signal handlers and callbacks */
 
+  audio_buffer_to_cleanup = audio_buffer;
   ATEXIT (exit_cleanup);
   signal (SIGINT, signal_handler);
   signal (SIGTSTP, signal_handler);
   signal (SIGCONT, signal_handler);
 
 
-  /* Play the files/streams */
-  i = 0;
-  while (playlist_array[i] && !sig_request.exit) {
-    play(&audio_play_arg, audio_buffer, playlist_array[i]);
-    i++;
-  }
-
+  control_default(&audio_play_arg, audio_buffer, playlist_array);
   playlist_array_destroy(playlist_array);
 
   exit (0);
 }
 
 
-void play (audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, char *source_string)
+void control_default(audio_play_arg_t *audio_play_arg, buf_t *audio_buffer, char **playlist)
 {
-  transport_t *transport;
-  format_t *format;
-  data_source_t *source;
-  decoder_t *decoder;
-
-  decoder_callbacks_t decoder_callbacks;
-  void *decoder_callbacks_arg;
-
-  /* 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 };
+  int pl_index = 0;
+  audio_format_t old_audio_fmt = { 0, 0, 0, 0, 0 };
+  int status_interval = 0;
   audio_format_t new_audio_fmt;
   audio_reopen_arg_t *reopen_arg;
+  
+  decoder_callbacks_t decoder_callbacks;
+  void *decoder_callbacks_arg;
 
-  /* Flags and counters galore */
-  int eof = 0, eos = 0, ret;
-  int nthc = 0, ntimesc = 0;
-  int next_status = 0;
-  static int status_interval = 0;
-
-  /* Reset all of the signal flags */
-  sig_request.cancel   = 0;
-  sig_request.skipfile = 0;
-  sig_request.exit     = 0;
-  sig_request.pause    = 0;
-
+  char *convbuffer;
+  int convsize = AUDIO_CHUNK_SIZE;
+  
+  convbuffer = malloc(convsize);
+  if (convbuffer == NULL) {
+    status_error(_("Error: Out of memory in control_default().\n"));
+    exit(1);
+  }
+  
   /* Set preferred audio format (used by decoder) */
   new_audio_fmt.big_endian = ao_is_big_endian();
   new_audio_fmt.signed_sample = 1;
@@ -422,171 +406,193 @@
     decoder_callbacks_arg = NULL;
   }
 
-  /* 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 ( (source = transport->open(source_string, &options, audio_play_arg->stat_format)) == 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;
-  }
+  /* Play the files/streams */
+  for (pl_index = 0; playlist[pl_index] && !sig_request.exit; pl_index++) {
+    char *source_string = playlist[pl_index];
+    transport_t *transport;
+    format_t *format;
+    data_source_t *source;
+    decoder_t *decoder;
+
+    /* Flags and counters galore */
+    int eof = 0, eos = 0, ret;
+    int nthc = 0, ntimesc = 0;
+    int next_status = 0;
+
+    /* Reset all of the signal flags */
+    sig_request.cancel   = 0;
+    sig_request.skipfile = 0;
+    sig_request.exit     = 0;
+    sig_request.pause    = 0;
+
+    /* 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);
+      continue;
+    }
   
-  if ( (decoder = format->init(source, &options, &new_audio_fmt, 
-			       &decoder_callbacks,
-			       decoder_callbacks_arg)) == NULL ) {
-
-    /* We may have failed because of user command */
-    if (!sig_request.cancel)
-      status_error(_("Error opening %s using the %s module."
-		     "  The file may be corrupted.\n"), source_string,
-		   format->name);
-    return;
-  }
-
-  /* Decide which statistics are valid */
-  select_stats(audio_play_arg->stat_format, &options, 
-               source, decoder, audio_buffer);
+    if ( (source = transport->open(source_string, &options, audio_play_arg->stat_format)) == NULL ) {
+      status_error(_("Cannot open %s.\n"), source_string);
+      continue;
+    }
 
-  /* Start the audio playback thread before we begin sending data */    
-  if (audio_buffer != NULL) {
+    /* 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);
+      continue;
+    }
     
-    /* First reset mutexes and other synchronization variables */
-    buffer_reset (audio_buffer);
-    buffer_thread_start (audio_buffer);
-  }
-
-  /* Show which file we are playing */
-  decoder_callbacks.printf_metadata(decoder_callbacks_arg, 1,
-				    _("Playing: %s"), source_string);
+    if ( (decoder = format->init(source, &options, &new_audio_fmt, 
+			         &decoder_callbacks,
+			         decoder_callbacks_arg)) == NULL ) {
+
+      /* We may have failed because of user command */
+      if (!sig_request.cancel)
+        status_error(_("Error opening %s using the %s module."
+		       "  The file may be corrupted.\n"), source_string,
+		     format->name);
+      continue;
+    }
 
-  /* Skip over audio */
-  if (options.seekpos > 0.0) {
-    if (!format->seek(decoder, options.seekpos, DECODER_SEEK_START))
-      status_error(_("Could not skip %f seconds of audio."), options.seekpos);
-  }
+    /* Decide which statistics are valid */
+    select_stats(audio_play_arg->stat_format, &options, 
+                 source, decoder, audio_buffer);
 
-  /* Main loop:  Iterates over all of the logical bitstreams in the file */
-  while (!eof && !sig_request.exit) {
-    
-    /* Loop through data within a logical bitstream */
-    eos = 0;    
-    while (!eos && !sig_request.exit) {
+    /* Start the audio playback thread before we begin sending data */    
+    if (audio_buffer) {
       
-      /* Check signals */
-      if (sig_request.skipfile) {
-	eof = eos = 1;
-	break;
-      }
-
-      if (sig_request.pause) {
-	if (audio_buffer)
-	  buffer_thread_pause (audio_buffer);
-
-	kill (getpid(), SIGSTOP); /* We block here until we unpause */
-	
-	/* Done pausing */
-	if (audio_buffer)
-	  buffer_thread_unpause (audio_buffer);
-
-	sig_request.pause = 0;
-      }
-
+      /* First reset mutexes and other synchronization variables */
+      buffer_reset (audio_buffer);
+      buffer_thread_start (audio_buffer);
+    }
 
-      /* Read another block of audio data */
-      ret = format->read(decoder, convbuffer, convsize, &eos, &new_audio_fmt);
+    /* Show which file we are playing */
+    decoder_callbacks.printf_metadata(decoder_callbacks_arg, 1,
+				      _("Playing: %s"), source_string);
+
+    /* Skip over audio */
+    if (options.seekpos > 0.0) {
+      if (!format->seek(decoder, options.seekpos, DECODER_SEEK_START))
+        status_error(_("Could not skip %f seconds of audio."), options.seekpos);
+    }
 
-      /* Bail if we need to */
-      if (ret == 0) {
-	eof = eos = 1;
-	break;
-      } else if (ret < 0) {
-	status_error(_("Error: Decoding failure.\n"));
-	break;
-      }
+    /* Main loop:  Iterates over all of the logical bitstreams in the file */
+    while (!eof && !sig_request.exit) {
+    
+      /* Loop through data within a logical bitstream */
+      eos = 0;    
+      while (!eos && !sig_request.exit) {
+        
+        /* Check signals */
+        if (sig_request.skipfile) {
+          eof = eos = 1;
+          break;
+        }
+
+        if (sig_request.pause) {
+          if (audio_buffer)
+            buffer_thread_pause (audio_buffer);
+
+          kill (getpid(), SIGSTOP); /* We block here until we unpause */
+          
+          /* Done pausing */
+          if (audio_buffer)
+            buffer_thread_unpause (audio_buffer);
+
+          sig_request.pause = 0;
+        }
+
+
+        /* Read another block of audio data */
+        ret = format->read(decoder, convbuffer, convsize, &eos, &new_audio_fmt);
+
+        /* Bail if we need to */
+        if (ret == 0) {
+          eof = eos = 1;
+          break;
+        } else if (ret < 0) {
+          status_error(_("Error: Decoding failure.\n"));
+          break;
+        }
 
       
-      /* Check to see if the audio format has changed */
-      if (!audio_format_equal(&new_audio_fmt, &old_audio_fmt)) {
-	old_audio_fmt = new_audio_fmt;
-	
-	/* Update our status printing interval */
-	status_interval = new_audio_fmt.word_size * new_audio_fmt.channels * 
-	  new_audio_fmt.rate / options.status_freq;
-	next_status = 0;
-
-	reopen_arg = new_audio_reopen_arg(options.devices, &new_audio_fmt);
-
-	if (audio_buffer)	  
-	  buffer_insert_action_at_end(audio_buffer, &audio_reopen_action,
-				      reopen_arg);
-	else
-	  audio_reopen_action(NULL, reopen_arg);
-      }
-      
-
-      /* Update statistics display if needed */
-      if (next_status <= 0) {
-	display_statistics(audio_play_arg->stat_format, audio_buffer, 
-	                   source, decoder); 
-	next_status = status_interval;
-      } else
-	next_status -= ret;
-
-
-      /* Write audio data block to output, skipping or repeating chunks
-	 as needed */
-      do {
-	
-	if (nthc-- == 0) {
-	  if (audio_buffer)
-	    buffer_submit_data(audio_buffer, convbuffer, ret);
-	  else
-	    audio_play_callback(convbuffer, ret, eos, &audio_play_arg);
-	  
-	  nthc = options.nth - 1;
-	}
-	
-      } while (++ntimesc < options.ntimes);
-
-      ntimesc = 0;
+        /* Check to see if the audio format has changed */
+        if (!audio_format_equal(&new_audio_fmt, &old_audio_fmt)) {
+          old_audio_fmt = new_audio_fmt;
+          
+          /* Update our status printing interval */
+          status_interval = new_audio_fmt.word_size * new_audio_fmt.channels * 
+            new_audio_fmt.rate / options.status_freq;
+          next_status = 0;
+
+          reopen_arg = new_audio_reopen_arg(options.devices, &new_audio_fmt);
+
+          if (audio_buffer)       
+            buffer_insert_action_at_end(audio_buffer, &audio_reopen_action,
+                                        reopen_arg);
+          else
+            audio_reopen_action(NULL, reopen_arg);
+        }
+        
+
+        /* Update statistics display if needed */
+        if (next_status <= 0) {
+          display_statistics(audio_play_arg->stat_format, audio_buffer, 
+                             source, decoder); 
+          next_status = status_interval;
+        } else
+          next_status -= ret;
+
+
+        /* Write audio data block to output, skipping or repeating chunks
+           as needed */
+        do {
+          
+          if (nthc-- == 0) {
+            if (audio_buffer)
+              buffer_submit_data(audio_buffer, convbuffer, ret);
+            else
+              audio_play_callback(convbuffer, ret, eos, &audio_play_arg);
             
-    } /* End of data loop */
+            nthc = options.nth - 1;
+          }
+          
+        } while (++ntimesc < options.ntimes);
+
+        ntimesc = 0;
+              
+      } /* End of data loop */
+      
+    } /* End of logical bitstream loop */
     
-  } /* End of logical bitstream loop */
-  
-  /* Done playing this logical bitstream.  Clean up house. */
+    /* Done playing this logical bitstream.  Clean up house. */
 
-  if (audio_buffer) {
-    
-    if (!sig_request.exit && !sig_request.skipfile) {
-      buffer_mark_eos(audio_buffer);
-      buffer_wait_for_empty(audio_buffer);
+    if (audio_buffer) {
+      
+      if (!sig_request.exit && !sig_request.skipfile) {
+        buffer_mark_eos(audio_buffer);
+        buffer_wait_for_empty(audio_buffer);
+      }
+
+      buffer_thread_kill(audio_buffer);
     }
 
-    buffer_thread_kill(audio_buffer);
-  }
+    /* Print final stats */
+    display_statistics_quick(audio_play_arg->stat_format, audio_buffer, 
+                             source, decoder); 
+     
+    
+    format->cleanup(decoder);
+    transport->close(source);
+    status_reset_output_lock();  /* In case we were killed mid-output */
 
-  /* Print final stats */
-  display_statistics_quick(audio_play_arg->stat_format, audio_buffer, 
-                           source, decoder); 
-   
+    status_message(1, _("Done."));
   
-  format->cleanup(decoder);
-  transport->close(source);
-  status_reset_output_lock();  /* In case we were killed mid-output */
-
-  status_message(1, _("Done."));
+    if (sig_request.exit)
+      break;
+  }
   
-  if (sig_request.exit)
-    exit (0);
+  free(convbuffer);
 }
 
 
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cmdline_options.c ogg123/cmdline_options.c
--- ogg123.orig/cmdline_options.c	Sat Oct  5 17:38:52 2002
+++ ogg123/cmdline_options.c	Sat Oct  5 17:53:09 2002
@@ -45,6 +45,7 @@
     {"nth", required_argument, 0, 'x'},
     {"ntimes", required_argument, 0, 'y'},
     {"shuffle", no_argument, 0, 'z'},
+    {"remote", no_argument, 0, 'R'},    
     {"list", required_argument, 0, '@'},
     {"audio-buffer", required_argument, 0, 0},
     {0, 0, 0, 0}
@@ -63,7 +64,7 @@
   audio_device_t *current;
   int ret;
 
-  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qvVx:y:z@:",
+  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qRvVx:y:z@:",
 				  long_options, &option_index))) {
 
       switch (ret) {
@@ -171,6 +172,11 @@
 	ogg123_opts->verbosity = 0;
 	break;
 	
+
+      case 'R':
+	ogg123_opts->remote = 1;
+	break;
+
       case 'v':
 	ogg123_opts->verbosity++;
 	break;
@@ -286,6 +292,7 @@
 	 "  -p n, --prebuffer n        load n%% of the input buffer before playing\n"
 	 "  -v,   --verbose            display progress and other status information\n"
 	 "  -q,   --quiet              don't display anything (no title)\n"
+	 "  -R,   --remote             remote interface\n"
 	 "  -x n, --nth                play every 'n'th block\n"
 	 "  -y n, --ntimes             repeat every played block 'n' times\n"
 	 "  -z,   --shuffle            shuffle play\n"
diff -urN -X diff.ignore ogg123.orig/ogg123.1 ogg123/ogg123.1
--- ogg123.orig/ogg123.1	Fri Jul 12 04:24:49 2002
+++ ogg123/ogg123.1	Sat Oct  5 17:44:19 2002
@@ -9,7 +9,7 @@
 .SH SYNOPSIS
 .B ogg123 
 [
-.B -vqzVh
+.B -vqRzVh
 ] [
 .B -k
 .I seconds 
@@ -87,6 +87,8 @@
 for a list of valid options for each device.
 .IP "-q, --quiet"
 Quiet mode.  No messages are displayed.
+.IP "-R, --remote"
+Activate the remote interface. Read README.remote for more information.
 .IP "-V, --version"
 Display version information.
 .IP "-v, --verbose"
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 17:08:59 2002
+++ ogg123/ogg123.c	Sat Oct  5 17:53:47 2002
@@ -42,6 +42,7 @@
 #include "status.h"
 #include "playlist.h"
 #include "compat.h"
+#include "control_generic.h"
 
 #include "ogg123.h"
 #include "i18n.h"
@@ -127,6 +128,7 @@
 {
   opts->verbosity = 2;
   opts->shuffle = 0;
+  opts->remote = 0;
   opts->delay = 500;
   opts->nth = 1;
   opts->ntimes = 1;
@@ -288,40 +290,40 @@
   audio_play_arg.devices = options.devices;
   audio_play_arg.stat_format = stat_format_create();
 
-  /* Add remaining arguments to playlist */
-  for (i = option_index; i < argc; i++) {
-    if (stat(argv[i], &stat_buf) == 0) {
-
-      if (S_ISDIR(stat_buf.st_mode)) {
-	if (playlist_append_directory(options.playlist, argv[i]) == 0)
-	  fprintf(stderr, 
-		  _("Warning: Could not read directory %s.\n"), argv[i]);
-      } else {
-	playlist_append_file(options.playlist, argv[i]);
-      }
-    } else /* If we can't stat it, it might be a non-disk source */
-      playlist_append_file(options.playlist, argv[i]);
-
+  if (!options.remote) {
+    /* Add remaining arguments to playlist */
+    for (i = option_index; i < argc; i++) {
+      if (stat(argv[i], &stat_buf) == 0) {
+
+        if (S_ISDIR(stat_buf.st_mode)) {
+          if (playlist_append_directory(options.playlist, argv[i]) == 0)
+            fprintf(stderr, 
+                    _("Warning: Could not read directory %s.\n"), argv[i]);
+        } else {
+          playlist_append_file(options.playlist, argv[i]);
+        }
+      } else /* If we can't stat it, it might be a non-disk source */
+        playlist_append_file(options.playlist, argv[i]);
+    }
 
+    /* Do we have anything left to play? */
+    if (playlist_length(options.playlist) == 0) {
+      cmdline_usage();
+      exit(1);
+    } else {
+      playlist_array = playlist_to_array(options.playlist);
+      playlist_destroy(options.playlist);
+      options.playlist = NULL;
+    }
   }
 
-
-  /* Do we have anything left to play? */
-  if (playlist_length(options.playlist) == 0) {
-    cmdline_usage();
-    exit(1);
+  if (options.remote) {
+    status_set_verbosity(0);
   } else {
-    playlist_array = playlist_to_array(options.playlist);
-    playlist_destroy(options.playlist);
-    options.playlist = NULL;
+    /* Don't use status_message until after this point! */
+    status_set_verbosity(options.verbosity);
   }
-
-  /* Don't use status_message until after this point! */
-  status_set_verbosity(options.verbosity);
-
-  print_audio_devices_info(options.devices);
-
-  
+    
   /* Setup buffer */ 
   if (options.buffer_size > 0) {
     audio_buffer = buffer_create(options.buffer_size,
@@ -355,16 +357,24 @@
   }
 
   /* Setup signal handlers and callbacks */
-
   audio_buffer_to_cleanup = audio_buffer;
   ATEXIT (exit_cleanup);
-  signal (SIGINT, signal_handler);
-  signal (SIGTSTP, signal_handler);
-  signal (SIGCONT, signal_handler);
 
+  if (options.remote) {
+    control_generic(&audio_play_arg, audio_buffer);
+  } else {
+    print_audio_devices_info(options.devices);
+  
+    signal (SIGINT, signal_handler);
+    signal (SIGTSTP, signal_handler);
+    signal (SIGCONT, signal_handler);
+
+    control_default(&audio_play_arg, audio_buffer, playlist_array);
+    playlist_array_destroy(playlist_array);
+  }
 
-  control_default(&audio_play_arg, audio_buffer, playlist_array);
-  playlist_array_destroy(playlist_array);
+  free(playlist_array);
+  stat_format_cleanup(audio_play_arg.stat_format);
 
   exit (0);
 }
diff -urN -X diff.ignore ogg123.orig/ogg123.h ogg123/ogg123.h
--- ogg123.orig/ogg123.h	Sat Oct  5 14:32:34 2002
+++ ogg123/ogg123.h	Sat Oct  5 17:53:05 2002
@@ -28,6 +28,7 @@
   long int verbosity;         /* Verbose output if > 1, quiet if 0 */
 
   int shuffle;                /* Should we shuffle playing? */
+  int remote;                 /* Use the remote interface */
   ogg_int64_t delay;          /* delay (in millisecs) for skip to next song */
   int nth;                    /* Play every nth chunk */
   int ntimes;                 /* Play every chunk n times */
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cmdline_options.c ogg123/cmdline_options.c
--- ogg123.orig/cmdline_options.c	Thu Jul 11 04:44:39 2002
+++ ogg123/cmdline_options.c	Sat Oct  5 17:35:23 2002
@@ -254,16 +254,16 @@
   int i, driver_count;
   ao_info **devices = ao_driver_info_list(&driver_count);
 
-  printf ( 
+  printf (
          _("ogg123 from %s %s\n"
 	 " by the Xiph.org Foundation (http://www.xiph.org/)\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"
-	 "        "), PACKAGE, VERSION);
-  
+	 "  -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"
+	 "                             "), PACKAGE, VERSION);
+
   for(i = 0; i < driver_count; i++) {
     printf ("%s", devices[i]->short_name);
     if (devices[i]->type == AO_TYPE_LIVE)
@@ -274,23 +274,23 @@
   }
 
   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 an input buffer of 'n' kilobytes\n"
-	 "  -p n, --prebuffer n  load n%% of the input buffer before playing\n"
-	 "  -v, --verbose  display progress and other status information\n"
-	 "  -q, --quiet    don't display anything (no title)\n"
-	 "  -x n, --nth    play every 'n'th block\n"
-	 "  -y n, --ntimes repeat every played block 'n' times\n"
-	 "  -z, --shuffle  shuffle play\n"
+	 _("  -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).\n"
+	 "                             See man page for more info.\n"
+	 "  -b n, --buffer n           use an input buffer of 'n' kilobytes\n"
+	 "  -p n, --prebuffer n        load n%% of the input buffer before playing\n"
+	 "  -v,   --verbose            display progress and other status information\n"
+	 "  -q,   --quiet              don't display anything (no title)\n"
+	 "  -x n, --nth                play every 'n'th block\n"
+	 "  -y n, --ntimes             repeat every played block 'n' times\n"
+	 "  -z,   --shuffle            shuffle play\n"
 	 "\n"
 	 "ogg123 will skip to the next song on SIGINT (Ctrl-C); two SIGINTs within\n"
 	 "s milliseconds make ogg123 terminate.\n"
-	 "  -l, --delay=s  set s [milliseconds] (default 500).\n"));
+	 "  -l,   --delay=s            set s [milliseconds] (default 500).\n"));
 }
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cmdline_options.c ogg123/cmdline_options.c
--- ogg123.orig/cmdline_options.c	Sat Oct  5 17:38:52 2002
+++ ogg123/cmdline_options.c	Sat Oct  5 17:53:09 2002
@@ -45,6 +45,7 @@
     {"nth", required_argument, 0, 'x'},
     {"ntimes", required_argument, 0, 'y'},
     {"shuffle", no_argument, 0, 'z'},
+    {"remote", no_argument, 0, 'R'},    
     {"list", required_argument, 0, '@'},
     {"audio-buffer", required_argument, 0, 0},
     {0, 0, 0, 0}
@@ -63,7 +64,7 @@
   audio_device_t *current;
   int ret;
 
-  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qvVx:y:z@:",
+  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qRvVx:y:z@:",
 				  long_options, &option_index))) {
 
       switch (ret) {
@@ -171,6 +172,11 @@
 	ogg123_opts->verbosity = 0;
 	break;
 	
+
+      case 'R':
+	ogg123_opts->remote = 1;
+	break;
+
       case 'v':
 	ogg123_opts->verbosity++;
 	break;
@@ -286,6 +292,7 @@
 	 "  -p n, --prebuffer n        load n%% of the input buffer before playing\n"
 	 "  -v,   --verbose            display progress and other status information\n"
 	 "  -q,   --quiet              don't display anything (no title)\n"
+	 "  -R,   --remote             remote interface\n"
 	 "  -x n, --nth                play every 'n'th block\n"
 	 "  -y n, --ntimes             repeat every played block 'n' times\n"
 	 "  -z,   --shuffle            shuffle play\n"
diff -urN -X diff.ignore ogg123.orig/ogg123.1 ogg123/ogg123.1
--- ogg123.orig/ogg123.1	Fri Jul 12 04:24:49 2002
+++ ogg123/ogg123.1	Sat Oct  5 17:44:19 2002
@@ -9,7 +9,7 @@
 .SH SYNOPSIS
 .B ogg123 
 [
-.B -vqzVh
+.B -vqRzVh
 ] [
 .B -k
 .I seconds 
@@ -87,6 +87,8 @@
 for a list of valid options for each device.
 .IP "-q, --quiet"
 Quiet mode.  No messages are displayed.
+.IP "-R, --remote"
+Activate the remote interface. Read README.remote for more information.
 .IP "-V, --version"
 Display version information.
 .IP "-v, --verbose"
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 17:08:59 2002
+++ ogg123/ogg123.c	Sat Oct  5 17:53:47 2002
@@ -42,6 +42,7 @@
 #include "status.h"
 #include "playlist.h"
 #include "compat.h"
+#include "control_generic.h"
 
 #include "ogg123.h"
 #include "i18n.h"
@@ -127,6 +128,7 @@
 {
   opts->verbosity = 2;
   opts->shuffle = 0;
+  opts->remote = 0;
   opts->delay = 500;
   opts->nth = 1;
   opts->ntimes = 1;
@@ -288,40 +290,40 @@
   audio_play_arg.devices = options.devices;
   audio_play_arg.stat_format = stat_format_create();
 
-  /* Add remaining arguments to playlist */
-  for (i = option_index; i < argc; i++) {
-    if (stat(argv[i], &stat_buf) == 0) {
-
-      if (S_ISDIR(stat_buf.st_mode)) {
-	if (playlist_append_directory(options.playlist, argv[i]) == 0)
-	  fprintf(stderr, 
-		  _("Warning: Could not read directory %s.\n"), argv[i]);
-      } else {
-	playlist_append_file(options.playlist, argv[i]);
-      }
-    } else /* If we can't stat it, it might be a non-disk source */
-      playlist_append_file(options.playlist, argv[i]);
-
+  if (!options.remote) {
+    /* Add remaining arguments to playlist */
+    for (i = option_index; i < argc; i++) {
+      if (stat(argv[i], &stat_buf) == 0) {
+
+        if (S_ISDIR(stat_buf.st_mode)) {
+          if (playlist_append_directory(options.playlist, argv[i]) == 0)
+            fprintf(stderr, 
+                    _("Warning: Could not read directory %s.\n"), argv[i]);
+        } else {
+          playlist_append_file(options.playlist, argv[i]);
+        }
+      } else /* If we can't stat it, it might be a non-disk source */
+        playlist_append_file(options.playlist, argv[i]);
+    }
 
+    /* Do we have anything left to play? */
+    if (playlist_length(options.playlist) == 0) {
+      cmdline_usage();
+      exit(1);
+    } else {
+      playlist_array = playlist_to_array(options.playlist);
+      playlist_destroy(options.playlist);
+      options.playlist = NULL;
+    }
   }
 
-
-  /* Do we have anything left to play? */
-  if (playlist_length(options.playlist) == 0) {
-    cmdline_usage();
-    exit(1);
+  if (options.remote) {
+    status_set_verbosity(0);
   } else {
-    playlist_array = playlist_to_array(options.playlist);
-    playlist_destroy(options.playlist);
-    options.playlist = NULL;
+    /* Don't use status_message until after this point! */
+    status_set_verbosity(options.verbosity);
   }
-
-  /* Don't use status_message until after this point! */
-  status_set_verbosity(options.verbosity);
-
-  print_audio_devices_info(options.devices);
-
-  
+    
   /* Setup buffer */ 
   if (options.buffer_size > 0) {
     audio_buffer = buffer_create(options.buffer_size,
@@ -355,16 +357,24 @@
   }
 
   /* Setup signal handlers and callbacks */
-
   audio_buffer_to_cleanup = audio_buffer;
   ATEXIT (exit_cleanup);
-  signal (SIGINT, signal_handler);
-  signal (SIGTSTP, signal_handler);
-  signal (SIGCONT, signal_handler);
 
+  if (options.remote) {
+    control_generic(&audio_play_arg, audio_buffer);
+  } else {
+    print_audio_devices_info(options.devices);
+  
+    signal (SIGINT, signal_handler);
+    signal (SIGTSTP, signal_handler);
+    signal (SIGCONT, signal_handler);
+
+    control_default(&audio_play_arg, audio_buffer, playlist_array);
+    playlist_array_destroy(playlist_array);
+  }
 
-  control_default(&audio_play_arg, audio_buffer, playlist_array);
-  playlist_array_destroy(playlist_array);
+  free(playlist_array);
+  stat_format_cleanup(audio_play_arg.stat_format);
 
   exit (0);
 }
diff -urN -X diff.ignore ogg123.orig/ogg123.h ogg123/ogg123.h
--- ogg123.orig/ogg123.h	Sat Oct  5 14:32:34 2002
+++ ogg123/ogg123.h	Sat Oct  5 17:53:05 2002
@@ -28,6 +28,7 @@
   long int verbosity;         /* Verbose output if > 1, quiet if 0 */
 
   int shuffle;                /* Should we shuffle playing? */
+  int remote;                 /* Use the remote interface */
   ogg_int64_t delay;          /* delay (in millisecs) for skip to next song */
   int nth;                    /* Play every nth chunk */
   int ntimes;                 /* Play every chunk n times */
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cmdline_options.c ogg123/cmdline_options.c
--- ogg123.orig/cmdline_options.c	Sat Oct  5 18:31:58 2002
+++ ogg123/cmdline_options.c	Sat Oct  5 18:29:02 2002
@@ -137,36 +137,36 @@
 	}
 	break;
 
-	case 'k':
-	  ogg123_opts->seekpos = atof(optarg);
-	  break;
+      case 'k':
+	ogg123_opts->seekpos = atof(optarg);
+	break;
 	  
-	case 'l':
-	  ogg123_opts->delay = atoi(optarg);
-	  break;
+      case 'l':
+	ogg123_opts->delay = atoi(optarg);
+	break;
 	  
-	case 'o':
-	  if (optarg && !add_ao_option(current_options, optarg)) {
-	    status_error(_("=== Incorrect option format: %s.\n"), optarg);
-	    exit(1);
-	  }
-	  break;
+      case 'o':
+	if (optarg && !add_ao_option(current_options, optarg)) {
+	  status_error(_("=== Incorrect option format: %s.\n"), optarg);
+	  exit(1);
+	}
+	break;
 
-	case 'h':
-	  cmdline_usage();
-	  exit(0);
-	  break;
+      case 'h':
+	cmdline_usage();
+	exit(0);
+	break;
 	  
-	case 'p':
-	  ogg123_opts->input_prebuffer = atof (optarg);
-	  if (ogg123_opts->input_prebuffer < 0.0f || 
-	      ogg123_opts->input_prebuffer > 100.0f) {
+      case 'p':
+	ogg123_opts->input_prebuffer = atof (optarg);
+	if (ogg123_opts->input_prebuffer < 0.0f || 
+	    ogg123_opts->input_prebuffer > 100.0f) {
 
-	    status_error (_("--- Prebuffer value invalid. Range is 0-100.\n"));
-	    ogg123_opts->input_prebuffer = 
-	      ogg123_opts->input_prebuffer < 0.0f ? 0.0f : 100.0f;
-	  }
-	  break;
+	  status_error (_("--- Prebuffer value invalid. Range is 0-100.\n"));
+	  ogg123_opts->input_prebuffer = 
+	    ogg123_opts->input_prebuffer < 0.0f ? 0.0f : 100.0f;
+	}
+	break;
 
       case 'q':
 	ogg123_opts->verbosity = 0;
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cmdline_options.c ogg123/cmdline_options.c
--- ogg123.orig/cmdline_options.c	Sat Oct  5 18:58:43 2002
+++ ogg123/cmdline_options.c	Sat Oct  5 18:58:55 2002
@@ -45,6 +45,7 @@
     {"nth", required_argument, 0, 'x'},
     {"ntimes", required_argument, 0, 'y'},
     {"shuffle", no_argument, 0, 'z'},
+    {"title", no_argument, 0, 'T'},
     {"remote", no_argument, 0, 'R'},    
     {"list", required_argument, 0, '@'},
     {"audio-buffer", required_argument, 0, 0},
@@ -64,7 +65,7 @@
   audio_device_t *current;
   int ret;
 
-  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qRvVx:y:z@:",
+  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qRTvVx:y:z@:",
 				  long_options, &option_index))) {
 
       switch (ret) {
@@ -177,6 +178,10 @@
 	ogg123_opts->remote = 1;
 	break;
 
+      case 'T':
+	ogg123_opts->title = 1;
+	break;
+
       case 'v':
 	ogg123_opts->verbosity++;
 	break;
@@ -293,6 +298,7 @@
 	 "  -v,   --verbose            display progress and other status information\n"
 	 "  -q,   --quiet              don't display anything (no title)\n"
 	 "  -R,   --remote             remote interface\n"
+	 "  -T,   --title              Set the xterm title to title of playing song\n"
 	 "  -x n, --nth                play every 'n'th block\n"
 	 "  -y n, --ntimes             repeat every played block 'n' times\n"
 	 "  -z,   --shuffle            shuffle play\n"
diff -urN -X diff.ignore ogg123.orig/ogg123.1 ogg123/ogg123.1
--- ogg123.orig/ogg123.1	Sat Oct  5 18:58:43 2002
+++ ogg123/ogg123.1	Sat Oct  5 18:50:28 2002
@@ -9,7 +9,7 @@
 .SH SYNOPSIS
 .B ogg123 
 [
-.B -vqRzVh
+.B -vqRTzVh
 ] [
 .B -k
 .I seconds 
@@ -89,6 +89,9 @@
 Quiet mode.  No messages are displayed.
 .IP "-R, --remote"
 Activate the remote interface. Read README.remote for more information.
+.IP "-T, --title"
+Set the title of an xterm to the file currently being played. Only done if
+$TERM is set to xterm.
 .IP "-V, --version"
 Display version information.
 .IP "-v, --verbose"
diff -urN -X diff.ignore ogg123.orig/ogg123.c ogg123/ogg123.c
--- ogg123.orig/ogg123.c	Sat Oct  5 18:58:43 2002
+++ ogg123/ogg123.c	Sat Oct  5 18:50:28 2002
@@ -129,6 +129,7 @@
   opts->verbosity = 2;
   opts->shuffle = 0;
   opts->remote = 0;
+  opts->title = 0;
   opts->delay = 500;
   opts->nth = 1;
   opts->ntimes = 1;
@@ -479,7 +480,10 @@
     /* Show which file we are playing */
     decoder_callbacks.printf_metadata(decoder_callbacks_arg, 1,
 				      _("Playing: %s"), source_string);
-
+    if (options.title && strcmp("xterm", getenv("TERM")) == 0) {
+      printf("'[\033]0;%s\007]", source_string);
+    }
+    
     /* Skip over audio */
     if (options.seekpos > 0.0) {
       if (!format->seek(decoder, options.seekpos, DECODER_SEEK_START))
diff -urN -X diff.ignore ogg123.orig/ogg123.h ogg123/ogg123.h
--- ogg123.orig/ogg123.h	Sat Oct  5 18:58:43 2002
+++ ogg123/ogg123.h	Sat Oct  5 18:50:28 2002
@@ -29,6 +29,7 @@
 
   int shuffle;                /* Should we shuffle playing? */
   int remote;                 /* Use the remote interface */
+  int title;                  /* set the xterm title to the current filename */
   ogg_int64_t delay;          /* delay (in millisecs) for skip to next song */
   int nth;                    /* Play every nth chunk */
   int ntimes;                 /* Play every chunk n times */
-------------- next part --------------
diff -urN -X diff.ignore ogg123.orig/cmdline_options.c ogg123/cmdline_options.c
--- ogg123.orig/cmdline_options.c	Sat Oct  5 18:59:56 2002
+++ ogg123/cmdline_options.c	Sat Oct  5 19:01:51 2002
@@ -45,6 +45,7 @@
     {"nth", required_argument, 0, 'x'},
     {"ntimes", required_argument, 0, 'y'},
     {"shuffle", no_argument, 0, 'z'},
+    {"test", no_argument, 0, 't'},
     {"title", no_argument, 0, 'T'},
     {"remote", no_argument, 0, 'R'},    
     {"list", required_argument, 0, '@'},
@@ -65,7 +66,7 @@
   audio_device_t *current;
   int ret;
 
-  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qRTvVx:y:z@:",
+  while (-1 != (ret = getopt_long(argc, argv, "b:c::d:f:hl:k:o:p:qRtTvVx:y:z@:",
 				  long_options, &option_index))) {
 
       switch (ret) {
@@ -173,6 +174,17 @@
 	ogg123_opts->verbosity = 0;
 	break;
 	
+
+      case 't':
+	temp_driver_id = ao_driver_id("null");
+	if (temp_driver_id < 0) {
+	    status_error(_("=== No such device null.\n"), optarg);
+	    exit(1);
+	}
+	free_audio_devices(ogg123_opts->devices);
+	ogg123_opts->devices = append_audio_device(NULL, temp_driver_id, NULL, NULL);
+	current_options = &current->options;
+	break;
 
       case 'R':
 	ogg123_opts->remote = 1;
diff -urN -X diff.ignore ogg123.orig/ogg123.1 ogg123/ogg123.1
--- ogg123.orig/ogg123.1	Sat Oct  5 18:59:56 2002
+++ ogg123/ogg123.1	Sat Oct  5 19:03:20 2002
@@ -9,7 +9,7 @@
 .SH SYNOPSIS
 .B ogg123 
 [
-.B -vqRTzVh
+.B -vqRtTzVh
 ] [
 .B -k
 .I seconds 
@@ -89,6 +89,8 @@
 Quiet mode.  No messages are displayed.
 .IP "-R, --remote"
 Activate the remote interface. Read README.remote for more information.
+.IP "-t, --test"
+Test mode. Output is decoded but not written. This is equivalent to -d null
 .IP "-T, --title"
 Set the title of an xterm to the file currently being played. Only done if
 $TERM is set to xterm.


More information about the Vorbis-dev mailing list