1 /*
2  * Copyright 1993 Network Computing Devices, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name Network Computing Devices, Inc. not be
9  * used in advertising or publicity pertaining to distribution of this
10  * software without specific, written prior permission.
11  *
12  * THIS SOFTWARE IS PROVIDED 'AS-IS'.  NETWORK COMPUTING DEVICES, INC.,
13  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
14  * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
15  * PARTICULAR PURPOSE, OR NONINFRINGEMENT.  IN NO EVENT SHALL NETWORK
16  * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
17  * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
18  * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
19  * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  *
22  * $Id: audiotool.c 292 2013-09-07 16:25:51Z auerswald $
23  * $NCDId: @(#)audiotool.c,v 1.27 1994/11/01 23:16:14 greg Exp $
24  */
25 
26 /*
27  * auplay -- a simple clone of Sun's audiotool
28  */
29 
30 #include "config.h"
31 
32 #include	<inttypes.h>
33 #include <stdio.h>
34 
35 #if defined(HAVE_STDLIB_H)
36 # include <stdlib.h>
37 #endif
38 
39 #if defined(HAVE_MALLOC_H)
40 # include <stdlib.h>
41 #endif
42 
43 #include <audio/audiolib.h>
44 #include <audio/soundlib.h>
45 
46 #include <X11/Intrinsic.h>
47 #include <X11/StringDefs.h>
48 #include <X11/Shell.h>
49 #include <X11/Xaw/Cardinals.h>
50 #include <audio/Xtutil.h>
51 
52 /* widgets */
53 #include <X11/Xaw/Command.h>
54 #include <X11/Xaw/Form.h>
55 #include <X11/Xaw/List.h>
56 #include <X11/Xaw/Viewport.h>
57 #include <X11/Xaw/AsciiText.h>
58 #include <X11/Xaw/Scrollbar.h>
59 #include <X11/Xaw/Label.h>
60 #include <X11/Xaw/SimpleMenu.h>
61 #include <X11/Xaw/MenuButton.h>
62 #include <X11/Xaw/SmeBSB.h>
63 #include <X11/Xaw/Dialog.h>
64 #include <X11/Xaw/Toggle.h>
65 
66 #if XtSpecificationRelease < 5
67 #define XawChainTop XtChainTop
68 #define XawChainLeft XtChainLeft
69 #endif
70 
71 #define APP_INSTANCE		"autool"
72 #define	APP_CLASS		"Autool"
73 #define SPACING_X		5
74 #define SPACING_Y		5
75 #define BORDER_X		5
76 #define BORDER_Y		5
77 #define INFO_COLS		50
78 #define INFO_ROWS		10
79 #define SAMPLES_COLS		50
80 #define SAMPLES_ROWS		10
81 #define BUCKETS_COLS		strlen(BUCKET_HEADER)
82 #define BUCKETS_ROWS		5
83 #define	VOLUME_FORMAT		"Volume: %3d%%"
84 #define	DEFAULT_VOLUME		10
85 #define BUF_SIZE 		200
86 #define	MAX_VOLUME		200
87 #define	MIN_VOLUME		1
88 #define	DOUBLE_CLICK_TIME	500000		/* in microseconds */
89 #define PORTAL_SIZE		100000
90 #define VOL			AuFixedPointFromFraction(globals->volume, 100)
91 
92 
93 #define AddToLinkedList(head, item)                                            \
94 {                                                                              \
95     (item)->prev = NULL;                                                       \
96     (item)->next = head;						       \
97     if (head)								       \
98 	(head)->prev = item;						       \
99     head = item;							       \
100 }
101 
102 #define RemoveFromLinkedList(head, item)				       \
103 {									       \
104     if ((item)->next)							       \
105 	(item)->next->prev = (item)->prev;				       \
106 									   \
107    if ((item)->prev)							      \
108        (item)->prev->next = (item)->next;				      \
109    else 								      \
110        head = (item)->next;						      \
111 }
112 
113 
114 static String   defaultResources[] =
115 {
116     "*font:		*courier-medium-r-normal*140*",
117     NULL
118 };
119 
120 static void     fatalError(const char *message, const char *arg);
121 
122 #define Invert(w)							       \
123 {									       \
124     Pixel fg, bg;							       \
125 									       \
126     XtVaGetValues(w, XtNforeground, &fg, XtNbackground, &bg, NULL);	       \
127     XtVaSetValues(w, XtNforeground, bg, XtNbackground, fg, NULL);	       \
128 }
129 
130 #define makeArg(resource, value)					       \
131     XtSetArg(args[i], resource, (XtArgVal) (value)); i++
132 
133 #define Anchor()							       \
134     makeArg(XtNtop, XawChainTop);					       \
135     makeArg(XtNbottom, XawChainTop);					       \
136     makeArg(XtNleft, XawChainLeft);					       \
137     makeArg(XtNright, XawChainLeft);					       \
138 
139 #define MakeCommandButton(w, parent, label, horiz, vert, callback)	       \
140 {									       \
141     Arg args[6];							       \
142     int i = 0;								       \
143 									       \
144     Anchor();								       \
145     makeArg(XtNfromHoriz, horiz);					       \
146     makeArg(XtNfromVert, vert);						       \
147 									       \
148     (w) = XtCreateManagedWidget(label, commandWidgetClass, parent, args, i);   \
149 									       \
150     /* SUPPRESS 558 */							       \
151     if ((void *) (callback) != NULL)					       \
152 	XtAddCallback(w, XtNcallback, callback, (XtPointer) globals);	       \
153 }
154 
155 #define MakeLabel(w, parent, label, horiz, vert)			       \
156 {									       \
157     Arg args[7];							       \
158     int i = 0;								       \
159 									       \
160     Anchor();								       \
161     makeArg(XtNfromHoriz, horiz);					       \
162     makeArg(XtNfromVert, vert);						       \
163     makeArg(XtNborderWidth, 0);						       \
164 									       \
165     (w) = XtCreateManagedWidget(label, labelWidgetClass, parent, args, i);     \
166 }
167 
168 typedef struct _VolumeList
169 {
170     AuFlowID        flow;
171     unsigned int    elementNumber;
172     struct _VolumeList *prev,
173                    *next;
174 }               VolumeListRec, *VolumeListPtr;
175 
176 typedef VolumeListPtr VolumeListId;
177 
178 typedef struct
179 {
180     Widget          topLevel,
181                     quit,
182                     form,
183                     play,
184                     stop,
185                     samples,
186                     info,
187                     view,
188                     volumeBar,
189                     volumeLabel;
190     int             volume;
191     AuServer       *aud;
192     Display        *dpy;
193     AuDeviceID      inputDeviceId;
194     char           *filename;
195 
196     Bool            playing;
197     AuFlowID        flow;
198     int             multiplier;
199     VolumeListId    vol;
200 }               GlobalDataRec, *GlobalDataPtr;
201 
202 static VolumeListPtr VolumeList;
203 static int      VolumeListCount;
204 
205 #define RemoveFromVolumeList(id)					       \
206 {									       \
207     RemoveFromLinkedList(VolumeList, (VolumeListPtr) id);		       \
208     VolumeListCount--;							       \
209 }
210 
211 static          VolumeListId
AddToVolumeList(AuFlowID flow,unsigned int elementNumber)212 AddToVolumeList(AuFlowID flow, unsigned int elementNumber)
213 {
214     VolumeListPtr   p;
215 
216     if (!(p = (VolumeListPtr) malloc(sizeof(VolumeListRec))))
217 	fatalError("malloc error in AddToVolumeList", NULL);
218 
219     p->flow = flow;
220     p->elementNumber = elementNumber;
221 
222     AddToLinkedList(VolumeList, p);
223     VolumeListCount++;
224 
225     return (VolumeListId) p;
226 }
227 
228 typedef struct
229 {
230     GlobalDataPtr   globals;
231     void            (*callback) ();
232 }               DonePrivRec, *DonePrivPtr;
233 
234 /* ARGSUSED */
235 static void
doneCB(AuServer * aud,AuEventHandlerRec * handler,AuEvent * ev,AuPointer d)236 doneCB(AuServer *aud, AuEventHandlerRec *handler, AuEvent *ev, AuPointer d)
237 {
238     DonePrivPtr     data = (DonePrivPtr) d;
239 
240     (*data->callback) (NULL, data->globals, data);
241 }
242 
243 /* ARGSUSED */
244 static void
stopCB(Widget w,XtPointer g,XtPointer data)245 stopCB(Widget w, XtPointer g, XtPointer data)
246 {
247     GlobalDataPtr   globals = (GlobalDataPtr) g;
248 
249     if (globals->playing)
250     {
251 	if (w)
252 	    /* user requested stop */
253 	    AuStopFlow(globals->aud, globals->flow, NULL);
254 	else
255 	{
256 	    /* got a done callback */
257 	    RemoveFromVolumeList(globals->vol);
258 	    globals->playing = False;
259 	    Invert(globals->play);
260 	}
261 
262 	return;
263     }
264 }
265 
266 /* ARGSUSED */
267 static void
playCB(Widget w,XtPointer g,XtPointer data)268 playCB(Widget w, XtPointer g, XtPointer data)
269 {
270     static DonePrivRec priv;
271     GlobalDataPtr   globals = (GlobalDataPtr) g;
272 
273     priv.globals = globals;
274     priv.callback = stopCB;
275 
276     if (globals->playing)
277 	return;
278     if (AuSoundPlayFromFile(globals->aud, globals->filename, AuNone,
279 	     VOL, doneCB, &priv, &globals->flow, &globals->multiplier, NULL,
280 			    NULL))
281     {
282 	globals->vol = AddToVolumeList(globals->flow, globals->multiplier);
283 	globals->playing = True;
284 	Invert(globals->play);
285     }
286 }
287 
288 static          Bool
showInfo(GlobalDataPtr globals)289 showInfo(GlobalDataPtr globals)
290 {
291     Sound           s;
292 
293     if ((s = SoundOpenFileForReading(globals->filename)))
294     {
295 	char           *buf,
296 	               *p;
297 
298 #define PRINT(p, f, a)							       \
299 {									       \
300     sprintf(p, f, a);							       \
301     p += strlen(p);							       \
302 }
303 
304 	if (!(p = buf = (char *) malloc(2000 + strlen(SoundComment(s)))))
305 	    fatalError("Can't malloc text in showInfo", NULL);
306 
307 	PRINT(p, "   Filename: %s\n", globals->filename);
308 	PRINT(p, "File Format: %s\n", SoundFileFormatString(s));
309 	PRINT(p, "Data Format: %s\n", SoundDataFormatString(s));
310 	PRINT(p, "     Tracks: %d\n", SoundNumTracks(s));
311 	PRINT(p, "  Frequency: %d Hz\n", SoundSampleRate(s));
312 	PRINT(p, "   Duration: %.2f seconds\n",
313 	      (float) SoundNumSamples(s) / SoundSampleRate(s));
314 
315 	PRINT(p, "\n%s", SoundComment(s));
316 
317 	XtVaSetValues(globals->info, XtNstring, buf, NULL);
318 
319 	free(buf);
320 	SoundCloseFile(s);
321 	return AuTrue;
322     }
323     else
324     {
325 	return AuFalse;
326     }
327 }
328 
329 /* ARGSUSED */
330 static void
quitCB(Widget w,XtPointer data,XtPointer call_data)331 quitCB(Widget w, XtPointer data, XtPointer call_data)
332 {
333     exit(0);
334 }
335 
336 static void
adjustVolume(GlobalDataPtr globals)337 adjustVolume(GlobalDataPtr globals)
338 {
339     AuElementParameters *parms;
340     VolumeListPtr   p = VolumeList;
341     int             i = 0;
342 
343     if (!VolumeListCount)
344 	return;
345 
346     if (!(parms = (AuElementParameters *)
347 	  malloc(sizeof(AuElementParameters) * VolumeListCount)))
348 	fatalError("malloc error in adjustVolume", NULL);
349 
350     while (p)
351     {
352 	parms[i].flow = p->flow;
353 	parms[i].element_num = p->elementNumber;
354 	parms[i].num_parameters = AuParmsMultiplyConstant;
355 	parms[i].parameters[AuParmsMultiplyConstantConstant] = VOL;
356 
357 	p = p->next;
358 	i++;
359     }
360 
361     AuSetElementParameters(globals->aud, VolumeListCount, parms, NULL);
362     free(parms);
363 }
364 
365 /* ARGSUSED */
366 static void
scrollProcCB(Widget w,XtPointer data,XtPointer cd)367 scrollProcCB(Widget w, XtPointer data, XtPointer cd)
368 {
369     GlobalDataPtr   globals = (GlobalDataPtr) data;
370     intptr_t        position = (intptr_t) cd;
371     int             newVolume;
372     char            buf[50];
373 
374     newVolume = globals->volume + (position > 0 ? -1 : 1);
375 
376     if (newVolume < MIN_VOLUME)
377 	newVolume = MIN_VOLUME;
378     else if (newVolume > MAX_VOLUME)
379 	newVolume = MAX_VOLUME;
380 
381     if (newVolume != globals->volume)
382     {
383 	globals->volume = newVolume;
384 	sprintf(buf, VOLUME_FORMAT, globals->volume);
385 	XtVaSetValues(globals->volumeLabel, XtNlabel, buf, NULL);
386 
387 	XawScrollbarSetThumb(globals->volumeBar,
388 			     (float) globals->volume / MAX_VOLUME, -1.0);
389 	adjustVolume(globals);
390     }
391 }
392 
393 /* ARGSUSED */
394 static void
jumpProcCB(Widget w,XtPointer data,XtPointer cd)395 jumpProcCB(Widget w, XtPointer data, XtPointer cd)
396 {
397     int             newVolume;
398     char            buf[50];
399     GlobalDataPtr   globals = (GlobalDataPtr) data;
400     float          *percent = (float *) cd;;
401 
402     newVolume = *percent * MAX_VOLUME;
403 
404     if (newVolume < MIN_VOLUME)
405 	newVolume = MIN_VOLUME;
406 
407     if (newVolume != globals->volume)
408     {
409 	globals->volume = newVolume;
410 	sprintf(buf, VOLUME_FORMAT, globals->volume);
411 	XtVaSetValues(globals->volumeLabel, XtNlabel, buf, NULL);
412 	adjustVolume(globals);
413     }
414 }
415 
416 static          Dimension
getFontCharWidth(Widget w)417 getFontCharWidth(Widget w)
418 {
419     XFontStruct    *font;
420 
421     XtVaGetValues(w, XtNfont, &font, NULL);
422     return font->max_bounds.rbearing - font->min_bounds.lbearing;
423 }
424 
425 static          Dimension
getFontCharHeight(Widget w)426 getFontCharHeight(Widget w)
427 {
428     XFontStruct    *font;
429 
430     XtVaGetValues(w, XtNfont, &font, NULL);
431     return font->max_bounds.ascent + font->max_bounds.descent;
432 }
433 
434 static char    *progname;
435 
436 static void
usage(void)437 usage(void)
438 {
439     fprintf(stderr,
440 	    "Usage: %s [-audio servername] [-volume percentage] [-toolkitoption ...] file\n", APP_INSTANCE);
441     exit(-1);
442 }
443 
444 int
main(int argc,char ** argv)445 main(int argc, char **argv)
446 {
447     int             i;
448     Dimension       infoWidth,
449                     labelHeight;
450     Arg             args[10];
451     char            buf[BUF_SIZE],
452                    *audioServerString = NULL;
453     GlobalDataRec   globalData,
454                    *globals;
455     Position        infoX,
456                     barX;
457     XtAppContext    appContext;
458     Widget          w;
459 
460     /* static          XtActionsRec Actions[] = {"Ok", okAction}; */
461 
462     progname = argv[0];
463 
464     globals = &globalData;
465 
466     globals->topLevel = XtVaAppInitialize(&appContext, APP_CLASS, NULL, ZERO,
467 					  &argc, argv, defaultResources,
468 					  NULL, NULL);
469 
470     XtVaSetValues(globals->topLevel, XtNinput, True, NULL);
471 
472     globals->volume = DEFAULT_VOLUME;
473     globals->playing = False;
474     globals->dpy = XtDisplay(globals->topLevel);
475     globals->filename = NULL;
476 
477     while (argc > 1)
478     {
479 	argv++;
480 	argc--;
481 
482 	if (!strcmp("-a", *argv) || !strcmp("-audio", *argv))
483 	{
484 	    if (argv[1])
485 		audioServerString = argv[1];
486 	    else
487 		usage();
488 	    argv++;
489 	    argc--;
490 	}
491 	else if (!strcmp("-v", *argv) || !strcmp("-volume", *argv))
492 	{
493 	    if (argv[1])
494 		globals->volume = atoi(argv[1]);
495 	    else
496 		usage();
497 	    argv++;
498 	    argc--;
499 	}
500 	else if (**argv == '-')
501 	{
502 	    usage();
503 	}
504 	else
505 	    globals->filename = *argv;
506     }
507     if (!globals->filename)
508 	fatalError("No sound file to play", NULL);
509 
510 
511     if (!(globals->aud = AuOpenServer(audioServerString, 0, NULL, 0, NULL,
512 				      NULL)))
513 	fatalError("Can't connect to audio server %s", audioServerString);
514 
515     for (i = 0; i < AuServerNumDevices(globals->aud); i++)
516 	if ((AuDeviceKind(AuServerDevice(globals->aud, i)) ==
517 	     AuComponentKindPhysicalInput))
518 	{
519 	    globals->inputDeviceId =
520 		AuDeviceIdentifier(AuServerDevice(globals->aud, i));
521 	    break;
522 	}
523     /* XtAppAddActions(appContext, Actions, XtNumber(Actions)); */
524 
525     globals->form = XtCreateManagedWidget("form", formWidgetClass,
526 					  globals->topLevel, NULL, 0);
527 
528     MakeCommandButton(globals->play, globals->form, "Play", NULL, NULL, playCB);
529     MakeCommandButton(globals->stop, globals->form, "Stop", globals->play,
530 		      NULL, stopCB);
531     MakeCommandButton(globals->quit, globals->form, "Quit", globals->stop,
532 		      NULL, quitCB);
533 
534     sprintf(buf, "NAS %d.%d", AuServerProtocolMajorVersion(globals->aud),
535 	    AuServerProtocolMinorVersion(globals->aud));
536     MakeLabel(w, globals->form, buf, globals->quit, NULL);
537 
538     i = 0;
539     Anchor();
540     makeArg(XtNfromVert, globals->play);
541     sprintf(buf, VOLUME_FORMAT, globals->volume);
542     makeArg(XtNlabel, buf);
543     globals->volumeLabel = XtCreateManagedWidget("VolumeLabel",
544 						 labelWidgetClass,
545 						 globals->form, args, i);
546 
547     i = 0;
548     makeArg(XtNtop, XawChainTop);
549     makeArg(XtNbottom, XawChainTop);
550     makeArg(XtNleft, XawChainLeft);
551     makeArg(XtNfromHoriz, globals->volumeLabel);
552     makeArg(XtNfromVert, globals->play);
553     makeArg(XtNorientation, XtorientHorizontal);
554     makeArg(XtNresizable, True);
555     globals->volumeBar = XtCreateManagedWidget("VolumeBar",
556 					       scrollbarWidgetClass,
557 					       globals->form, args, i);
558     XawScrollbarSetThumb(globals->volumeBar,
559 			 ((float) globals->volume) / MAX_VOLUME, -1.0);
560     XtAddCallback(globals->volumeBar, XtNscrollProc, scrollProcCB, globals);
561     XtAddCallback(globals->volumeBar, XtNjumpProc, jumpProcCB, globals);
562 
563     i = 0;
564     makeArg(XtNtop, XawChainTop);
565     makeArg(XtNbottom, XawChainTop);
566     makeArg(XtNscrollVertical, XawtextScrollWhenNeeded);
567     makeArg(XtNwrap, XawtextWrapWord);
568     makeArg(XtNdisplayCaret, False);
569     makeArg(XtNfromVert, globals->volumeLabel);
570     globals->info = XtCreateManagedWidget("info", asciiTextWidgetClass,
571 					  globals->form, args, i);
572 
573     i = 0;
574     infoWidth = getFontCharWidth(globals->info) * INFO_COLS;
575     makeArg(XtNwidth, infoWidth);
576     makeArg(XtNheight, getFontCharHeight(globals->info) * INFO_ROWS);
577     XtSetValues(globals->info, args, i);
578 
579     if (!showInfo(globals))
580     {
581 	FILE           *fp;
582 
583 	/* figure out why it failed */
584 	fp = fopen(globals->filename, "r");
585 	if (fp)
586 	{
587 	    fprintf(stderr,
588 		    "Cannot parse sound file \"%s\".\n", globals->filename);
589 	}
590 	else
591 	{
592 	    perror(globals->filename);
593 	}
594 	fclose(fp);
595 	exit(-1);
596     }
597     XtRealizeWidget(globals->topLevel);
598 
599     XtVaGetValues(globals->info, XtNx, &infoX, NULL);
600     XtVaGetValues(globals->volumeBar, XtNx, &barX, NULL);
601     XtVaGetValues(globals->volumeLabel, XtNheight, &labelHeight, NULL);
602     XtVaSetValues(globals->volumeBar, XtNwidth, infoX + infoWidth - barX,
603 		  XtNheight, labelHeight, NULL);
604 
605     AuXtAppAddAudioHandler(appContext, globals->aud);
606     playCB(globals->play, (XtPointer) globals, (XtPointer) 0);
607     XtAppMainLoop(appContext);
608     return 0;
609 }
610 
611 static void
fatalError(const char * message,const char * arg)612 fatalError(const char *message, const char *arg)
613 {
614     fprintf(stderr, message, arg);
615     fprintf(stderr, ".\n");
616     exit(1);
617 }
618