[xiph-cvs] cvs commit: vorbis-tools/vorbiscomment Makefile.am vcedit.c

Stan Seibert volsung at xiph.org
Tue Dec 18 18:53:02 PST 2001



volsung     01/12/18 18:53:01

  Modified:    .        acinclude.m4 configure.in
               ogg123   Makefile.am buffer.c buffer.h ogg123.1 ogg123.c
                        ogg123.h ogg123rc-example
               oggenc   Makefile.am
               ogginfo  Makefile.am
               share    Makefile.am
               vcut     vcut.c
               vorbiscomment Makefile.am vcedit.c
  Added:       ogg123   audio.c audio.h callbacks.c callbacks.h
                        cfgfile_options.c cfgfile_options.h
                        cmdline_options.c cmdline_options.h compat.h
                        file_transport.c format.c format.h http_transport.c
                        oggvorbis_format.c status.c status.h transport.c
                        transport.h
  Removed:     ogg123   nullbuffer.c
  Log:
  Finally, we merge my branch onto the head.  Duck and cover.

Revision  Changes    Path
1.14      +291 -63   vorbis-tools/acinclude.m4

Index: acinclude.m4
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/acinclude.m4,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- acinclude.m4	2001/10/28 02:51:16	1.13
+++ acinclude.m4	2001/12/19 02:52:52	1.14
@@ -28,7 +28,7 @@
     OGG_CFLAGS="-I$ogg_includes"
   elif test "x$ogg_prefix" != "x" ; then
     OGG_CFLAGS="-I$ogg_prefix/include"
-  elif test "$prefix" != "xNONE"; then
+  elif test "x$prefix" != "xNONE"; then
     OGG_CFLAGS="-I$prefix/include"
   fi
 
@@ -102,68 +102,6 @@
   rm -f conf.oggtest
 ])
 
-dnl Shamelessly stolen from Joerg Schilling's star.
-dnl Copyright 1998 J. Schilling
-
-dnl Checks if mmap() works to get shared memory
-dnl Defines HAVE_SMMAP on success.
-AC_DEFUN(AC_FUNC_SMMAP,
-[AC_CACHE_CHECK([if mmap works to get shared memory], ac_cv_func_smmap,
-                [AC_TRY_RUN([
-#include <sys/types.h>
-#include <sys/mman.h>
-
-char *
-mkshare()
-{
-        int     size = 8192;
-        int     f;
-        char    *addr;
-
-        if ((f = open("/dev/zero", 2)) < 0)
-                exit(1);
-        addr = mmap(0, size, PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
-        if (addr == (char *)-1)
-                exit(1);
-        close(f);
-
-        return (addr);
-}
-
-main()
-{
-        char    *addr;
-        
-        addr = mkshare(8192);
-        *addr = 'I';
-
-        switch (fork()) {
-
-        case -1:
-                printf("help\n"); exit(1);
-
-        case 0: /* child */
-                *addr = 'N';
-                _exit(0);
-                break;
-        default: /* parent */
-                wait(0);
-                sleep(1);
-                break;
-        }
-
-        if (*addr != 'N')
-                exit(1);
-        exit(0);
-}
-], 
-                [ac_cv_func_smmap=yes],
-                [ac_cv_func_smmap=no],
-                [ac_cv_func_smmap=no])])
-if test $ac_cv_func_smmap = yes; then
-  AC_DEFINE(HAVE_SMMAP)
-fi])
-
 # Configure paths for libvorbis
 # Jack Moffitt <jack at icecast.org> 10-21-2000
 # Shamelessly stolen from Owen Taylor and Manish Singh
@@ -471,4 +409,294 @@
     AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
       [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
   fi
+])
+
+dnl AM_PATH_CURL([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]])
+dnl Test for libcurl, and define CURL_CFLAGS and CURL_LIBS
+dnl
+AC_DEFUN(AM_PATH_CURL,
+[dnl 
+dnl Get the cflags and libraries
+dnl
+AC_ARG_WITH(curl,[  --with-curl=PFX   Prefix where libcurl is installed (optional)], curl_prefix="$withval", curl_prefix="")
+AC_ARG_WITH(curl-libraries,[  --with-curl-libraries=DIR   Directory where libcurl library is installed (optional)], curl_libraries="$withval", curl_libraries="")
+AC_ARG_WITH(curl-includes,[  --with-curl-includes=DIR   Directory where libcurl header files are installed (optional)], curl_includes="$withval", curl_includes="")
+AC_ARG_ENABLE(curltest, [  --disable-curltest       Do not try to compile and run a test libcurl program],, enable_curltest=yes)
+
+  if test "x$curl_libraries" != "x" ; then
+    CURL_LIBS="-L$curl_libraries"
+  elif test "x$curl_prefix" != "x" ; then
+    CURL_LIBS="-L$curl_prefix/lib"
+  elif test "x$prefix" != "xNONE" ; then
+    CURL_LIBS="-L$prefix/lib"
+  fi
+
+  CURL_LIBS="$CURL_LIBS -lcurl"
+
+  if test "x$curl_includes" != "x" ; then
+    CURL_CFLAGS="-I$curl_includes"
+  elif test "x$curl_prefix" != "x" ; then
+    CURL_CFLAGS="-I$curl_prefix/include"
+  elif test "x$prefix" != "xNONE"; then
+    CURL_CFLAGS="-I$prefix/include"
+  fi
+
+  AC_MSG_CHECKING(for libcurl)
+  no_curl=""
+
+
+  if test "x$enable_curltest" = "xyes" ; then
+    ac_save_CFLAGS="$CFLAGS"
+    ac_save_LIBS="$LIBS"
+    CFLAGS="$CFLAGS $CURL_CFLAGS"
+    LIBS="$LIBS $CURL_LIBS"
+dnl
+dnl Now check if the installed libcurl is sufficiently new.
+dnl
+      rm -f conf.curltest
+      AC_TRY_RUN([
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+
+int main ()
+{
+  system("touch conf.curltest");
+  return 0;
+}
+
+],, no_curl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+  fi
+
+  if test "x$no_curl" = "x" ; then
+     AC_MSG_RESULT(yes)
+     ifelse([$1], , :, [$1])     
+  else
+     AC_MSG_RESULT(no)
+     if test -f conf.curltest ; then
+       :
+     else
+       echo "*** Could not run libcurl test program, checking why..."
+       CFLAGS="$CFLAGS $CURL_CFLAGS"
+       LIBS="$LIBS $CURL_LIBS"
+       AC_TRY_LINK([
+#include <stdio.h>
+#include <curl/curl.h>
+],     [ return 0; ],
+       [ echo "*** The test program compiled, but did not run. This usually means"
+       echo "*** that the run-time linker is not finding libcurl or finding the wrong"
+       echo "*** version of libcurl. If it is not finding libcurl, you'll need to set your"
+       echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point"
+       echo "*** to the installed location  Also, make sure you have run ldconfig if that"
+       echo "*** is required on your system"
+       echo "***"
+       echo "*** If you have an old version installed, it is best to remove it, although"
+       echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"],
+       [ echo "*** The test program failed to compile or link. See the file config.log for the"
+       echo "*** exact error that occured. This usually means libcurl was incorrectly installed"
+       echo "*** or that you have moved libcurl since it was installed." ])
+       CFLAGS="$ac_save_CFLAGS"
+       LIBS="$ac_save_LIBS"
+     fi
+     CURL_CFLAGS=""
+     CURL_LIBS=""
+     ifelse([$2], , :, [$2])
+  fi
+  AC_SUBST(CURL_CFLAGS)
+  AC_SUBST(CURL_LIBS)
+  rm -f conf.curltest
+])
+
+
+dnl ACX_PTHREAD macro by Steven G. Johnson <stevenj at alum.mit.edu> and
+dnl Alejandro Forero Cuervo <bachue at bachue.com>.  Found at:
+dnl http://www.gnu.org/software/ac-archive/Installed_Packages/acx_pthread.html
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+acx_pthread_ok=no
+
+# First, check if the POSIX threads header, pthread.h, is available.
+# If it isn't, don't bother looking for the threads libraries.
+AC_CHECK_HEADER(pthread.h, , acx_pthread_ok=noheader)
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+        AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test x"$acx_pthread_ok" = xno; then
+                PTHREAD_LIBS=""
+                PTHREAD_CFLAGS=""
+        fi
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+fi
+
+# Create a list of thread flags to try.  Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread pthread -pthread -pthreads -mthreads --thread-safe -mt"
+
+# The ordering *is* (sometimes) important.  Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+#       other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+#      doesn't hurt to check since this sometimes defines pthreads too;
+#      also defines -D_REENTRANT)
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+
+case "${host_cpu}-${host_os}" in
+        *solaris*)
+
+        # On Solaris (at least, for some versions), libc contains stubbed
+        # (non-functional) versions of the pthreads routines, so link-based
+        # tests will erroneously succeed.  (We need to link with -pthread or
+        # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather
+        # a function called by this macro, so we could check for that, but
+        # who knows whether they'll stub that too in a future libc.)  So,
+        # we'll just look for -pthreads and -lpthread first:
+        acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+        ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+        case $flag in
+                none)
+                AC_MSG_CHECKING([whether pthreads work without any flags])
+                ;;
+
+                -*)
+                AC_MSG_CHECKING([whether pthreads work with $flag])
+                PTHREAD_CFLAGS="$flag"
+                ;;
+
+                *)
+                AC_MSG_CHECKING([for the pthreads library -l$flag])
+                PTHREAD_LIBS="-l$flag"
+                ;;
+        esac
+
+        save_LIBS="$LIBS"
+        save_CFLAGS="$CFLAGS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Check for various functions.  We must include pthread.h,
+        # since some functions may be macros.  (On the Sequent, we
+        # need a special flag -Kthread to make this header compile.)
+        # We check for pthread_join because it is in -lpthread on IRIX
+        # while pthread_create is in libc.  We check for pthread_attr_init
+        # due to DEC craziness with -lpthreads.  We check for
+        # pthread_cleanup_push because it is one of the few pthread
+        # functions on Solaris that doesn't have a non-functional libc stub.
+        # We try pthread_create on general principles.
+        AC_TRY_LINK([#include <pthread.h>],
+                    [pthread_t th; pthread_join(th, 0);
+                     pthread_attr_init(0); pthread_cleanup_push(0, 0);
+                     pthread_create(0,0,0,0);
+                     pthread_cancel(0); pthread_cleanup_pop(0); ],
+                    [acx_pthread_ok=yes])
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        AC_MSG_RESULT($acx_pthread_ok)
+        if test "x$acx_pthread_ok" = xyes; then
+                break;
+        fi
+
+        PTHREAD_LIBS=""
+        PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+        save_LIBS="$LIBS"
+        LIBS="$PTHREAD_LIBS $LIBS"
+        save_CFLAGS="$CFLAGS"
+        CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+        # Detect AIX lossage: threads are created detached by default
+        # and the JOINABLE attribute has a nonstandard name (UNDETACHED).
+        AC_MSG_CHECKING([for joinable pthread attribute])
+        AC_TRY_LINK([#include <pthread.h>],
+                    [int attr=PTHREAD_CREATE_JOINABLE;],
+                    ok=PTHREAD_CREATE_JOINABLE, ok=unknown)
+        if test x"$ok" = xunknown; then
+                AC_TRY_LINK([#include <pthread.h>],
+                            [int attr=PTHREAD_CREATE_UNDETACHED;],
+                            ok=PTHREAD_CREATE_UNDETACHED, ok=unknown)
+        fi
+        if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then
+                AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok,
+                          [Define to the necessary symbol if this constant
+                           uses a non-standard name on your system.])
+        fi
+        AC_MSG_RESULT(${ok})
+        if test x"$ok" = xunknown; then
+                AC_MSG_WARN([we do not know how to create joinable pthreads])
+        fi
+
+        AC_MSG_CHECKING([if more special flags are required for pthreads])
+        flag=no
+        case "${host_cpu}-${host_os}" in
+                *-aix* | *-freebsd*)     flag="-D_THREAD_SAFE";;
+                *solaris* | alpha*-osf*) flag="-D_REENTRANT";;
+        esac
+        AC_MSG_RESULT(${flag})
+        if test "x$flag" != xno; then
+                PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+        fi
+
+        LIBS="$save_LIBS"
+        CFLAGS="$save_CFLAGS"
+
+        # More AIX lossage: must compile with cc_r
+        AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+else
+        PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+        ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+        :
+else
+        acx_pthread_ok=no
+        $2
+fi
+
 ])

1.32      +10 -19    vorbis-tools/configure.in

Index: configure.in
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/configure.in,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -r1.31 -r1.32
--- configure.in	2001/10/02 03:03:41	1.31
+++ configure.in	2001/12/19 02:52:52	1.32
@@ -19,13 +19,7 @@
 
 AM_PROG_LIBTOOL
 
-dnl --------------------------------------------------  
-dnl Additional arguments
-dnl --------------------------------------------------  
 
-AC_ARG_WITH(ogg, [  --with-ogg=DIR          Set where the Ogg library is located]) 
-AC_ARG_WITH(vorbis, [  --with-vorbis=DIR          Set where the Vorbis library is located]) 
-
 dnl --------------------------------------------------
 dnl Set build flags based on environment
 dnl --------------------------------------------------
@@ -82,35 +76,29 @@
 LDFLAGS="$LDFLAGS $ldflags_save"
 
 dnl --------------------------------------------------
-dnl Check for headers
-dnl --------------------------------------------------
-
-dnl none
-
-dnl --------------------------------------------------
-dnl Check for typedefs, structures, etc
-dnl --------------------------------------------------
-
-dnl none
-
-dnl --------------------------------------------------
 dnl Check for libraries
 dnl --------------------------------------------------
 
 AM_PATH_OGG(,AC_MSG_ERROR(Ogg needed!))
 AM_PATH_VORBIS(,AC_MSG_ERROR(Vorbis needed!))
 AM_PATH_AO(,AC_MSG_ERROR(libao needed!))
+AM_PATH_CURL(,AC_MSG_ERROR(libcurl needed!))
+
+ACX_PTHREAD(,AC_MSG_ERROR(POSIX threads required!))
+
 SOCKET_LIBS=
 AC_CHECK_LIB(socket, socket, SOCKET_LIBS="-lsocket")
 AC_CHECK_LIB(nsl, gethostbyname, SOCKET_LIBS="-lnsl $SOCKET_LIBS")
+
 SHARE_LIBS='$(top_srcdir)/share/libutf8.a $(top_srcdir)/share/libgetopt.a'
+SHARE_CFLAGS='-I$(top_srcdir)/include'
 
 dnl --------------------------------------------------
 dnl Check for library functions
 dnl --------------------------------------------------
 
 AM_ICONV
-AC_FUNC_SMMAP
+AC_CHECK_FUNCS(atexit on_exit)
 AM_LANGINFO_CODESET
 
 dnl --------------------------------------------------
@@ -131,6 +119,9 @@
 AC_SUBST(DEBUG)
 AC_SUBST(PROFILE)
 AC_SUBST(SOCKET_LIBS)
+AC_SUBST(SHARE_CFLAGS)
 AC_SUBST(SHARE_LIBS)
+AC_SUBST(CURL_CFLAGS)
+AC_SUBST(CURL_LIBS)
 
 AC_OUTPUT(Makefile include/Makefile share/Makefile oggenc/Makefile oggenc/man/Makefile ogg123/Makefile vorbiscomment/Makefile vcut/Makefile ogginfo/Makefile debian/Makefile)

1.17      +11 -6     vorbis-tools/ogg123/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/Makefile.am,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- Makefile.am	2001/09/22 22:49:49	1.16
+++ Makefile.am	2001/12/19 02:52:53	1.17
@@ -8,15 +8,20 @@
 mandir = @MANDIR@
 man_MANS = ogg123.1
 
-INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ @AO_CFLAGS@
+INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ @AO_CFLAGS@ @CURL_CFLAGS@ \
+           @PTHREAD_CFLAGS@ @SHARE_CFLAGS@
 
 ogg123_LDADD = @VORBISFILE_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ @AO_LIBS@ \
-		@SOCKET_LIBS@ @SHARE_LIBS@
+               @SOCKET_LIBS@ @SHARE_LIBS@ @CURL_LIBS@ @PTHREAD_CFLAGS@ \
+               @PTHREAD_LIBS@
 ogg123_DEPENDENCIES = @SHARE_LIBS@
-
-ogg123_SOURCES = ogg123.c ao_interface.c buffer.c ogg123.h buffer.h
-## Comment the above and uncomment the next line to disable the buffer support
-##ogg123_SOURCES = ogg123.c ao_interface.c nullbuffer.c ogg123.h buffer.h
+ogg123_SOURCES = audio.c buffer.c callbacks.c \
+                 cfgfile_options.c cmdline_options.c \
+                 file_transport.c format.c http_transport.c \
+                 ogg123.c oggvorbis_format.c status.c transport.c     \
+                 audio.h buffer.h callbacks.h compat.h \
+                 cfgfile_options.h cmdline_options.h \
+                 format.h ogg123.h status.h transport.h
 
 EXTRA_DIST = $(man_MANS) $(doc_DATA)
 

1.9       +664 -166  vorbis-tools/ogg123/buffer.c

Index: buffer.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/buffer.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- buffer.c	2001/10/02 03:10:42	1.8
+++ buffer.c	2001/12/19 02:52:53	1.9
@@ -1,210 +1,708 @@
-/* buffer.c
- *  buffering code for ogg123. This is Unix-specific. Other OSes anyone?
- *
- * Thanks to Lee McLouchlin's buffer(1) for inspiration; no code from
- * that program is used in this buffer.
- */
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
 
+ last mod: $Id: buffer.c,v 1.9 2001/12/19 02:52:53 volsung Exp $
+
+ ********************************************************************/
+
+
 #include <sys/types.h>
-#if HAVE_SMMAP
-#include <sys/mman.h>
-#else
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#endif
+#include <sys/wait.h>
 #include <sys/time.h>
-#include <unistd.h> /* for fork and pipe*/
+#include <string.h>
 #include <fcntl.h>
 #include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
 
-#include "ogg123.h"
+#include "compat.h"
 #include "buffer.h"
+
+#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); }
+#else
+#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)); }
+
+
+/* -------------------- Private Functions ------------------ */
+
+void buffer_init_vars (buf_t *buf)
+{
+  /* Initialize buffer flags */
+  buf->prebuffering = buf->prebuffer_size > 0;
+  buf->paused = 0;
+  buf->eos = 0;
+  buf->abort_write = 0;
+  
+  buf->curfill = 0;
+  buf->start = 0;
+  buf->position = 0;
+  buf->position_end = 0;
+}
 
-/* Initialize the buffer structure. */
-void buffer_init (buf_t *buf, long size)
+void buffer_thread_init (buf_t *buf)
 {
-  buf->status = 0;
-  buf->reader = buf->writer = buf->buffer;
-  buf->end = buf->buffer + (size - 1);
+  sigset_t set;
+
+  DEBUG("Enter buffer_thread_init");
+
+  /* Block signals to this thread */
+  sigfillset(&set);
+  sigaddset(&set, SIGINT);
+  sigaddset(&set, SIGTSTP);
+  sigaddset(&set, SIGCONT);
+  if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
+    DEBUG("pthread_sigmask failed");
 }
 
-/* Main write loop. No semaphores. No deadlock. No problem. I hope. */
-void writer_main (volatile buf_t *buf, devices_t *d)
+
+void buffer_thread_cleanup (void *arg)
 {
-  devices_t *d1;
-  signal (SIGINT, SIG_IGN);
+  buf_t *buf = (buf_t *)arg;
+
+  DEBUG("Enter buffer_thread_cleanup");
+
+  /* Cleanup thread data structures */
+  pthread_mutex_unlock(&buf->mutex);
+  pthread_mutex_destroy(&buf->mutex);
+  pthread_cond_destroy(&buf->playback_cond);
+  pthread_cond_destroy(&buf->write_cond);
+}
 
-  while (! (buf->status & STAT_SHUTDOWN && buf->reader == buf->writer))
-    {
-      /* Writer just waits on reader to be done with buf_write.
-       * Reader must ensure that we don't deadlock. */
 
-      write (buf->fds[1], "1", 1); /* This identifier could hold a lot
-				    * more detail in the future. */
+action_t *malloc_action (action_func_t action_func, void *action_arg)
+{
+  action_t *action;
 
-      if (buf->status & STAT_FLUSH) {
-	buf->reader = buf->writer;
-	buf->status &= ~STAT_FLUSH;
-      }
+  action = malloc(sizeof(action_t));
+  
+  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;
 
-      while (buf->reader == buf->writer && !(buf->status & STAT_SHUTDOWN));
+  return action;
+}
 
-      if (buf->reader == buf->writer) break;
 
-      /* devices_write (buf->writer->data, buf->writer->len, d); */
-      {
-	d1 = d;
-	while (d1 != NULL) {
-	  ao_play(d1->device, buf->writer->data, buf->writer->len);
-	  d1 = d1->next_device;
-	}
-      }
+/* insert = 1:  Make this action the first action associated with this position
+   insert = 0:  Make this action the last action associated with this position
+*/
+#define INSERT 1
+#define APPEND 0
+void in_order_add_action (action_t **action_list, action_t *action, int insert)
+{
+  insert = insert > 0 ? 1 : 0;  /* Clamp in case caller messed up */
+
+  while (*action_list != NULL && 
+	 (*action_list)->position <= (action->position + insert))
+    action_list = &((*action_list)->next);
 
-      if (buf->writer == buf->end)
-	buf->writer = (chunk_t *)buf->buffer;
-      else
-	buf->writer++;
-   }
-  buf->status = 0;
-  write (buf->fds[1], "2", 1);
-  _exit(0);
+  action->next = *action_list;
+  *action_list = action;
 }
 
-/* fork_writer is called to create the writer process. This creates
- * the shared memory segment of 'size' chunks, and returns a pointer
- * to the buffer structure that is shared. Just pass this straight to
- * submit_chunk and all will be happy. */
 
-buf_t *fork_writer (long size, devices_t *d)
+void execute_actions (buf_t *buf, action_t **action_list, ogg_int64_t position)
 {
-  int childpid;
-  buf_t *buf;
+  action_t *action;
 
-#if HAVE_SMMAP
-  int fd;
+  while (*action_list != NULL && (*action_list)->position <= position) {
+    action = *action_list;
+    action->action_func(buf, action->arg);
+    
+    *action_list = (*action_list)->next;
+    free(action);
+  }
+}
 
-  if ((fd = open("/dev/zero", O_RDWR)) < 0)
-    {
-      perror ("cannot open /dev/zero");
-      exit (1);
+
+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");
+  
+  buffer_thread_init(buf);
+
+  pthread_cleanup_push(buffer_thread_cleanup, buf);
+  
+  DEBUG("Start main play loop");
+  
+  /* 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("Waiting for more data to play.");
+      COND_WAIT(buf->playback_cond, buf->mutex);
     }
-  if ((buf = (buf_t *) mmap (0, sizeof(buf_t) + sizeof (chunk_t) * (size - 1),
-                             PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED)
-    {
-      perror("mmap");
-      exit(1);
+
+    DEBUG("Ready to play");
+    
+    UNLOCK_MUTEX(buf->mutex);
+
+    pthread_testcancel();
+
+    /* Don't need to lock buffer while running actions since position
+       won't change.  We clear out any actions before we compute the
+       dequeue size so we don't consider actions that need to
+       run right now.  */
+    execute_actions(buf, &buf->actions, buf->position);
+
+    LOCK_MUTEX(buf->mutex);
+
+    /* Need to be locked while we check things. */
+    write_amount = compute_dequeue_size(buf, buf->audio_chunk_size);
+
+    pthread_testcancel();
+
+    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, 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, position = %ld", buf->curfill,
+	  buf->position);
+
+    /* 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");
+
+  return 0;
+}
+
+
+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);
+
+  /* Put the data into the buffer as space is made available */
+  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);
     }
-  close(fd);
-#else
-  /* Get the shared memory segment. */
-  int shmid = shmget (IPC_PRIVATE,
-			  sizeof(buf_t) + sizeof (chunk_t) * (size - 1),
-			  IPC_CREAT|SHM_R|SHM_W);
-
-  if (shmid == -1)
-    {
-      perror ("shmget");
-      exit (1);
+    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);
+    }
+      
+    /* 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");
+}
+
+
+buffer_stats_t *malloc_buffer_stats ()
+{
+  buffer_stats_t *new_stats;
+
+  new_stats = malloc(sizeof(buffer_stats_t));
+
+  if (new_stats == NULL) {
+    fprintf(stderr, "Error: Could not allocate memory in malloc_buffer_stats()\n");
+    exit(1);
+  }
+
+  return new_stats;
+}
+
+
+/* ------------------ Begin public interface ------------------ */
+
+/* --- Buffer allocation --- */
+
+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 (char) * (size - 1));
+
+  if (buf == NULL) {
+      perror ("malloc");
+      exit(1);
+  }
+
+#ifdef DEBUG_BUFFER
+  debugfile = fopen ("/tmp/bufferdebug", "w");
+  setvbuf (debugfile, NULL, _IONBF, 0);
+#endif
+
+  /* Initialize the buffer structure. */
+  DEBUG("buffer_create, size = %ld", size);
+
+  memset (buf, 0, sizeof(*buf));
+
+  buf->write_func = write_func;
+  buf->write_arg = arg;
+
+  /* Setup pthread variables */
+  pthread_mutex_init(&buf->mutex, NULL);
+  pthread_cond_init(&buf->write_cond, NULL);
+  pthread_cond_init(&buf->playback_cond, NULL);
   
-  /* Attach the segment to us (and our kids). Get and store the pointer. */
-  buf = (buf_t *) shmat (shmid, 0, 0);
+  /* Correct for impossible chunk sizes */
+  if (audio_chunk_size > size || audio_chunk_size == 0)
+    audio_chunk_size = size / 2;
+
+  buf->audio_chunk_size = audio_chunk_size;
+
+  buf->prebuffer_size = prebuffer;
+  buf->size = size;
+
+  buf->actions = 0;
+
+  /* Initialize flags */
+  buffer_init_vars(buf);
+
+  return buf;
+}
+
+
+void buffer_reset (buf_t *buf)
+{
+  action_t *action;
+
+  /* Cleanup pthread variables */
+  pthread_mutex_destroy(&buf->mutex);
+  pthread_cond_destroy(&buf->write_cond);
+  pthread_cond_destroy(&buf->playback_cond);
   
-  if (buf == NULL)
-    {
-      perror ("shmat");
-      exit (1);
-    }
+  /* Reinit pthread variables */
+  pthread_mutex_init(&buf->mutex, NULL);
+  pthread_cond_init(&buf->write_cond, NULL);
+  pthread_cond_init(&buf->playback_cond, NULL);
+
+  /* Clear old actions */
+  while (buf->actions != NULL) {
+    action = buf->actions;
+    buf->actions = buf->actions->next;
+    free(action);
+  }
 
-  /* Remove segment after last process detaches it or terminates. */
-  shmctl(shmid, IPC_RMID, 0);
-#endif /* HAVE_SMMAP */
-
-  buffer_init (buf, size);
-  
-  /* Create a pipe for communication between the two processes. Unlike
-   * the first incarnation of an ogg123 buffer, the data is not transferred
-   * over this channel, only occasional "WAKE UP!"'s. */
-
-  if (pipe (buf->fds))
-    {
-      perror ("pipe");
-      exit (1);
-    }
+  buffer_init_vars(buf);
+}
+
+
+void buffer_destroy (buf_t *buf)
+{
+  DEBUG("buffer_destroy");
+  free(buf);
+}
+
+
+/* --- Buffer thread control --- */
 
-  fcntl (buf->fds[1], F_SETFL, O_NONBLOCK);
-  /* write should never block; read should always block. */
+int buffer_thread_start (buf_t *buf)
+{
+  DEBUG("Starting new thread.");
+
+  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)
+{
+  DEBUG("Pausing playback thread");
+
+  LOCK_MUTEX(buf->mutex);
+  buf->paused = 1;
+  UNLOCK_MUTEX(buf->mutex);
+}
+
 
-  fflush (stdout);
-  /* buffer flushes stderr, but stderr is unbuffered (*duh*!) */
+void buffer_thread_unpause (buf_t *buf)
+{
+  DEBUG("Unpausing playback thread");
   
-  childpid = fork();
+  LOCK_MUTEX(buf->mutex);
+  buf->paused = 0;
+  COND_SIGNAL(buf->playback_cond);
+  UNLOCK_MUTEX(buf->mutex);
+}
+
+
+void buffer_thread_kill (buf_t *buf)
+{
+  DEBUG("Attempting to kill playback thread.");
+
+  /* End thread */
+  pthread_cancel (buf->thread);
   
-  if (childpid == -1)
-    {
-      perror ("fork");
-      exit (1);
-    }
+  /* Signal all the playback condition to wake stuff up */
+  COND_SIGNAL(buf->playback_cond);
 
-  if (childpid == 0)
-    {
-      writer_main (buf, d);
-      return NULL;
-    }
-  else
-    return buf;
+  pthread_join(buf->thread, NULL);
+
+  buffer_thread_cleanup(buf);
+
+  DEBUG("Playback thread killed.");
+}
+
+
+/* --- Data buffering functions --- */
+
+void buffer_submit_data (buf_t *buf, char *data, long nbytes)
+{
+  submit_data_chunk (buf, data, nbytes);
 }
 
-void submit_chunk (buf_t *buf, chunk_t chunk)
+size_t buffer_get_data (buf_t *buf, char *data, long nbytes)
 {
-  struct timeval tv;
-  static fd_set set;
+  int write_amount;
+  int orig_size;
 
-  FD_ZERO(&set);
-  FD_SET(buf->fds[0], &set);
+  orig_size = nbytes;
 
-  /* Wait wait, don't step on my sample! */
-  while (!((buf->reader != buf->end && buf->reader + 1 != buf->writer) ||
-	   (buf->reader == buf->end && buf->writer != buf->buffer)))
-    {
-      /* buffer overflow (yikes! no actually it's a GOOD thing) */
-      int ret;
-      char t;
-      
-      tv.tv_sec = 1;
-      tv.tv_usec = 0;
-      ret = select (buf->fds[0]+1, &set, NULL, NULL, &tv);
+  DEBUG("Enter buffer_get_data");
+
+  LOCK_MUTEX(buf->mutex);
+
+  /* Put the data into the buffer as space is made available */
+  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 */
       
-      while (ret-- > 0)
-	read (buf->fds[0], &t, 1);
+      DEBUG("Waiting for more data to copy.");
+      COND_WAIT(buf->playback_cond, buf->mutex);
     }
-	      
-  *(buf->reader) = chunk;
-  /* do this atomically */
-  if (buf->reader == buf->end)
-    buf->reader = buf->buffer;
-  else
-    buf->reader++;
-}
-
-void buffer_flush (buf_t *buf)
-{
-  buf->status |= STAT_FLUSH;
-}
-
-void buffer_shutdown (buf_t *buf)
-{
-  struct timeval tv;
-
-  buf->status |= STAT_SHUTDOWN;
-  while (buf->status != 0)
-    {
-      tv.tv_sec = 1;
-      tv.tv_usec = 0;
-      select (0, NULL, NULL, NULL, &tv);
-    } 
-#ifndef HAVE_SMMAP
-  /* Deallocate the shared memory segment. */
-  shmdt(buf);
-#endif /* HAVE_SMMAP */
+
+    if (buf->abort_write)
+      break;
+
+    /* 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);
+  }
+
+  UNLOCK_MUTEX(buf->mutex);
+  
+  pthread_testcancel();
+
+  DEBUG("Exit buffer_get_data");
+   
+  return orig_size - nbytes;
+}
+
+void buffer_mark_eos (buf_t *buf)
+{
+  DEBUG("buffer_mark_eos");
+
+  LOCK_MUTEX(buf->mutex);
+  buf->eos = 1;
+  buf->prebuffering = 0;
+  COND_SIGNAL(buf->playback_cond);
+  UNLOCK_MUTEX(buf->mutex);
+}
+
+void buffer_abort_write (buf_t *buf)
+{
+  DEBUG("buffer_mark_eos");
+
+  LOCK_MUTEX(buf->mutex);
+  buf->abort_write = 1;
+  COND_SIGNAL(buf->write_cond);
+  UNLOCK_MUTEX(buf->mutex);  
+}
+
+
+/* --- 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_insert_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_end;
+
+  in_order_add_action(&buf->actions, action, INSERT);
+
+  UNLOCK_MUTEX(buf->mutex);
+}
+
+
+void buffer_append_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_end;
+
+  in_order_add_action(&buf->actions, action, APPEND);
+
+  UNLOCK_MUTEX(buf->mutex);
 }
+
+
+void buffer_insert_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_add_action(&buf->actions, action, INSERT);
+
+  UNLOCK_MUTEX(buf->mutex);  
+}
+
+
+void buffer_append_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_add_action(&buf->actions, action, APPEND);
+
+  UNLOCK_MUTEX(buf->mutex);  
+}
+
+
+/* --- Buffer status functions --- */
+
+void buffer_wait_for_empty (buf_t *buf)
+{
+  int empty = 0;
+
+  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;
+  }
+  UNLOCK_MUTEX(buf->mutex);
+
+  DEBUG("Exit buffer_wait_for_empty");
+}
+
+
+long buffer_full (buf_t *buf)
+{
+  return buf->curfill;
+}
+
+
+buffer_stats_t *buffer_statistics (buf_t *buf)
+{
+  buffer_stats_t *stats;
+  
+  LOCK_MUTEX(buf->mutex);
+
+  stats = malloc_buffer_stats();
+
+  stats->size = buf->size;
+  stats->fill = (double) buf->curfill / (double) buf->size * 100.0;
+  stats->prebuffering = buf->prebuffering;
+  stats->paused = buf->paused;
+  stats->eos = buf->eos;
+
+  UNLOCK_MUTEX(buf->mutex);
+
+  return stats;
+}
+
+    

1.4       +122 -27   vorbis-tools/ogg123/buffer.h

Index: buffer.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/buffer.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- buffer.h	2001/08/04 23:54:37	1.3
+++ buffer.h	2001/12/19 02:52:53	1.4
@@ -1,38 +1,133 @@
-/* Common things between reader and writer threads */
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+ 
+ last mod: $Id: buffer.h,v 1.4 2001/12/19 02:52:53 volsung Exp $
+ 
+********************************************************************/
+
+/* A generic circular buffer interface with the ability to buffer
+   actions (conceptually) between bytes in the buffer.*/
+
+#ifndef __BUFFER_H__
+#define __BUFFER_H__
+
+#include <stdlib.h>
+#include <pthread.h>
+#include <ogg/os_types.h>
 
-#ifndef __BUFFER_H
-#define __BUFFER_H
 
-#include "ogg123.h"
+struct action_t; /* forward declaration */
 
-/* 4096 is the chunk size we request from libvorbis. */
-#define BUFFER_CHUNK_SIZE 4096
+/* buffer_write_func(void *data, int nbytes, int eos, void *arg) */
+typedef int (*buffer_write_func_t) (void *, int, int, void *);
 
-typedef struct chunk_s
+typedef struct buf_t
 {
-  long len; /* Length of the chunk (for if we only got partial data) */
-  char data[BUFFER_CHUNK_SIZE]; 
-} chunk_t;
+  /* generic buffer interface */
+  void *write_arg;
+  buffer_write_func_t write_func;
+
+  /* pthread variables */
+  pthread_t thread;
+
+  pthread_mutex_t mutex;
+  
+  pthread_cond_t playback_cond; /* signalled when playback can continue */
+  pthread_cond_t write_cond;    /* signalled when more data can be written 
+				   to the buffer */
+  
+  /* buffer info (constant) */
+  int  audio_chunk_size;  /* write data to audio device in this chunk size, 
+			     if possible */
+  long prebuffer_size;    /* number of bytes to prebuffer */
+  long size;              /* buffer size, for reference */
+
+  /* ----- Everything after this point is protected by mutex ----- */
+
+  /* buffering state variables */
+  int prebuffering;
+  int paused;
+  int eos;
+  int abort_write;
+
+  /* buffer data */
+  long curfill;     /* how much the buffer is currently filled */
+  long start;       /* offset in buffer of start of available data */
+  ogg_int64_t position; /* How many bytes have we output so far */
+  ogg_int64_t position_end; /* Position right after end of data */
 
-typedef struct buf_s
-{
-  char status;       /* Status. See STAT_* below. */
-  int fds[2];        /* Pipe file descriptors. */
-  chunk_t *reader;   /* Chunk the reader is busy with */
-  chunk_t *writer;   /* Chunk the writer is busy with */
-  chunk_t *end;      /* Last chunk in the buffer (for convenience) */
-  chunk_t buffer[1]; /* The buffer itself. It's more than one chunk. */
+  struct action_t *actions; /* Queue actions to perform */
+  char buffer[1];   /* The buffer itself. It's more than one byte. */
 } buf_t;
 
-buf_t *fork_writer (long size, devices_t *d);
-void submit_chunk (buf_t *buf, chunk_t chunk);
-void buffer_shutdown (buf_t *buf);
-void buffer_flush (buf_t *buf);
-
-#define STAT_FLUSH 1
-#define STAT_SHUTDOWN 2
-
-#endif /* !defined (__BUFFER_H) */
 
+/* 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;
+
+
+typedef struct buffer_stats_t {
+  long size;
+  double fill;
+  int prebuffering;
+  int paused;
+  int eos;
+} buffer_stats_t;
+
+
+/* --- Buffer allocation --- */
+
+buf_t *buffer_create (long size, long prebuffer,
+		      buffer_write_func_t write_func, void *arg,
+		      int audio_chunk_size);
+
+void buffer_reset (buf_t *buf);
+void buffer_destroy (buf_t *buf);
+
+/* --- Buffer thread control --- */
+int  buffer_thread_start   (buf_t *buf);
+void buffer_thread_pause   (buf_t *buf);
+void buffer_thread_unpause (buf_t *buf);
+void buffer_thread_kill    (buf_t *buf);
+
+/* --- Data buffering functions --- */
+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);
+void buffer_abort_write (buf_t *buf);
+
+/* --- Action buffering functions --- */
+void buffer_action_now (buf_t *buf, action_func_t action_func, 
+			void *action_arg);
+void buffer_insert_action_at_end (buf_t *buf, action_func_t action_func, 
+				  void *action_arg);
+void buffer_append_action_at_end (buf_t *buf, action_func_t action_func, 
+				  void *action_arg);
+void buffer_insert_action_at (buf_t *buf, action_func_t action_func, 
+			      void *action_arg, ogg_int64_t position);
+void buffer_append_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);
+buffer_stats_t *buffer_statistics (buf_t *buf);
 
+#endif /* __BUFFER_H__ */

1.14      +13 -9     vorbis-tools/ogg123/ogg123.1

Index: ogg123.1
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.1,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- ogg123.1	2001/08/13 04:40:01	1.13
+++ ogg123.1	2001/12/19 02:52:53	1.14
@@ -14,6 +14,12 @@
 .B -k
 .I seconds 
 ] [
+.B -x
+.I nth
+] [
+.B -y
+.I ntimes
+] [
 .B -b
 .I buffer_size 
 ] [
@@ -72,6 +78,13 @@
 Display version information.
 .IP "-v, --verbose"
 Increase verbosity.
+.IP "-x n, --nth"
+Play every 'n'th decoded block.  Has the effect of playing audio at 'n' times
+faster than normal speed.
+.IP "-y n, --ntimes"
+Repeat every played block 'n' times.  Has the effect of playing audio 'n'
+times slower than normal speed.  May be with -x for interesting fractional
+speeds.
 .IP "-z, --shuffle"
 Play files in pseudo-random order.
 
@@ -243,15 +256,6 @@
 immediately, due to audio data buffering in the audio device.
 This delay is system dependent, but it is usually not more
 than one or two seconds.
-
-.SH NOTES
-
-The use of
-.B /etc/ogg123rc
-and
-.B ~/.ogg123rc
-has been removed in favor of the configuration system provided by
-libao.  The ability to override libao will be added in a future version.
 
 .SH FILES
 

1.50      +474 -577  vorbis-tools/ogg123/ogg123.c

Index: ogg123.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.c,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -r1.49 -r1.50
--- ogg123.c	2001/11/28 05:47:16	1.49
+++ ogg123.c	2001/12/19 02:52:54	1.50
@@ -1,5 +1,5 @@
-/* ogg123.c by Kenneth Arnold <kcarnold at arnoldnet.net> */
-/* Modified to use libao by Stan Seibert <volsung at asu.edu> */
+/* ogg123.c by Kenneth Arnold <ogg123 at arnoldnet.net> */
+/* Maintained by Stan Seibert <volsung at xiph.org> */
 
 /********************************************************************
  *                                                                  *
@@ -8,657 +8,554 @@
  * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
  * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2000             *
- * by Monty <monty at xiph.org> and the XIPHOPHORUS Company            *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
  * http://www.xiph.org/                                             *
  *                                                                  *
  ********************************************************************
 
- last mod: $Id: ogg123.c,v 1.49 2001/11/28 05:47:16 volsung Exp $
+ last mod: $Id: ogg123.c,v 1.50 2001/12/19 02:52:54 volsung Exp $
 
  ********************************************************************/
 
-/* FIXME : That was a messy message. Fix it. */
-
 #include <sys/types.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <sys/socket.h>
 #include <errno.h>
 #include <time.h>
 #include <getopt.h>
+#include <signal.h>
 #include <unistd.h>
 
-#include <signal.h>
+#include "audio.h"
+#include "buffer.h"
+#include "callbacks.h"
+#include "cfgfile_options.h"
+#include "cmdline_options.h"
+#include "format.h"
+#include "transport.h"
+#include "status.h"
+#include "compat.h"
 
 #include "ogg123.h"
 
+void exit_cleanup ();
+void play (char *source_string);
+
 /* take buffer out of the data segment, not the stack */
-char convbuffer[BUFFER_CHUNK_SIZE];
-int convsize = BUFFER_CHUNK_SIZE;
-buf_t * buffer = NULL;
-
-static char skipfile_requested;
-static void (*old_sig)(int);
-
-struct {
-    char *key;			/* includes the '=' for programming convenience */
-    char *formatstr;		/* formatted output */
-} ogg_comment_keys[] = {
-  {"ARTIST=", "Artist: %s\n"},
-  {"ALBUM=", "Album: %s\n"},
-  {"TITLE=", "Title: %s\n"},
-  {"VERSION=", "Version: %s\n"},
-  {"TRACKNUMBER=", "Track number: %s\n"},
-  {"ORGANIZATION=", "Organization: %s\n"},
-  {"GENRE=", "Genre: %s\n"},
-  {"DESCRIPTION=", "Description: %s\n"},
-  {"DATE=", "Date: %s\n"},
-  {"LOCATION=", "Location: %s\n"},
-  {"COPYRIGHT=", "Copyright %s\n"},
-  {NULL, NULL}
+#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;
+
+
+/* ------------------------- config file options -------------------------- */
+
+/* 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);
+
+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}
 };
 
-struct option long_options[] = {
-    {"help", no_argument, 0, 'h'},
-    {"version", no_argument, 0, 'V'},
-    {"device", required_argument, 0, 'd'},
-    {"file", required_argument, 0, 'f'},
-    {"skip", required_argument, 0, 'k'},
-    {"device-option", required_argument, 0, 'o'},
-    {"verbose", no_argument, 0, 'v'},
-    {"quiet", no_argument, 0, 'q'},
-    {"shuffle", no_argument, 0, 'z'},
-    {"buffer", required_argument, 0, 'b'},
-    {"delay", required_argument, 0, 'l'},
-    {0, 0, 0, 0}
-};
 
-void usage(void)
-{
-    FILE *o = stderr;
-    int i, driver_count;
-    ao_info **devices = ao_driver_info_list(&driver_count);
-
-    fprintf(o,
-	    "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:\n"
-	    "        ");
-
-    for(i = 0; i < driver_count; i++)
-      fprintf(o,"%s ",devices[i]->short_name);
-
-    fprintf(o,"\n");
-
-    fprintf(o,
-	    "  -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"
-	    "  -v, --verbose  display progress and other useful stuff\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."
-	    "  -l, --delay=s  set s (default 1). If s=-1, disable song skip.\n");
-}
+/* Flags set by the signal handler to control the threads */
+signal_request_t sig_request = {0, 0, 0, 0};
 
-int main(int argc, char **argv)
+
+/* ------------------------------- signal handler ------------------------- */
+
+
+void signal_handler (int signo)
 {
-    ogg123_options_t opt;
-    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;
-    
-    opt.read_file = NULL;
-    opt.shuffle = 0;
-    opt.verbose = 0;
-    opt.quiet = 0;
-    opt.seekpos = 0;
-    opt.instream = NULL;
-    opt.outdevices = NULL;
-    opt.buffer_size = 0;
-    opt.delay = 1;
-
-    ao_initialize();
-
-    while (-1 != (ret = getopt_long(argc, argv, "b:d:f:hl:k:o:qvVz",
-				    long_options, &option_index))) {
-	switch (ret) {
-	case 0:
-	    fprintf(stderr,
-		    "Internal error: long option given when none expected.\n");
-	    exit(1);
-	case 'b':
-	  opt.buffer_size = atoi(optarg) / (BUFFER_CHUNK_SIZE / 1024);
-	  if (opt.buffer_size == 1)
-	    opt.buffer_size = 0; /* Hack to work around boundary case in
-				    buffering code.  FIXME! */
-	  break;
-	case 'd':
-	    temp_driver_id = ao_driver_id(optarg);
-	    if (temp_driver_id < 0) {
-		fprintf(stderr, "No such device %s.\n", optarg);
-		exit(1);
-	    }
-	    current = append_device(opt.outdevices, temp_driver_id, 
-				    NULL, NULL);
-	    if(opt.outdevices == NULL)
-		    opt.outdevices = 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 {
-	        fprintf(stderr, "Driver %s is not a file output driver.\n",
-			info->short_name);
-	        exit(1);
-	      }
-	    } else {
-	      fprintf(stderr, "Cannot specify output file without specifying a driver.\n");
-	      exit (1);
-	    }
-	    break;
-	case 'k':
-	    opt.seekpos = atof(optarg);
-	    break;
-	case 'l':
-	    opt.delay = atoi(optarg);
-	    break;
-	case 'o':
-	    if (optarg && !add_option(current_options, optarg)) {
-		fprintf(stderr, "Incorrect option format: %s.\n", optarg);
-		exit(1);
-	    }
-	    break;
-	case 'h':
-	    usage();
-	    exit(0);
-	case 'q':
-	    opt.quiet++;
-	    break;
-	case 'v':
-	    opt.verbose++;
-	    break;
-	case 'V':
-	    fprintf(stderr, "Ogg123 from " PACKAGE " " VERSION "\n");
-	    exit(0);
-	case 'z':
-	    opt.shuffle = 1;
-	    break;
-	case '?':
-	    break;
-	default:
-	    usage();
-	    exit(1);
-	}
-    }
+  switch (signo) {
+  case SIGALRM:
+    sig_request.ticks++;
+    if (sig_request.ticks < options.delay) {
+      signal (SIGALRM, signal_handler);
+      alarm(1);
+    }
+    break;
+
+  case SIGINT:
+    if (sig_request.ticks < options.delay)
+      sig_request.exit = 1;
+    else
+      sig_request.skipfile = 1;
+    break;
 
-    /* Use the default device if needed */
-    if (temp_driver_id < 0) {
-        temp_driver_id = ao_default_driver_id();
-	
-	if (temp_driver_id < 0) {
-	  fprintf(stderr,
-		  "Could not load default driver.  Audio devices may be already in use.\nExiting.\n");
-	  exit(1);
-	}
+  case SIGTSTP:
+    sig_request.pause = 1;
+    /* buffer_Pause (Options.outputOpts.buffer);
+       buffer_WaitForPaused (Options.outputOpts.buffer);
+       }
+       if (Options.outputOpts.devicesOpen == 0) {
+       close_audio_devices (Options.outputOpts.devices);
+       Options.outputOpts.devicesOpen = 0;
+       }
+    */
+    /* open_audio_devices();
+       if (Options.outputOpts.buffer) {
+       buffer_Unpause (Options.outputOpts.buffer);
+       }
+    */
+    break;
 
-	opt.outdevices = append_device(opt.outdevices, temp_driver_id, 
-				       temp_options, NULL);
-    }
+  case SIGCONT:
+    break;  /* Don't need to do anything special to resume */
+  }
+}
 
-    if (optind == argc) {
-	usage();
-	exit(1);
-    }
+/* -------------------------- util functions ---------------------------- */
 
-    if (opt.shuffle) {
-	int i;
-	
-	srand(time(NULL));
+void options_init (ogg123_options_t *opts)
+{
+  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;
 
-	for (i = optind; i < argc; i++) {
-		int j = i + rand() % (argc - i);
-		char *temp = argv[i];
-		argv[i] = argv[j];
-		argv[j] = temp;
-	}
-    }
+  opts->status_freq = 10.0;
+}
 
-    while (optind < argc) {
-	opt.read_file = argv[optind];
-	play_file(opt);
-	optind++;
-    }
 
-    while (opt.outdevices != NULL) {
-      if (opt.outdevices->device)
-        ao_close(opt.outdevices->device);
-      current = opt.outdevices->next_device;
-      free(opt.outdevices);
-      opt.outdevices = current;
-    }
+/* This function selects which statistics to display for our
+   particular configuration.  This does not have anything to do with
+   verbosity, but rather with which stats make sense to display. */
+void select_stats (stat_format_t *stats, ogg123_options_t *opts, 
+		   data_source_t *source, decoder_t *decoder, 
+		   buf_t *audio_buffer)
+{
+  data_source_stats_t *data_source_stats;
 
-    if (buffer != NULL)
-	    buffer_shutdown(buffer);
-    
-    ao_shutdown();
+  if (audio_buffer != NULL) {
+    /* Turn on output buffer stats */
+    stats[8].enabled = 1; /* Fill */
+    stats[9].enabled = 1; /* State */
+  } else {
+    stats[8].enabled = 0;
+    stats[9].enabled = 0;
+  }
 
-    return (0);
+  data_source_stats = source->transport->statistics(source);
+  if (data_source_stats->input_buffer_used) {
+    /* Turn on input buffer stats */
+    stats[6].enabled = 1; /* Fill */
+    stats[7].enabled = 1; /* State */
+  } else {
+    stats[6].enabled = 0;
+    stats[7].enabled = 0;
+  }
+    
+  /* Put logic here to decide if this stream needs a total time display */
 }
 
-/* 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 signal_skipfile(int which_signal)
+/* Handles printing statistics depending upon whether or not we have 
+   buffering going on */
+void display_statistics (stat_format_t *stat_format,
+			 buf_t *audio_buffer, 
+			 data_source_t *source,
+			 decoder_t *decoder)
 {
-  skipfile_requested = 1;
+  print_statistics_arg_t *pstats_arg;
+  buffer_stats_t *buffer_stats;
+
+  pstats_arg = new_print_statistics_arg(stat_format,
+					source->transport->statistics(source),
+					decoder->format->statistics(decoder));
+
+  /* Disable/Enable statistics as needed */
+
+  if (pstats_arg->decoder_statistics->total_time <
+      pstats_arg->decoder_statistics->current_time) {
+    stat_format[2].enabled = 0;  /* Remaining playback time */
+    stat_format[3].enabled = 0;  /* Total playback time */
+  }
 
-  /* 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.
-   */
-
-  if (old_sig != NULL) {
-    signal(which_signal,old_sig);
-    raise(which_signal);
+  if (pstats_arg->data_source_statistics->input_buffer_used) {
+    stat_format[6].enabled = 1;  /* Input buffer fill % */
+    stat_format[7].enabled = 1;  /* Input buffer state  */
   }
 
+  if (audio_buffer) {
+    /* Place a status update into the buffer */
+    buffer_append_action_at_end(audio_buffer,
+				&print_statistics_action,
+				pstats_arg);
+    
+    /* And if we are not playing right now, do an immediate
+       update just the output buffer */
+    buffer_stats = buffer_statistics(audio_buffer);
+    if (buffer_stats->paused || buffer_stats->prebuffering) {
+      pstats_arg = new_print_statistics_arg(stat_format,
+					    NULL,
+					    NULL);
+      print_statistics_action(audio_buffer, pstats_arg);
+    }
+    free(buffer_stats);
+    
+  } else
+    print_statistics_action(NULL, pstats_arg);
 }
+
 
-void signal_activate_skipfile(int ignored)
+void display_statistics_quick (stat_format_t *stat_format,
+			       buf_t *audio_buffer, 
+			       data_source_t *source,
+			       decoder_t *decoder)
 {
-  old_sig = signal(SIGINT,signal_skipfile);
+  print_statistics_arg_t *pstats_arg;
+
+  pstats_arg = new_print_statistics_arg(stat_format,
+					source->transport->statistics(source),
+					decoder->format->statistics(decoder));
+
+  if (audio_buffer) {
+    print_statistics_action(audio_buffer, pstats_arg);
+  } else
+    print_statistics_action(NULL, pstats_arg);
 }
 
 
-void play_file(ogg123_options_t opt)
+void print_audio_devices_info(audio_device_t *d)
 {
-    /* Oh my gosh this is disgusting. Big cleanups here will include an
-       almost complete rewrite of the hacked-out HTTP streaming and a shift
-       to using callbacks for the vorbisfile input.
-    */
+  ao_info *info;
 
-    OggVorbis_File vf;
-    int current_section = -1, eof = 0, eos = 0, ret;
-    int old_section = -1;
-    long t_min = 0, c_min = 0, r_min = 0;
-    double t_sec = 0, c_sec = 0, r_sec = 0;
-    int is_big_endian = ao_is_big_endian();
-    double realseekpos = opt.seekpos;
-
-    /* Junk left over from the failed info struct */
-    double u_time, u_pos;
-
-    if (strcmp(opt.read_file, "-")) {	/* input file not stdin */
-	if (!strncmp(opt.read_file, "http://", 7)) {
-	    /* Stream down over http */
-	    char *temp = NULL, *server = NULL, *port = NULL, *path = NULL;
-	    int index;
-	    long iport;
-
-	    temp = opt.read_file + 7;
-	    for (index = 0; temp[index] != '/' && temp[index] != ':';
-		 index++);
-	    server = (char *) malloc(index + 1);
-	    strncpy(server, temp, index);
-	    server[index] = '\0';
-
-	    /* Was a port specified? */
-	    if (temp[index] == ':') {
-		/* Grab the port. */
-		temp += index + 1;
-		for (index = 0; temp[index] != '/'; index++);
-		port = (char *) malloc(index + 1);
-		strncpy(port, temp, index);
-		port[index] = '\0';
-		if ((iport = atoi(port)) <= 0 || iport > 65535) {
-		    fprintf(stderr, "%s is not a valid port.\n", port);
-		    exit(1);
-		}
-	    } else
-		iport = 80;
-
-	    path = strdup(temp + index);
-
-	    if ((opt.instream = http_open(server, iport, path)) == NULL) {
-		fprintf(stderr, "Error while connecting to server!\n");
-		exit(1);
-	    }
-	    /* Send HTTP header */
-	    fprintf(opt.instream,
-		    "GET %s HTTP/1.0\r\n"
-		    "Accept: */*\r\n"
-		    "User-Agent: ogg123\r\n"
-		    "Host: %s\r\n\r\n\r\n", path, server);
-
-		fflush(opt.instream); /* Make sure these are all actually sent */
-
-	    /* Dump headers */
-	    {
-		char last = 0, in = 0;
-		int eol = 0;
-
-		if (opt.verbose > 0)
-		  fprintf(stderr, "HTTP Headers:\n");
-		for (;;) {
-		    last = in;
-		    in = getc(opt.instream);
-		    if (opt.verbose > 0)
-		      putc(in, stderr);
-		    if (last == 13 && in == 10) {
-			if (eol)
-			    break;
-			eol = 1;
-		    } else if (in != 10 && in != 13)
-			eol = 0;
-		}
-	    }
-	    free(server);
-	    free(path);
-	} else {
-	    if (opt.quiet < 1)
-		fprintf(stderr, "Playing from file %s.\n", opt.read_file);
-	    /* Open the file. */
-	    if ((opt.instream = fopen(opt.read_file, "rb")) == NULL) {
-		fprintf(stderr, "Error opening input file.\n");
-		exit(1);
-	    }
-	}
-    } else {
-	if (opt.quiet < 1)
-	    fprintf(stderr, "Playing from standard input.\n");
-	opt.instream = stdin;
-    }
+  while (d != NULL) {
+    info = ao_driver_info(d->driver_id);
+    
+    status_message(2, "\nDevice:   %s", info->name);
+    status_message(2, "Author:   %s", info->author);
+    status_message(2, "Comments: %s\n", info->comment);
 
-    if ((ov_open(opt.instream, &vf, NULL, 0)) < 0) {
-	fprintf(stderr, "E: input not an Ogg Vorbis audio stream.\n");
-	return;
-    }
+    d = d->next_device;
+  }
 
-    /* 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. */
-
-    if (opt.delay > 0) {
-        skipfile_requested = 0;
-	signal(SIGALRM,signal_activate_skipfile);
-	alarm(opt.delay);
-    }
+}
 
-    while (!eof) {
-	int i;
-	vorbis_comment *vc = ov_comment(&vf, -1);
-	vorbis_info *vi = ov_info(&vf, -1);
-
-	if(open_audio_devices(&opt, vi->rate, vi->channels, &buffer) < 0)
-		exit(1);
-
-	if (opt.quiet < 1) {
-	    if (eos && opt.verbose) fprintf (stderr, "\r                                                      \r\n");
-	    for (i = 0; i < vc->comments; i++) {
-		char *cc = vc->user_comments[i];	/* current comment */
-		int i;
-
-		for (i = 0; ogg_comment_keys[i].key != NULL; i++)
-		    if (!strncasecmp
-			(ogg_comment_keys[i].key, cc,
-			 strlen(ogg_comment_keys[i].key))) {
-			fprintf(stderr, ogg_comment_keys[i].formatstr,
-				cc + strlen(ogg_comment_keys[i].key));
-			break;
-		    }
-		if (ogg_comment_keys[i].key == NULL)
-		    fprintf(stderr, "Unrecognized comment: '%s'\n", cc);
-	    }
-
-	    fprintf(stderr, "\nBitstream is %d channel, %ldHz\n",
-		    vi->channels, vi->rate);
-	    if (opt.verbose > 1)
-	      fprintf(stderr, "Encoded by: %s\n\n", vc->vendor);
-	}
 
-	if (opt.verbose > 0 && ov_seekable(&vf)) {
-	    /* Seconds with double precision */
-	    u_time = ov_time_total(&vf, -1);
-	    t_min = (long) u_time / (long) 60;
-	    t_sec = u_time - 60 * t_min;
-	}
+/* --------------------------- main code -------------------------------- */
 
-	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;
-
-	if (realseekpos > 0)
-	    ov_time_seek(&vf, realseekpos);
-
-	eos = 0;
-
-	while (!eos) {
-
-	    if (skipfile_requested) {
-	      eof = eos = 1;
-	      skipfile_requested = 0;
-	      signal(SIGALRM,signal_activate_skipfile);
-	      alarm(opt.delay);
-	      if (buffer) {
-		buffer_flush (buffer);
-	      }
-	      break;
-  	    }
-
-	    old_section = current_section;
-	    ret =
-		ov_read(&vf, 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 (opt.verbose > 1) 
-		/* we should be able to resync silently; if not there are 
-		   bigger problems. */
-		fprintf (stderr, "Warning: hole in the stream; probably harmless\n");
-	    } else if (ret < 0) {
-	      /* Stream error */
-	      fprintf(stderr, "Error: libvorbis reported a stream error.\n");
-	    } else {
-		/* did we enter a new logical bitstream */
-		if (old_section != current_section && old_section != -1)
-		    eos = 1;
-
-		if (buffer)
-		  {
-		    chunk_t chunk;
-		    chunk.len = ret;
-		    memcpy (chunk.data, convbuffer, ret);
-		    
-		    submit_chunk (buffer, chunk);
-		  }
-		else
-		  devices_write(convbuffer, ret, opt.outdevices);
-		
-		if (opt.verbose > 0) {
-		    if (ov_seekable (&vf)) {
-		      u_pos = ov_time_tell(&vf);
-		      c_min = (long) u_pos / (long) 60;
-		      c_sec = u_pos - 60 * c_min;
-		      r_min = (long) (u_time - u_pos) / (long) 60;
-		      r_sec = (u_time - u_pos) - 60 * r_min;
-		      fprintf(stderr,
-			      "\rTime: %02li:%05.2f [%02li:%05.2f] of %02li:%05.2f, Bitrate: %.1f   \r",
-			      c_min, c_sec, r_min, r_sec, t_min, t_sec,
-			      (float) ov_bitrate_instant(&vf) / 1000.0F);
-		    } else {
-		      /* working around a bug in vorbisfile */
-		      u_pos = (double) ov_pcm_tell(&vf) / (double) vi->rate;
-		      c_min = (long) u_pos / (long) 60;
-		      c_sec = u_pos - 60 * c_min;
-		      fprintf(stderr,
-			      "\rTime: %02li:%05.2f, Bitrate: %.1f   \r",
-			      c_min, c_sec,
-			      (float) ov_bitrate_instant (&vf) / 1000.0F);
-		    }
-		}
-	    }
-	}
-    }
 
-    alarm(0);
-    signal(SIGALRM,SIG_DFL);
-    signal(SIGINT,old_sig);
 
-    ov_clear(&vf);
+int main(int argc, char **argv)
+{
+  int optind;
 
-    if (opt.quiet < 1)
-	fprintf(stderr, "\nDone.\n");
-}
+  ao_initialize();
+  stat_format = stat_format_create();
+  options_init(&options);
+  file_options_init(file_opts);
 
-int get_tcp_socket(void)
-{
-    return socket(AF_INET, SOCK_STREAM, 0);
-}
+  parse_std_configs(file_opts);
+  optind = parse_cmdline_options(argc, argv, &options, file_opts);
 
-FILE *http_open(char *server, int port, char *path)
-{
-    int sockfd = get_tcp_socket();
-    struct hostent *host;
-    struct sockaddr_in sock_name;
-
-    if (sockfd == -1)
-	return NULL;
-
-    if (!(host = gethostbyname(server))) {
-	fprintf(stderr, "Unknown host: %s\n", server);
-	return NULL;
-    }
+  audio_play_arg.devices = options.devices;
+  audio_play_arg.stat_format = stat_format;
+
+  /* Don't use status_message until after this point! */
+  status_set_verbosity(options.verbosity);
+
+  print_audio_devices_info(options.devices);
 
-    memcpy(&sock_name.sin_addr, host->h_addr, host->h_length);
-    sock_name.sin_family = AF_INET;
-    sock_name.sin_port = htons(port);
-
-    if (connect(sockfd, (struct sockaddr *) &sock_name, sizeof(sock_name))) {
-	if (errno == ECONNREFUSED)
-	    fprintf(stderr, "Connection refused\n");
-	return NULL;
+  /* 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) {
+    cmdline_usage();
+    exit(1);
+  }
+  
+  /* Setup buffer */ 
+  if (options.buffer_size > 0) {
+    audio_buffer = buffer_create(options.buffer_size,
+				 options.buffer_size * options.prebuffer / 100,
+				 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;
+
+
+  /* Shuffle playlist */
+  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;
     }
-    return fdopen(sockfd, "r+b");
+  }
+  
+
+  /* Play the files/streams */
+
+  while (optind < argc) {
+    play(argv[optind]);
+    optind++;
+  }
+
+
+  exit (0);
 }
 
-int open_audio_devices(ogg123_options_t *opt, int rate, int channels, buf_t **buffer)
+
+void play (char *source_string)
 {
-  static int prevrate=0, prevchan=0;
-  devices_t *current;
-  ao_sample_format format;
+  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 };
+  audio_format_t new_audio_fmt;
+  audio_reopen_arg_t *reopen_arg;
+
+  /* Flags and counters galore */
+  int eof = 0, eos = 0, ret;
+  int nthc = 0, ntimesc = 0;
+  int next_status = 0;
+  int status_interval = 0;
+
+
+  /* Set preferred audio format (used by decoder) */
+  new_audio_fmt.big_endian = ao_is_big_endian();
+  new_audio_fmt.signed_sample = 1;
+  new_audio_fmt.word_size = 2;
+
+  /* Select appropriate callbacks */
+  if (audio_buffer != NULL) {
+    decoder_callbacks.printf_error = &decoder_buffered_error_callback;
+    decoder_callbacks.printf_metadata = &decoder_buffered_error_callback;
+    decoder_callbacks_arg = audio_buffer;
+  } else {
+    decoder_callbacks.printf_error = &decoder_error_callback;
+    decoder_callbacks.printf_metadata = &decoder_error_callback;
+    decoder_callbacks_arg = NULL;
+  }
 
-  if(prevrate == rate && prevchan == channels)
-    return 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);
+    return;
+  }
   
-  if(prevrate !=0 && prevchan!=0)
-	{
-	  if (buffer != NULL && *buffer != NULL) {
-	    buffer_shutdown (*buffer);
-	    *buffer = NULL;
-	  }
-
-	  current = opt->outdevices;
-	  while (current != NULL) {
-	    ao_close(current->device);
-	    current = current->next_device;
-	  }
-	}
+  if ( (source = transport->open(source_string, &options)) == 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;
+  }
   
-  format.rate = prevrate = rate;
-  format.channels = prevchan = channels;
-  format.bits = 16;
-  format.byte_format = AO_FMT_NATIVE;
-
-  current = opt->outdevices;
-  while (current != NULL) {
-    ao_info *info = ao_driver_info(current->driver_id);
-    
-    if (opt->verbose > 0) {
-      fprintf(stderr, "Device:   %s\n", info->name);
-      fprintf(stderr, "Author:   %s\n", info->author);
-      fprintf(stderr, "Comments: %s\n", info->comment);
-      fprintf(stderr, "\n");	
-    }
+  if ( (decoder = format->init(source, &options, &new_audio_fmt, 
+			       &decoder_callbacks, audio_buffer)) == NULL ) {
+    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(stat_format, &options, source, decoder, audio_buffer);
+
+
+  /* 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) {
     
-    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,
-				     1, &format, current->options);
+    /* First reset mutexes and other synchronization variables */
+    buffer_reset (audio_buffer);
+    buffer_thread_start (audio_buffer);
+  }
 
-    if (current->device == NULL) {
-      switch (errno) {
-      case AO_ENODRIVER:
-	fprintf(stderr, "Error: No device not available.\n");
-	break;
-      case AO_ENOTLIVE:
-	fprintf(stderr, "Error: %s requires an output filename to be specified with -f.\n", info->short_name);
-	break;
-      case AO_EBADOPTION:
-	fprintf(stderr, "Error: Unsupported option value to %s device.\n",
-		info->short_name);
-	break;
-      case AO_EOPENDEVICE:
-	fprintf(stderr, "Error: Cannot open device %s.\n",
-		info->short_name);
-	break;
-      case AO_EFAIL:
-	fprintf(stderr, "Error: Device failure.\n");
-	break;
-      case AO_ENOTFILE:
-	fprintf(stderr, "Error: An output file cannot be given for %s device.\n", info->short_name);
-	break;
-      case AO_EOPENFILE:
-	fprintf(stderr, "Error: Cannot open file %s for writing.\n",
-		current->filename);
+  /* 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);
+  }
+
+  /* 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;
-      case AO_EFILEEXISTS:
-	fprintf(stderr, "Error: File %s already exists.\n", current->filename);
+      }
+
+      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;
-      default:
-	fprintf(stderr, "Error: This error should never happen.  Panic!\n");
+      } 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(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 {
         
-      return -1;
+	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;
+            
+    } /* End of data loop */
+    
+  } /* End of logical bitstream loop */
+  
+  /* 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);
     }
-    current = current->next_device;
+
+    buffer_thread_kill(audio_buffer);
   }
+
+  /* Print final stats */
+  display_statistics_quick(stat_format, audio_buffer, source, decoder); 
+   
   
-  if (opt->buffer_size)
-    *buffer = fork_writer (opt->buffer_size, opt->outdevices);
+  alarm(0);  
+  format->cleanup(decoder);
+  transport->close(source);
+  status_reset_output_lock();  /* In case we were killed mid-output */
+
+  status_message(1, "Done.");
   
-    return 0;
+  if (sig_request.exit)
+    exit (0);
+}
+
+
+void exit_cleanup ()
+{
+      
+  if (audio_buffer != NULL) {
+    buffer_destroy (audio_buffer);
+    audio_buffer = NULL;
+  }
+
+  ao_onexit (options.devices);
 }

1.10      +46 -48    vorbis-tools/ogg123/ogg123.h

Index: ogg123.h
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123.h,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- ogg123.h	2001/08/12 14:04:21	1.9
+++ ogg123.h	2001/12/19 02:52:54	1.10
@@ -1,51 +1,49 @@
-/* This file is part of ogg123, an Ogg Vorbis player. See ogg123.c
- * for copyright information. */
-#ifndef __OGG123_H
-#define __OGG123_H
-
-/* Common includes */
-#include <ogg/ogg.h>
-#include <vorbis/codec.h>
-#include <vorbis/vorbisfile.h>
-#include <ao/ao.h>
-
-#ifdef __sun
-#include <alloca.h>
-#endif
-
-/* For facilitating output to multiple devices */
-typedef struct devices_s {
-  int driver_id;
-  ao_device *device;
-  ao_option *options;
-  char *filename;
-  struct devices_s *next_device;
-} devices_t;
-
-typedef struct ogg123_options_s {
-  char *read_file;            /* File to decode */
-  char shuffle;               /* Should we shuffle playing? */
-  signed short int verbose;   /* Verbose output if > 0, quiet if < 0 */
-  signed short int quiet;     /* Be quiet (no title) */
-  double seekpos;             /* Amount to seek by */
-  FILE *instream;             /* Stream to read from. */
-  devices_t *outdevices;      /* Streams to write to. */
-  int buffer_size;            /* Size of the buffer in chunks. */
-  int rate, channels;         /* playback params for opening audio devices */
-  int delay;                  /* delay for skip to next song */
-} ogg123_options_t;           /* Changed in 0.6 to be non-static */
+/********************************************************************
+ *                                                                  *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
+ * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
+ * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
+ *                                                                  *
+ * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2001                *
+ * by Kenneth C. Arnold <ogg at arnoldnet.net> AND OTHER CONTRIBUTORS  *
+ * http://www.xiph.org/                                             *
+ *                                                                  *
+ ********************************************************************
+
+ last mod: $Id: ogg123.h,v 1.10 2001/12/19 02:52:54 volsung Exp $
+
+ ********************************************************************/
+
+#ifndef __OGG123_H__
+#define __OGG123_H__
+
+#include <ogg/os_types.h>
+#include "audio.h"
 
-/* This goes here because it relies on some of the above. */
-#include "buffer.h"
+typedef struct ogg123_options_t {
+  long int verbosity;         /* Verbose output if > 1, quiet if 0 */
 
-devices_t *append_device(devices_t * devices_list, int driver_id,
-                         ao_option * options, char *filename);
-void devices_write(void *ptr, size_t size, devices_t * d);
-void usage(void);
-int add_option(ao_option ** op_h, const char *optstring);
-void play_file(ogg123_options_t opt);
-int get_tcp_socket(void); /* Will be going soon. */
-FILE *http_open(char *server, int port, char *path); /* ditto */
-int open_audio_devices(ogg123_options_t *opt, int rate, int channels, buf_t ** buffer);
+  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 use */
+
+  double status_freq;         /* Number of status updates per second */
+} ogg123_options_t;
+
+typedef struct signal_request_t {
+  int skipfile;
+  int exit;
+  int pause;
+  ogg_int64_t ticks;
+} signal_request_t;
 
-#endif /* !defined(__OGG123_H) */
+#endif /* __OGG123_H__ */

1.4       +15 -2     vorbis-tools/ogg123/ogg123rc-example

Index: ogg123rc-example
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogg123/ogg123rc-example,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- ogg123rc-example	2001/06/18 02:29:16	1.3
+++ ogg123rc-example	2001/12/19 02:52:54	1.4
@@ -1,3 +1,16 @@
-# Copy this to ~/.ogg123rc and edit as necessary. The current
-# parser appears to handle comments.
+# Copy this to ~/.ogg123rc and edit as necessary. These are all the
+# default options except for default device, which is a reasonable
+# default for many people. To see a full list of available options,
+# type:
+#
+# $ ogg123 -c
+
 default_device=oss
+shuffle=n
+verbose=1
+outbuffer=0
+outprebuffer=0
+inbuffer=10000
+inprebuffer=10
+#save_stream=
+delay=1

1.2       +127 -0    vorbis-tools/ogg123/audio.c

1.2       +56 -0     vorbis-tools/ogg123/audio.h

1.2       +360 -0    vorbis-tools/ogg123/callbacks.c

1.2       +72 -0     vorbis-tools/ogg123/callbacks.h

1.2       +454 -0    vorbis-tools/ogg123/cfgfile_options.c

1.2       +59 -0     vorbis-tools/ogg123/cfgfile_options.h

1.2       +276 -0    vorbis-tools/ogg123/cmdline_options.c

1.2       +31 -0     vorbis-tools/ogg123/cmdline_options.h

1.2       +37 -0     vorbis-tools/ogg123/compat.h

1.2       +161 -0    vorbis-tools/ogg123/file_transport.c

1.2       +65 -0     vorbis-tools/ogg123/format.c

1.2       +78 -0     vorbis-tools/ogg123/format.h

1.2       +298 -0    vorbis-tools/ogg123/http_transport.c

1.2       +380 -0    vorbis-tools/ogg123/oggvorbis_format.c

1.2       +460 -0    vorbis-tools/ogg123/status.c

1.2       +74 -0     vorbis-tools/ogg123/status.h

1.2       +65 -0     vorbis-tools/ogg123/transport.c

1.2       +62 -0     vorbis-tools/ogg123/transport.h

1.12      +1 -1      vorbis-tools/oggenc/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/oggenc/Makefile.am,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- Makefile.am	2001/09/22 22:49:50	1.11
+++ Makefile.am	2001/12/19 02:52:58	1.12
@@ -6,7 +6,7 @@
 
 bin_PROGRAMS = oggenc
 
-INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ -I$(top_srcdir)/include
+INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ @SHARE_CFLAGS@
 
 oggenc_LDADD = @VORBISENC_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ @LIBICONV@ \
                @SHARE_LIBS@

1.6       +1 -1      vorbis-tools/ogginfo/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/ogginfo/Makefile.am,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- Makefile.am	2001/09/22 22:49:50	1.5
+++ Makefile.am	2001/12/19 02:52:58	1.6
@@ -6,7 +6,7 @@
 mandir = @MANDIR@
 man_MANS = ogginfo.1
 
-INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@
+INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ @SHARE_CFLAGS@
 
 ogginfo_LDADD = @VORBIS_LIBS@ @OGG_LIBS@ @LIBICONV@ @SHARE_LIBS@
 ogginfo_DEPENDENCIES = @SHARE_LIBS@

1.4       +2 -2      vorbis-tools/share/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/share/Makefile.am,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- Makefile.am	2001/10/02 03:03:42	1.3
+++ Makefile.am	2001/12/19 02:52:58	1.4
@@ -6,11 +6,11 @@
 
 noinst_LIBRARIES = libutf8.a libgetopt.a
 
-libutf8_a_SOURCES = charset.c iconvert.c utf8.c
+libutf8_a_SOURCES = charset.c charset.h iconvert.c utf8.c
 
 libgetopt_a_SOURCES = getopt.c getopt1.c
 
-EXTRA_DIST = charmaps.h makemap.c charset_test.c
+EXTRA_DIST = charmaps.h makemap.c charset_test.c charsetmap.h
 
 debug:
         $(MAKE) all CFLAGS="@DEBUG@"

1.4       +1 -1      vorbis-tools/vcut/vcut.c

Index: vcut.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/vcut/vcut.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- vcut.c	2001/07/16 13:09:57	1.3
+++ vcut.c	2001/12/19 02:52:59	1.4
@@ -7,7 +7,7 @@
  * Simple application to cut an ogg at a specified frame, and produce two
  * output files.
  *
- * last modified: $Id: vcut.c,v 1.3 2001/07/16 13:09:57 msmith Exp $
+ * last modified: $Id: vcut.c,v 1.4 2001/12/19 02:52:59 volsung Exp $
  */
 
 #include <stdio.h>

1.10      +1 -1      vorbis-tools/vorbiscomment/Makefile.am

Index: Makefile.am
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/vorbiscomment/Makefile.am,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- Makefile.am	2001/09/22 22:49:52	1.9
+++ Makefile.am	2001/12/19 02:52:59	1.10
@@ -4,7 +4,7 @@
 
 bin_PROGRAMS = vorbiscomment
 
-INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ -I$(top_srcdir)/include
+INCLUDES = @OGG_CFLAGS@ @VORBIS_CFLAGS@ @SHARE_CFLAGS@
 
 vorbiscomment_LDADD = @VORBIS_LIBS@ @OGG_LIBS@ @LIBICONV@ @SHARE_LIBS@
 vorbiscomment_DEPENDENCIES = @SHARE_LIBS@

1.16      +1 -1      vorbis-tools/vorbiscomment/vcedit.c

Index: vcedit.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-tools/vorbiscomment/vcedit.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- vcedit.c	2001/11/07 12:45:03	1.15
+++ vcedit.c	2001/12/19 02:53:00	1.16
@@ -6,7 +6,7 @@
  *
  * Comment editing backend, suitable for use by nice frontend interfaces.
  *
- * last modified: $Id: vcedit.c,v 1.15 2001/11/07 12:45:03 msmith Exp $
+ * last modified: $Id: vcedit.c,v 1.16 2001/12/19 02:53:00 volsung Exp $
  */
 
 #include <stdio.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