[nas] another patch for auplay

Paul Fox pgf at foxharp.boston.ma.us
Sat Jan 27 10:35:53 MST 2001


i wrote, in private mail to jon:
 > > i have another patch that i can send as well -- the utility is
 > > a little less clear, but it's something i needed at some point.
 > > it lets auplay accept a list of files to play on stdin.  you
 > > can use it to create a long-lived "file playing server", and you
 > > just toss it names.  shall i send it for your inspection, and you
 > > can decide if it's generally useful enough to include?
 > > 
 > 
 > 	Sure!  Send it along... 


okay jon, here we go.  i included a manpage patch, but that's kind of
terse, so i'll fill in the rationale here.  and i'm replying via the
list so that others get to see what's up, and maybe suggest better
ways of doing what i'm doing.

these patches were driven by my home voice-response menuing system. 
the menu system generates it's ouput by invoking a speech synth
system.  (i currently use festival, but used to, believe it or not,
fetch the converted wav files from the bell labs TTS demo site.  :-)
since the voice synthesis is a slow bulky process no matter what you
use, i cache the results, so that recurring phrases, or phrase parts,
don't need to be generated all the time.  for instance, the simple
phrase "The time is | twelve | thirty | five | PM" is cached and
spoken at the indicated boundaries.  this leads to lots and lots of
files being referenced, in arbitrary combinations. 

to keep the output flowing crisply, i had to come up with a way of
using as few auplay-to-server connections as possible -- i'm running
on relatively slow hardware, and using the menus is a real-time
experience.

that led to a design where i create a named pipe, and start auplay (with
my patches) hooked to that pipe as stdin.  it just sits in the
background and does that forever.  writing filenames into that pipe
from the menu system causes them to be played.  so that's the whole
point of the "-l" option i added.  when given "-l", auplay will first
consume any filenames given as arguments, and will then start reading
them from stdin.

the other option, "-s", didn't go in quite as cleanly, i'm afraid, but
i found it was equally important in my application.  in order for new
user input (this is driven by an IR remote) to interrupt the current
spoken phrase (i.e., in order for typahead to short-circuit a lengthy
menu), there had to be a way to cut off the speech quickly.  simply
killing the auplay -l "daemon" wasn't enough -- the server would have
a bunch of speech buffered up, and would continue talking for many
seconds in some cases, depending on the length of the file it was
speaking at the time.  the AuStopFlow() call is designed to cut off
the stream, but it wasn't available to callers of
AuSoundPlaySynchronousFromFile(), which is what auplay calls.  so,
since i didn't want to change the library, i ended up including a
local copy of AuSoundPlaySynchronousFromFile(), and auplay invokes
that.  this gives it access to the flowid handle that's needed to
invoke AuStopFlow() when a signal is received.  having that facility
available in the existing library api would be preferable, of course.

so you can see, these are both kind of specialized options.

let me know what you think...  if there's another/better way of accomplishing
either, i'm all ears!

paul
=---------------------
  paul fox, pgf at foxharp.boston.ma.us (arlington, ma, where it's 33.3 degrees)

--- auplay.c.orig	Sat Jan 27 12:08:00 2001
+++ auplay.c	Sat Jan 27 12:08:24 2001
@@ -26,21 +26,92 @@
  * auplay -- a trivial program for playing audio files.
  */
 
+#include	<unistd.h>
+#include	<fcntl.h>
 #include	<stdio.h>
+#include	<signal.h>
 #include	<audio/audiolib.h>
 #include	<audio/soundlib.h>
 
-static AuServer *aud;
+static AuServer *aud = 0;
 static int      volume = 100,
                 infoflag = 0,
                 playflag = 1;
 static char    *progname;
+static AuFlowID auflowid = 0;
+
+void
+sighandler(i)
+int i;
+{
+    char buf[BUFSIZ];
+
+    if (aud && auflowid)
+	AuStopFlow(aud, auflowid, 0);
+
+    /* consume anything remaining on stdin to prevent errors */
+    fcntl(0, F_SETFL, O_NONBLOCK);
+    while (read(0, buf, BUFSIZ) > 0);
+
+    exit(1);
+}
+
+/*
+ * Unfortunately, in order to get access to the AuFlowID of the flow
+ * being played, so that we can call AuStopFlow() on it when we're
+ * told to terminate (above), we need to have our own copy of
+ * AuSoundPlaySynchronousFromFile(), and a couple of support
+ * definitions.
+ */
+
+#define	VOL(volume)		((1 << 16) * (volume) / 100)
+
+static void
+sync_play_cb(aud, handler, ev, data)
+AuServer       *aud;
+AuEventHandlerRec *handler;
+AuEvent        *ev;
+AuPointer       data;
+{
+    int            *d = (int *) data;
+
+    *d = 1;
+}
+
+static AuBool
+localAuSoundPlaySynchronousFromFile(aud, fname, volume)
+AuServer       *aud;
+_AuConst char  *fname;
+int             volume;
+{
+    AuStatus        ret;
+    AuEvent         ev;
+    int             d = 0;
+
+    if (!AuSoundPlayFromFile(aud, fname, AuNone, VOL(volume),
+			     sync_play_cb, (AuPointer) &d, &auflowid,
+			     (int *) NULL, (int *) NULL, &ret))
+	return AuFalse;		/* XXX do something with ret? */
+
+    while (1)
+    {
+	AuNextEvent(aud, AuTrue, &ev);
+	AuDispatchEvent(aud, &ev);
+
+	if (d)
+	    break;
+    }
+
+    return AuTrue;
+}
+
+
 
 static void
 usage()
 {
     fprintf(stderr,
-       "Usage:  %s [-iI] [-audio servername] [-volume percent] files ...\n",
+       "Usage:  %s [-iIls] [-audio servername] [-volume percent] files ...\n",
 	    progname);
     exit(1);
 }
@@ -75,7 +146,7 @@
 	}
     }
 
-    if (playflag && !AuSoundPlaySynchronousFromFile(aud, fname, volume))
+    if (playflag && !localAuSoundPlaySynchronousFromFile(aud, fname, volume))
 	fprintf(stderr, "Couldn't play file \"%s\"\n", fname);
 }
 
@@ -84,7 +155,9 @@
 char          **argv;
 {
     int             i,
-                    numfnames;
+                    numfnames,
+		    filelist = 0,
+		    stopflow_on_signal = 0;
     char           *auservername = NULL;
     AuBool          did_file = AuFalse;
 
@@ -127,6 +200,14 @@
 	    infoflag = 1;
 	    playflag = 0;
 	}
+	else if (!strncmp(argv[0], "-l", 2))
+	{
+	    filelist = 1;
+	}
+	else if (!strncmp(argv[0], "-s", 2))
+	{
+	    stopflow_on_signal = 1;
+	}
 	else
 	    usage();
 	argv++;
@@ -135,6 +216,12 @@
 
     if (playflag)
     {
+	if (stopflow_on_signal)
+	{
+	    signal(SIGINT, sighandler);
+	    signal(SIGTERM, sighandler);
+	    signal(SIGHUP, sighandler);
+	}
 	aud = AuOpenServer(auservername, 0, NULL, 0, NULL, NULL);
 	if (!aud)
 	{
@@ -149,10 +236,26 @@
 	did_file = AuTrue;
     }
 
-    if (!did_file)		/* must want stdin */
-	do_file("-");
+    if (filelist) {
+	while (1)
+	{
+	    char filename[256];
+	    if (fgets(filename, 256, stdin) == 0) 
+		break;
+	    filename[strlen(filename)-1] = '\0';
+	    if (!strcmp(filename, "-"))
+	    {
+		fprintf(stderr, "Skipping filename \"-\" in file list");
+		continue;
+	    }
+	    do_file(filename);
+	}
+    } else {
+	if (!did_file)		/* must want stdin */
+	    do_file("-");
+    }
 
-    if (playflag)
+    if (aud)
 	AuCloseServer(aud);
 
     exit(0);
--- auplay.man.orig	Sat Jan 27 12:08:08 2001
+++ auplay.man	Sat Jan 27 12:08:24 2001
@@ -4,11 +4,13 @@
 auplay \- play a sound file to a Network Audio System server
 .SH SYNOPSIS
 .B auplay
-[\-audio \fIservername\fP] [\-volume \fI0\-100\fP] [\-i] [\-I] \fIfiles...\fP
+[\-audio \fIservername\fP] [\-volume \fI0\-100\fP] [\-i] [\-I] [\-l] [\-s] \fIfiles...\fP
 .SH DESCRIPTION
 The \fIauplay\fP program can be used to play audio data stored in 
 the .SND, .AU, or .WAV formats common on Sun workstations and PCs.
 It is typically used from shell scripts or command line procedures.
+If no filenames are given on the command line, audio data will be
+read fron stdin, unless the \fI-l\fP option is given.
 .SH OPTIONS
 The following options may be used with the \fIauplay\fP program:
 .TP 8
@@ -26,6 +28,16 @@
 .B "\-I"
 This option indicates that the header information associated with the 
 sound data should be printed instead of playing the data.
+.TP 8
+.B "\-l"
+This option indicates that a list of filenames is to be read from
+stdin.  \fIauplay\fP will play these files after playing any files
+given explicitly as command line arguments.
+.TP 8
+.B "\-s"
+This option indicates that \fIauplay\fP should attempt to terminate
+the audio flow at the server immediately upon receipt of a signal. 
+Normally data already buffered will continue to play to completion.
 .SH "SEE ALSO"
 nas(1), auctl(1), audemo(1), autool(1)
 .SH COPYRIGHT



More information about the Nas mailing list