1 /**
2  * Copyright 1993 Network Computing Devices, Inc.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee, provided
6  * that 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  * Author:	Greg Renda <greg@ncd.com>
23  * 		Network Computing Devices, Inc.
24  * 		350 North Bernardo Ave.
25  * 		Mountain View, CA  94043
26  *
27  * $NCDId: @(#)audemo.c,v 1.60 1995/12/06 01:08:33 greg Exp $
28  */
29 #include "config.h"
30 
31 #include <inttypes.h>
32 #include <stdio.h>
33 
34 #if defined(HAVE_STDLIB_H)
35 # include <stdlib.h>
36 #endif
37 
38 #if defined(HAVE_MALLOC_H)
39 # include <malloc.h>
40 #endif
41 
42 #include <signal.h>
43 #if !defined(SYSV) || defined(WIN32)
44 #include <audio/Aos.h>		/* for string and other os stuff */
45 #endif
46 #include <audio/Afuncs.h>	/* for bcopy et. al. */
47 #include <audio/audiolib.h>
48 #include <audio/soundlib.h>
49 
50 #if defined(SYSV) && !defined(WIN32)
51 #include <sys/time.h>		/* for timeval */
52 
53 /* not defined in <stdio.h> when -Xc used */
54 extern FILE    *
55 popen(
56 #if NeedFunctionPrototypes
57       const char *, const char *
58 #endif
59 );
60 #endif						/* SVR4 */
61 
62 #ifdef WIN32
63 #include <winsock.h>
64 #endif /* WIN32 */
65 
66 #define AddToLinkedList(head, item)					       \
67 {									       \
68     (item)->prev = NULL;						       \
69     (item)->next = head;						       \
70     if (head)								       \
71 	(head)->prev = item;						       \
72     head = item;							       \
73 }
74 
75 #define RemoveFromLinkedList(head, item)				       \
76 {									       \
77     if ((item)->next)							       \
78 	(item)->next->prev = (item)->prev;				       \
79 									       \
80     if ((item)->prev)							       \
81 	(item)->prev->next = (item)->next;				       \
82     else								       \
83 	head = (item)->next;						       \
84 }
85 
86 #define XT
87 
88 #ifdef XT
89 #include <X11/Intrinsic.h>
90 #include <X11/StringDefs.h>
91 #include <X11/Shell.h>
92 #include <X11/Xaw/Cardinals.h>
93 #include <audio/Xtutil.h>
94 
95 /* widgets */
96 #include <X11/Xaw/Command.h>
97 #include <X11/Xaw/Form.h>
98 #include <X11/Xaw/List.h>
99 #include <X11/Xaw/Viewport.h>
100 #include <X11/Xaw/AsciiText.h>
101 #include <X11/Xaw/Scrollbar.h>
102 #include <X11/Xaw/Label.h>
103 #include <X11/Xaw/SimpleMenu.h>
104 #include <X11/Xaw/MenuButton.h>
105 #include <X11/Xaw/SmeBSB.h>
106 #include <X11/Xaw/Toggle.h>
107 #endif						/* XT */
108 
109 #define APP_INSTANCE		"audemo"
110 #define	APP_CLASS		"Audemo"
111 #define	VOLUME_FORMAT		"Volume: %3d%%"
112 #define	GAIN_FORMAT		"Gain: %3d%%"
113 #define	DEFAULT_VOLUME		100
114 #define BUF_SIZE 		200
115 #define	MAX_VOLUME		200
116 #define	MIN_VOLUME		1
117 #define	MAX_GAIN		100
118 #define	MIN_GAIN		1
119 #define	DOUBLE_CLICK_TIME	500000		/* in microseconds */
120 
121 #define VOL			AuFixedPointFromFraction(globals->volume, 100)
122 #define GAIN			AuFixedPointFromSum(globals->rec.gain, 0)
123 
124 #define BUCKET_HEADER_FORMAT \
125 "%-20s  %5.2f     %2d     %5d Hz     %c      %s"
126 
127 #define MakePopup(_w, _parent, _type, _name)				       \
128 {									       \
129     (_w) =								       \
130 	XtCreatePopupShell(_name, _type, _parent, NULL, 0);		       \
131 }
132 
133 #define MakeWidget(_w, _parent, _type, _callback, _name)		       \
134 {									       \
135     (_w) = XtCreateManagedWidget(_name, _type, _parent, NULL, 0);	       \
136 									       \
137     if ((void *) (_callback) != NULL)					       \
138 	XtAddCallback(_w, XtNcallback, _callback, g);			       \
139 }
140 
141 #define MakeButton(_w, _parent, _callback, _name)			       \
142     MakeWidget(_w, _parent, commandWidgetClass, _callback, _name)
143 
144 #define MakeLabel(_w, _parent, _name)					       \
145     MakeWidget(_w, _parent, labelWidgetClass, NULL, _name)
146 
147 #define MakeMenuItem(_parent, _label)					       \
148     XtCreateManagedWidget(_label, smeBSBObjectClass, _parent, NULL, 0)
149 
150 static String   defaultResources[] =
151 {
152 #include "resources.h"
153     NULL
154 };
155 
156 static char   **makeFileList(char **fileNames, int nfiles);
157 static int      getFileNames(char *dir, char ***fileNames, char *template);
158 static void     fatalError(const char *message, const char *arg),
159                 bucketQueryCB(Widget w, XtPointer globalsp, XtPointer call_data),
160                 bucketRecordDismissCB(Widget w, XtPointer globalsp, XtPointer call_data),
161                 meterCB();
162 
163 #ifdef XT
164 #define Invert(w)							       \
165 {									       \
166     Pixel fg, bg;							       \
167 									       \
168     XtVaGetValues(w, XtNforeground, &fg, XtNbackground, &bg, NULL);	       \
169     XtVaSetValues(w, XtNforeground, bg, XtNbackground, fg, NULL);	       \
170 }
171 
172 typedef struct
173 {
174     Widget          popShell,
175                     file,
176                     fileFormatMenuButton,
177                     form;
178 }               SaveDialogDataRec, *SaveDialogDataPtr;
179 
180 typedef struct
181 {
182     Widget          popShell,
183                     form,
184                     record,
185                     dismiss,
186                     file,
187                     fileFormatMenuButton,
188                     dataFormatMenuButton,
189                     rate,
190                     monitor,
191                     comment,
192                     duration,
193                     new,
194                     readOnly,
195                     mode,
196                     gainLabel,
197                     gainBar;
198     unsigned int    gain;
199 }               RecordDialogDataRec, *RecordDialogDataPtr;
200 
201 typedef struct
202 {
203     Widget          popShell,
204                     form,
205                     dismiss,
206                     viewport,
207                     bucketList,
208                     query,
209                     play,
210                     record,
211                     load,
212                     delete,
213                     save,
214                     formatMenu,
215                     formatMenuButton,
216                     accessMenu,
217                     accessMenuButton;
218     int             numBuckets;
219     AuBucketAttributes *list;
220     char          **bucketText;
221 }               BucketDialogDataRec, *BucketDialogDataPtr;
222 
223 typedef struct
224 {
225     Widget          topLevel,
226                     meter,
227                     quit,
228                     form,
229                     play,
230                     samples,
231                     info,
232                     viewport,
233                     volumeBar,
234                     volumeLabel,
235                     leftMeter,
236                     rightMeter,
237                     record,
238                     buckets,
239                     directory,
240                     template,
241                     rescan;
242     int             volume,
243                     numFiles,
244                     inputDeviceNum,
245                     sourceIndex;
246     AuServer       *aud;
247     char          **fileNames;
248     Display        *dpy;
249     RecordDialogDataRec rec;
250     BucketDialogDataRec buf;
251     SaveDialogDataRec save;
252     AuDeviceID      inputDeviceId;
253 }               GlobalDataRec, *GlobalDataPtr;
254 
255 typedef struct _ElementList
256 {
257     AuFlowID        flow;
258     int             volumeElement,
259                     monitorElement;
260     struct _ElementList *prev,
261                    *next;
262 }               ElementListRec, *ElementListPtr;
263 
264 typedef ElementListPtr ElementListId;
265 
266 static ElementListPtr ElementList;
267 static int      ElementCount,
268                 MonitorCount;
269 
270 #if !defined(sun) && !defined(linux) && !defined(__GNU__) && !defined(__GLIBC__)		/* who else doesn't have
271 						 * this? */
272 #define strdup ncd_strdup			/* To avoid conflicting with
273 						 * headers on hosts that *do*
274 						 * have strdup... */
275 char           *
strdup(char * s)276 strdup(char *s)
277 {
278     char           *t;
279 
280     if (!s)
281 	return s;
282     t = (char *) malloc(strlen(s) + 1);
283     strcpy(t, s);
284     return t;
285 }
286 #endif
287 
288 static void
RemoveFromElementList(GlobalDataPtr globals,ElementListPtr p)289 RemoveFromElementList(GlobalDataPtr globals, ElementListPtr p)
290 {
291     RemoveFromLinkedList(ElementList, p);
292     ElementCount--;
293 
294     if (p->monitorElement != -1)
295     {
296 	MonitorCount--;
297 
298 	if (!MonitorCount)
299 	    meterCB(globals->aud, NULL, NULL, globals);
300     }
301 
302     free(p);
303 }
304 
305 static          ElementListId
AddToElementList(AuFlowID flow,int volumeElement,int monitorElement)306 AddToElementList(AuFlowID flow, int volumeElement, int monitorElement)
307 {
308     ElementListPtr  p;
309 
310     if (!(p = (ElementListPtr) malloc(sizeof(ElementListRec))))
311 	fatalError("malloc error in AddToElementList", NULL);
312 
313     p->flow = flow;
314     p->volumeElement = volumeElement;
315     p->monitorElement = monitorElement;
316 
317     AddToLinkedList(ElementList, p);
318     ElementCount++;
319 
320     if (monitorElement != -1)
321 	MonitorCount++;
322 
323     return (ElementListId) p;
324 }
325 
326 static void
queryInputAttributes(GlobalDataPtr globals)327 queryInputAttributes(GlobalDataPtr globals)
328 {
329     Boolean         mode;
330     AuDeviceAttributes *attr;
331     char            buf[50];
332 
333     attr = AuGetDeviceAttributes(globals->aud, globals->inputDeviceId, NULL);
334     globals->rec.gain = AuFixedPointRoundUp(AuDeviceGain(attr));
335     sprintf(buf, GAIN_FORMAT, globals->rec.gain);
336     XtVaSetValues(globals->rec.gainLabel, XtNlabel, buf, NULL);
337     XawScrollbarSetThumb(globals->rec.gainBar,
338 			 (float) globals->rec.gain / MAX_GAIN, -1.0);
339 
340     mode = AuDeviceLineMode(attr) == AuDeviceLineModeHigh;
341     XtVaSetValues(globals->rec.mode, XtNstate, mode, NULL);
342 
343     AuFreeDeviceAttributes(globals->aud, 1, attr);
344 }
345 
346 static void
meterToggleCB(Widget w,XtPointer globalsp,XtPointer call_data)347 meterToggleCB(Widget w, XtPointer globalsp, XtPointer call_data)
348 {
349     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
350     Boolean         meter;
351     AuElementState *states,
352                    *s;
353     ElementListPtr  p;
354     int             state;
355 
356     XtVaGetValues(globals->meter, XtNstate, &meter, NULL);
357 
358     state = meter ? AuStateStart : AuStateStop;
359 
360     if (MonitorCount)
361     {
362 	if (!(states = (AuElementState *) malloc(sizeof(AuElementState) *
363 						 MonitorCount)))
364 	    return;
365 
366 	p = ElementList;
367 	s = states;
368 
369 	while (p)
370 	{
371 	    if (p->monitorElement != -1)
372 	    {
373 		AuMakeElementState(s, p->flow, p->monitorElement, state);
374 		s++;
375 	    }
376 
377 	    p = p->next;
378 	}
379 
380 	AuSetElementStates(globals->aud, MonitorCount, states, NULL);
381 	free(states);
382 	meterCB(globals->aud, NULL, NULL, globals);
383     }
384 }
385 
386 #define MonitorNotifyEventMin(e, i) ((short *) &(e)->data)[i * (e)->num_fields]
387 #define MonitorNotifyEventMax(e, i)					       \
388     ((short *) &(e)->data)[i * (e)->num_fields + 1]
389 
meterCB(AuServer * aud,AuEventHandlerRec * handler,AuEvent * ev,GlobalDataPtr globals)390 static void meterCB(AuServer *aud, AuEventHandlerRec *handler, AuEvent *ev,
391                     GlobalDataPtr globals)
392 {
393     AuMonitorNotifyEvent *e = (AuMonitorNotifyEvent *) ev;
394     static int      count;
395     static float    left,
396                     right;
397     float           l;
398     Boolean         meter;
399 
400 
401     if (!ev)
402     {
403 	XawScrollbarSetThumb(globals->leftMeter, -1.0, 0);
404 	XawScrollbarSetThumb(globals->rightMeter, -1.0, 0);
405 	count = left = right = 0;
406 	return;
407     }
408 
409     XtVaGetValues(globals->meter, XtNstate, &meter, NULL);
410 
411     if (!meter)
412 	return;
413 
414     l = (float) (MonitorNotifyEventMax(e, 0) -
415 		 MonitorNotifyEventMin(e, 0)) / 65535;
416 
417     left += l;
418 
419     if (e->num_tracks == 1)
420 	right += l;
421     else
422 	right += (float) (MonitorNotifyEventMax(e, 1) -
423 			  MonitorNotifyEventMin(e, 1)) / 65535;
424 
425     count++;
426 
427     if (count >= MonitorCount)
428     {
429 	left /= MonitorCount;
430 	right /= MonitorCount;
431 
432 	XawScrollbarSetThumb(globals->leftMeter, -1.0, left);
433 	XawScrollbarSetThumb(globals->rightMeter, -1.0, right);
434 	count = left = right = 0;
435     }
436 }
437 
438 typedef struct
439 {
440     GlobalDataPtr   globals;
441     void            (*callback) ();
442 }               DonePrivRec, *DonePrivPtr;
443 
444 static void
doneCB(AuServer * aud,AuEventHandlerRec * handler,AuEvent * ev,AuPointer datap)445 doneCB(AuServer *aud, AuEventHandlerRec *handler, AuEvent *ev, AuPointer datap)
446 {
447     DonePrivPtr     data = (DonePrivPtr) datap;
448 
449     if (ev->type == AuEventTypeMonitorNotify)
450 	meterCB(aud, handler, ev, data->globals);
451     else
452 	(*data->callback) (NULL, data->globals, data);
453 }
454 
455 static void
modeCB(Widget w,XtPointer globalsp,XtPointer call_data)456 modeCB(Widget w, XtPointer globalsp, XtPointer call_data)
457 {
458     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
459     Boolean         mode;
460     AuDeviceAttributes a;
461 
462     XtVaGetValues(globals->rec.mode, XtNstate, &mode, NULL);
463     AuDeviceValueMask(&a) = AuCompDeviceLineModeMask;
464     AuDeviceLineMode(&a) = mode ? AuDeviceLineModeHigh : AuDeviceLineModeLow;
465     AuSetDeviceAttributes(globals->aud, globals->inputDeviceId,
466 			  AuCompDeviceLineModeMask, &a, NULL);
467 }
468 
469 static void
newBucketCB(Widget w,XtPointer globalsp,XtPointer call_data)470 newBucketCB(Widget w, XtPointer globalsp, XtPointer call_data)
471 {
472     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
473     String          s;
474     unsigned int    rate,
475                     format;
476     Boolean         readOnly;
477     AuString        desc;
478     float           maxDuration;
479 
480     XtVaGetValues(globals->rec.rate, XtNstring, &s, NULL);
481     rate = atoi(s);
482     XtVaGetValues(globals->rec.duration, XtNstring, &s, NULL);
483     sscanf(s, "%f", &maxDuration);
484     XtVaGetValues(globals->rec.readOnly, XtNstate, &readOnly, NULL);
485     XtVaGetValues(globals->rec.dataFormatMenuButton, XtNlabel, &s, NULL);
486     format = AuStringToFormat(s);
487     XtVaGetValues(globals->rec.comment, XtNstring, &s, NULL);
488 
489     desc.type = AuStringLatin1;
490     desc.len = strlen(s);
491     desc.data = s;
492 
493     AuCreateBucket(globals->aud, format, 1,
494 		   readOnly ? AuAccessImportMask | AuAccessListMask :
495 		   AuAccessAllMasks, rate,
496 		   (AuUint32) (maxDuration * rate), &desc, NULL);
497 
498     bucketQueryCB(globals->buf.query, globals, NULL);
499 }
500 
501 static void
rescanCB(Widget w,XtPointer globalsp,XtPointer call_data)502 rescanCB(Widget w, XtPointer globalsp, XtPointer call_data)
503 {
504     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
505     static char    *noFilesString = "No files found";
506     char           *dir,
507                    *template;
508     int             i;
509 
510     if (globals->numFiles)
511     {
512 	for (i = 0; i < globals->numFiles; i++)
513 	    free(globals->fileNames[i]);
514 
515 	free(globals->fileNames);
516     }
517 
518     XtVaGetValues(globals->directory, XtNstring, &dir, NULL);
519     XtVaGetValues(globals->template, XtNstring, &template, NULL);
520     globals->numFiles = getFileNames(dir, &globals->fileNames, template);
521 
522     if (globals->numFiles)
523     {
524 	XawListChange(globals->samples,
525 		      makeFileList(globals->fileNames, globals->numFiles),
526 		      globals->numFiles, -1, True);
527 	XtSetSensitive(globals->samples, True);
528     }
529     else
530     {
531 	XawListChange(globals->samples, &noFilesString, 1, -1, True);
532 	XtSetSensitive(globals->samples, False);
533     }
534 }
535 
536 static void
bucketPlayCB(Widget w,XtPointer globalsp,XtPointer call_data)537 bucketPlayCB(Widget w, XtPointer globalsp, XtPointer call_data)
538 {
539     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
540     static Boolean  playing;
541     static AuFlowID flow;
542     static int      multiplier;
543     static DonePrivRec priv;
544     static ElementListId listid;
545     XawListReturnStruct *sel;
546     BucketDialogDataPtr buf = &globals->buf;
547     Boolean         meter;
548     int             ind,
549                     monitor = -1;
550 
551     if (playing)
552     {
553 	if (w)
554 	    /* user requested stop */
555 	    AuStopFlow(globals->aud, flow, NULL);
556 	else
557 	{
558 	    /* got a done callback */
559 	    RemoveFromElementList(globals, listid);
560 	    playing = False;
561 	    Invert(globals->buf.play);
562 	}
563 
564 	return;
565     }
566     else if (w != globals->buf.play)
567 	return;
568 
569     sel = XawListShowCurrent(buf->bucketList);
570 
571     if ((ind = sel->list_index) == -1)
572 	return;
573 
574     priv.globals = globals;
575     priv.callback = bucketPlayCB;
576 
577     XtVaGetValues(globals->meter, XtNstate, &meter, NULL);
578 
579     if (AuSoundPlayFromBucket(globals->aud,
580 			      AuBucketIdentifier(&buf->list[ind]), AuNone,
581 			      VOL, doneCB, &priv, 1, &flow, &multiplier,
582 			      meter ? &monitor : NULL, NULL))
583     {
584 	listid = AddToElementList(flow, multiplier, monitor);
585 	playing = True;
586 	Invert(globals->buf.play);
587     }
588 }
589 
590 #define COMMENT_LEN 20
591 
592 static void
bucketQueryCB(Widget w,XtPointer globalsp,XtPointer call_data)593 bucketQueryCB(Widget w, XtPointer globalsp, XtPointer call_data)
594 {
595     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
596     BucketDialogDataPtr buf = &globals->buf;
597     char            tmp[200],
598                     access[4],
599                     desc[COMMENT_LEN];
600     int             i;
601     static char    *noBucketString = "No buckets";
602 
603     if (buf->numBuckets)
604     {
605 	AuFreeBucketAttributes(globals->aud, buf->numBuckets, buf->list);
606 
607 	for (i = 0; i < buf->numBuckets; i++)
608 	    free(buf->bucketText[i]);
609 
610 	free(buf->bucketText);
611     }
612 
613     buf->list = AuListBuckets(globals->aud, 0, NULL, &buf->numBuckets, NULL);
614 
615     if (buf->numBuckets &&
616     !(buf->bucketText = (char **) malloc(sizeof(char *) * buf->numBuckets)))
617 	fatalError("malloc error in bucketQueryCB", NULL);
618 
619     for (i = 0; i < buf->numBuckets; i++)
620     {
621 	AuBucketAttributes *p;
622 	int             j;
623 	char            format;
624 
625 	p = &buf->list[i];
626 	*desc = 0;
627 	*access = 0;
628 
629 	if (AuBucketValueMask(p) & AuCompCommonDescriptionMask)
630 	{
631 	    strncpy(desc, AuBucketDescription(p)->data, COMMENT_LEN - 1);
632 	    desc[COMMENT_LEN - 1] = 0;
633 	}
634 
635 	if (AuBucketAccess(p) & AuAccessImportMask)
636 	    strcat(access, "I");
637 	if (AuBucketAccess(p) & AuAccessExportMask)
638 	    strcat(access, "E");
639 	if (AuBucketAccess(p) & AuAccessDestroyMask)
640 	    strcat(access, "D");
641 
642 	for (j = 0, format = 'A'; j < AuServerNumFormats(globals->aud);
643 	     j++, format++)
644 	    if (AuBucketFormat(p) == AuServerFormat(globals->aud, j))
645 		break;
646 
647 	sprintf(tmp, BUCKET_HEADER_FORMAT,
648 		desc, (float) AuBucketNumSamples(p) / AuBucketSampleRate(p),
649 		AuBucketNumTracks(p), AuBucketSampleRate(p), format, access);
650 
651 	buf->bucketText[i] = strdup(tmp);
652     }
653 
654     if (buf->numBuckets)
655     {
656 	XawListChange(buf->bucketList, buf->bucketText, buf->numBuckets, -1,
657 		      True);
658 	XtSetSensitive(buf->bucketList, True);
659     }
660     else
661     {
662 	XawListChange(buf->bucketList, &noBucketString, 1, -1, True);
663 	XtSetSensitive(buf->bucketList, False);
664     }
665 }
666 
667 static void
bucketDeleteCB(Widget w,XtPointer globalsp,XtPointer call_data)668 bucketDeleteCB(Widget w, XtPointer globalsp, XtPointer call_data)
669 {
670     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
671     XawListReturnStruct *sel;
672     BucketDialogDataPtr buf = &globals->buf;
673     int             ind;
674 
675     sel = XawListShowCurrent(buf->bucketList);
676 
677     if ((ind = sel->list_index) == -1)
678 	return;
679 
680     AuDestroyBucket(globals->aud, AuBucketIdentifier(&buf->list[ind]), NULL);
681     bucketQueryCB(buf->query, globals, NULL);
682 }
683 
684 static void
bucketLoadCB(Widget w,XtPointer globalsp,XtPointer call_data)685 bucketLoadCB(Widget w, XtPointer globalsp, XtPointer call_data)
686 {
687     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
688     XawListReturnStruct *sel;
689     int             ind;
690 
691     sel = XawListShowCurrent(globals->samples);
692 
693     if ((ind = sel->list_index) == -1)
694 	return;
695 
696     AuSoundCreateBucketFromFile(globals->aud, globals->fileNames[ind],
697 				AuAccessAllMasks, NULL, NULL);
698     bucketQueryCB(globals->buf.query, globals, NULL);
699 }
700 
701 static void
bucketsCB(Widget w,XtPointer globalsp,XtPointer call_data)702 bucketsCB(Widget w, XtPointer globalsp, XtPointer call_data)
703 {
704     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
705 
706     bucketQueryCB(globals->buf.query, globals, 0);
707     XtSetSensitive(w, False);
708     XtPopup(globals->buf.popShell, XtGrabNone);
709 }
710 
711 static void
bucketRecordStartCB(Widget w,XtPointer globalsp,XtPointer call_data)712 bucketRecordStartCB(Widget w, XtPointer globalsp, XtPointer call_data)
713 {
714     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
715     static Boolean  recording;
716     static DonePrivRec priv;
717     static AuFlowID flow;
718     Boolean         mode;
719     XawListReturnStruct *sel;
720     BucketDialogDataPtr buf = &globals->buf;
721     int             ind;
722 
723     if (recording)
724     {
725 	if (w)
726 	    /* user requested stop */
727 	    AuStopFlow(globals->aud, flow, NULL);
728 	else
729 	{
730 	    /* got a done callback */
731 	    recording = False;
732 	    Invert(globals->rec.record);
733 	}
734 
735 	return;
736     }
737     else if (w != globals->rec.record)
738 	return;
739 
740     sel = XawListShowCurrent(buf->bucketList);
741 
742     if ((ind = sel->list_index) == -1)
743 	return;
744 
745     priv.globals = globals;
746     priv.callback = bucketRecordStartCB;
747 
748     XtVaGetValues(globals->rec.mode, XtNstate, &mode, NULL);
749 
750     if (AuSoundRecordToBucket(globals->aud,
751 			      AuBucketIdentifier(&buf->list[ind]),
752 			      globals->inputDeviceId, GAIN, doneCB, &priv,
753 			  mode ? AuDeviceLineModeHigh : AuDeviceLineModeLow,
754 			      &flow, NULL, NULL))
755     {
756 	Invert(globals->rec.record);
757 	recording = True;
758     }
759 }
760 
761 static void
recordStartCB(Widget w,XtPointer globalsp,XtPointer call_data)762 recordStartCB(Widget w, XtPointer globalsp, XtPointer call_data)
763 {
764     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
765     static Boolean  recording;
766     static DonePrivRec priv;
767     static AuFlowID flow;
768     String          s,
769                     comment;
770     AuUint32        rate,
771                     dataFormat,
772                     fileFormat;
773     Boolean         mode;
774 
775     if (recording)
776     {
777 	if (w)
778 	    /* user requested stop */
779 	    AuStopFlow(globals->aud, flow, NULL);
780 	else
781 	{
782 	    /* got a done callback */
783 	    recording = False;
784 	    Invert(globals->rec.record);
785 	    rescanCB(globals->rescan, globals, 0);
786 	}
787 
788 	return;
789     }
790     else if (w != globals->rec.record)
791 	return;
792 
793     priv.globals = globals;
794     priv.callback = recordStartCB;
795 
796     XtVaGetValues(globals->rec.fileFormatMenuButton, XtNlabel, &s, NULL);
797     fileFormat = SoundStringToFileFormat(s);
798 
799     XtVaGetValues(globals->rec.dataFormatMenuButton, XtNlabel, &s, NULL);
800     dataFormat = AuStringToFormat(s);
801 
802     XtVaGetValues(globals->rec.rate, XtNstring, &s, NULL);
803     rate = atoi(s);
804 
805     XtVaGetValues(globals->rec.mode, XtNstate, &mode, NULL);
806     XtVaGetValues(globals->rec.comment, XtNstring, &comment, NULL);
807     XtVaGetValues(globals->rec.file, XtNstring, &s, NULL);
808 
809     if (strlen(s) &&
810 	AuSoundRecordToFile(globals->aud, s, globals->inputDeviceId, GAIN,
811 			    doneCB, &priv,
812 			    mode ? AuDeviceLineModeHigh :
813 			    AuDeviceLineModeLow,
814 			    fileFormat, comment, rate, dataFormat,
815 			    &flow, NULL, NULL))
816     {
817 	Invert(globals->rec.record);
818 	recording = True;
819     }
820 }
821 
822 static void
monitorCB(Widget w,XtPointer globalsp,XtPointer call_data)823 monitorCB(Widget w, XtPointer globalsp, XtPointer call_data)
824 {
825     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
826     static Boolean  monitoring;
827     static AuFlowID flow;
828     static int      multiplier;
829     static DonePrivRec priv;
830     static ElementListId listid;
831     String          s;
832     Boolean         meter;
833     int             rate,
834                     monitor = -1;
835 
836     if (monitoring)
837     {
838 	if (w)
839 	    /* user requested stop */
840 	    AuStopFlow(globals->aud, flow, NULL);
841 	else
842 	{
843 	    /* got a done callback */
844 	    RemoveFromElementList(globals, listid);
845 	    monitoring = False;
846 	    Invert(globals->rec.monitor);
847 	}
848 
849 	return;
850     }
851     else if (w != globals->rec.monitor)
852 	return;
853 
854     XtVaGetValues(globals->rec.rate, XtNstring, &s, NULL);
855     rate = atoi(s);
856 
857     priv.globals = globals;
858     priv.callback = monitorCB;
859 
860     XtVaGetValues(globals->meter, XtNstate, &meter, NULL);
861 
862     if (AuMonitorDevice(globals->aud, rate, globals->inputDeviceId, AuNone, VOL,
863 			doneCB, &priv, &flow, &multiplier,
864 			meter ? &monitor : NULL, NULL))
865     {
866 	listid = AddToElementList(flow, multiplier, monitor);
867 	monitoring = True;
868 	Invert(globals->rec.monitor);
869     }
870 }
871 
872 static void
saveCancel(Widget w,XtPointer globalsp,XtPointer call_data)873 saveCancel(Widget w, XtPointer globalsp, XtPointer call_data)
874 {
875     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
876 
877     XtPopdown(globals->save.popShell);
878     XtSetSensitive(globals->buf.save, True);
879 }
880 
881 static void
saveOk(Widget w,XtPointer globalsp,XtPointer call_data)882 saveOk(Widget w, XtPointer globalsp, XtPointer call_data)
883 {
884     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
885     String          s;
886     XawListReturnStruct *sel;
887     BucketDialogDataPtr buf = &globals->buf;
888     int             ind,
889                     fileFormat;
890 
891     sel = XawListShowCurrent(buf->bucketList);
892 
893     if ((ind = sel->list_index) == -1)
894 	return;
895 
896     XtVaGetValues(globals->save.fileFormatMenuButton, XtNlabel, &s, NULL);
897     fileFormat = SoundStringToFileFormat(s);
898 
899     XtVaGetValues(globals->save.file, XtNstring, &s, NULL);
900 
901     if (strlen(s) &&
902 	AuSoundCreateFileFromBucket(globals->aud, s, fileFormat,
903 				    AuBucketIdentifier(&buf->list[ind]),
904 				    NULL))
905     {
906 	saveCancel(w, globals, call_data);
907 	rescanCB(globals->rescan, globals, 0);
908     }
909 }
910 
911 static void
okAction(Widget w,XEvent * event,String * params,Cardinal * num_params)912 okAction(Widget w, XEvent *event, String *params, Cardinal *num_params)
913 {
914     GlobalDataPtr   globals;
915 
916     /* retrieve the address of the globals from the first parameter */
917     globals = (GlobalDataPtr)(uintptr_t)strtoull(params[0], NULL, 0);
918     saveOk(w, globals, 0);
919 }
920 
921 static void
bucketSaveCB(Widget w,XtPointer globalsp,XtPointer call_data)922 bucketSaveCB(Widget w, XtPointer globalsp, XtPointer call_data)
923 {
924     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
925     XawListReturnStruct *sel;
926 
927     sel = XawListShowCurrent(globals->buf.bucketList);
928 
929     if (sel->list_index == -1)
930 	return;
931 
932     XtSetSensitive(w, False);
933     XtPopup(globals->save.popShell, XtGrabNonexclusive);
934 }
935 
936 static void
bucketDismissCB(Widget w,XtPointer globalsp,XtPointer call_data)937 bucketDismissCB(Widget w, XtPointer globalsp, XtPointer call_data)
938 {
939     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
940 
941     if (globals->inputDeviceId)
942 	bucketRecordDismissCB(globals->rec.dismiss, globals, NULL);
943 
944     bucketPlayCB(NULL, globals, NULL);
945 
946     XtPopdown(globals->buf.popShell);
947     XtSetSensitive(globals->buckets, True);
948 }
949 
950 static void
recordDismissCB(Widget w,XtPointer globalsp,XtPointer call_data)951 recordDismissCB(Widget w, XtPointer globalsp, XtPointer call_data)
952 {
953     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
954 
955     monitorCB(w, globals, NULL);
956     recordStartCB(w, globals, NULL);
957 
958     XtPopdown(globals->rec.popShell);
959     XtSetSensitive(globals->record, True);
960     XtSetSensitive(globals->buf.record, True);
961 }
962 
963 static void
bucketRecordDismissCB(Widget w,XtPointer globalsp,XtPointer call_data)964 bucketRecordDismissCB(Widget w, XtPointer globalsp, XtPointer call_data)
965 {
966     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
967 
968     monitorCB(w, globals, NULL);
969     bucketRecordStartCB(w, globals, NULL);
970 
971     if (globals->inputDeviceId)
972     {
973 	XtPopdown(globals->rec.popShell);
974 	XtSetSensitive(globals->record, True);
975 	XtSetSensitive(globals->buf.record, True);
976     }
977 }
978 
979 #define ReplaceCallback(w, cb)						       \
980 {									       \
981     XtRemoveAllCallbacks(w, XtNcallback);				       \
982     XtAddCallback(w, XtNcallback, cb, globals);				       \
983 }
984 
985 static void
recordSensitives(GlobalDataPtr globals,Boolean state)986 recordSensitives(GlobalDataPtr globals, Boolean state)
987 {
988     XtSetSensitive(globals->rec.file, state);
989 }
990 
991 static void
bucketRecordSensitives(GlobalDataPtr globals,Boolean state)992 bucketRecordSensitives(GlobalDataPtr globals, Boolean state)
993 {
994     XtSetSensitive(globals->rec.duration, state);
995     XtSetSensitive(globals->rec.readOnly, state);
996     XtSetSensitive(globals->rec.new, state);
997 }
998 
999 static void
recordCB(Widget w,XtPointer globalsp,XtPointer call_data)1000 recordCB(Widget w, XtPointer globalsp, XtPointer call_data)
1001 {
1002     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1003 
1004     ReplaceCallback(globals->rec.record, recordStartCB);
1005     ReplaceCallback(globals->rec.dismiss, recordDismissCB);
1006 
1007     recordSensitives(globals, True);
1008     bucketRecordSensitives(globals, False);
1009     XtSetSensitive(globals->buf.record, False);
1010 
1011     queryInputAttributes(globals);
1012 
1013     XtSetSensitive(w, False);
1014     XtPopup(globals->rec.popShell, XtGrabNone);
1015 }
1016 
1017 static void
bucketRecordCB(Widget w,XtPointer globalsp,XtPointer call_data)1018 bucketRecordCB(Widget w, XtPointer globalsp, XtPointer call_data)
1019 {
1020     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1021 
1022     ReplaceCallback(globals->rec.record, bucketRecordStartCB);
1023     ReplaceCallback(globals->rec.dismiss, bucketRecordDismissCB);
1024 
1025     recordSensitives(globals, False);
1026     bucketRecordSensitives(globals, True);
1027     XtSetSensitive(globals->record, False);
1028 
1029     queryInputAttributes(globals);
1030 
1031     XtSetSensitive(w, False);
1032     XtPopup(globals->rec.popShell, XtGrabNone);
1033 }
1034 
1035 static void
playCB(Widget w,XtPointer globalsp,XtPointer datap)1036 playCB(Widget w, XtPointer globalsp, XtPointer datap)
1037 {
1038     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1039     static Boolean  playing;
1040     static AuFlowID flow;
1041     static int      multiplier;
1042     static DonePrivRec priv;
1043     static ElementListId listid;
1044     XawListReturnStruct *sel;
1045     Boolean         meter;
1046     int             ind,
1047                     monitor = -1;
1048 
1049     if (playing)
1050     {
1051 	if (w)
1052 	    /* user requested stop */
1053 	    AuStopFlow(globals->aud, flow, NULL);
1054 	else
1055 	{
1056 	    /* got a done callback */
1057 	    RemoveFromElementList(globals, listid);
1058 	    playing = False;
1059 	    Invert(globals->play);
1060 	}
1061 
1062 	return;
1063     }
1064 
1065     sel = XawListShowCurrent(globals->samples);
1066 
1067     if ((ind = sel->list_index) == -1)
1068 	return;
1069 
1070     priv.globals = globals;
1071     priv.callback = playCB;
1072 
1073     XtVaGetValues(globals->meter, XtNstate, &meter, NULL);
1074 
1075     if (AuSoundPlayFromFile(globals->aud, globals->fileNames[ind], AuNone,
1076 			    VOL, doneCB, &priv, &flow, &multiplier,
1077 			    meter ? &monitor : NULL, NULL))
1078     {
1079 	listid = AddToElementList(flow, multiplier, monitor);
1080 	playing = True;
1081 	Invert(globals->play);
1082     }
1083 }
1084 
1085 static void
bucketListCB(Widget w,XtPointer globalsp,XtPointer listInfop)1086 bucketListCB(Widget w, XtPointer globalsp, XtPointer listInfop)
1087 {
1088     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1089     XawListReturnStruct *listInfo = (XawListReturnStruct *) listInfop;
1090     struct timeval  tp;
1091     AuUint32        currentTime;
1092     static AuInt32  lastTime;
1093     static int      lastSelection;
1094 
1095     gettimeofday(&tp, NULL);
1096     currentTime = tp.tv_sec * 1000000 + tp.tv_usec;
1097 
1098     if (listInfo->list_index == lastSelection &&
1099 	currentTime - lastTime < DOUBLE_CLICK_TIME)
1100 	bucketPlayCB(globals->buf.play, globals, NULL);
1101 
1102     lastTime = currentTime;
1103     lastSelection = listInfo->list_index;
1104 }
1105 
1106 static void
samplesCB(Widget w,XtPointer globalsp,XtPointer listInfop)1107 samplesCB(Widget w, XtPointer globalsp, XtPointer listInfop)
1108 {
1109     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1110     XawListReturnStruct *listInfo = (XawListReturnStruct *) listInfop;
1111     struct timeval  tp;
1112     AuInt32         currentTime;
1113     static AuInt32  lastTime;
1114     static int      lastSelection = -1;
1115 
1116     gettimeofday(&tp, NULL);
1117     currentTime = tp.tv_sec * 1000000 + tp.tv_usec;
1118 
1119     if (listInfo->list_index == lastSelection &&
1120 	currentTime - lastTime < DOUBLE_CLICK_TIME)
1121 	playCB(globals->play, globals, (XtPointer) 0);
1122     else if (lastSelection != listInfo->list_index)
1123     {
1124 	Sound           s;
1125 
1126 	lastSelection = listInfo->list_index;
1127 
1128 	if ((s = SoundOpenFileForReading(globals->fileNames[lastSelection])))
1129 	{
1130 	    char           *buf,
1131 	                   *p;
1132 
1133 #define PRINT(p, f, a)							       \
1134 {									       \
1135     sprintf(p, f, a);							       \
1136     p += strlen(p);							       \
1137 }
1138 
1139 	    if (!(p = buf = (char *) malloc(2000 + strlen(SoundComment(s)))))
1140 		fatalError("Can't malloc text in samplesCB", NULL);
1141 
1142 	    PRINT(p, "   Filename: %s\n", globals->fileNames[lastSelection]);
1143 	    PRINT(p, "File Format: %s\n", SoundFileFormatString(s));
1144 	    PRINT(p, "Data Format: %s\n", SoundDataFormatString(s));
1145 	    PRINT(p, "     Tracks: %d\n", SoundNumTracks(s));
1146 	    PRINT(p, "  Frequency: %d Hz\n", SoundSampleRate(s));
1147 	    PRINT(p, "   Duration: %.2f seconds\n",
1148 		  (float) SoundNumSamples(s) / SoundSampleRate(s));
1149 
1150 	    PRINT(p, "\n%s", SoundComment(s));
1151 
1152 	    XtVaSetValues(globals->info, XtNstring, buf, NULL);
1153 
1154 	    free(buf);
1155 	    SoundCloseFile(s);
1156 	}
1157     }
1158 
1159     gettimeofday(&tp, NULL);
1160     lastTime = tp.tv_sec * 1000000 + tp.tv_usec;
1161 }
1162 
1163 static void
quitCB(Widget w,XtPointer globalsp,XtPointer call_data)1164 quitCB(Widget w, XtPointer globalsp, XtPointer call_data)
1165 {
1166     exit(0);
1167 }
1168 
1169 static void
adjustVolume(GlobalDataPtr globals)1170 adjustVolume(GlobalDataPtr globals)
1171 {
1172     AuElementParameters *parms;
1173     ElementListPtr  p = ElementList;
1174     int             i = 0;
1175 
1176     if (!ElementCount)
1177 	return;
1178 
1179     if (!(parms = (AuElementParameters *)
1180 	  malloc(sizeof(AuElementParameters) * ElementCount)))
1181 	fatalError("malloc error in adjustVolume", NULL);
1182 
1183     while (p)
1184     {
1185 	parms[i].flow = p->flow;
1186 	parms[i].element_num = p->volumeElement;
1187 	parms[i].num_parameters = AuParmsMultiplyConstant;
1188 	parms[i].parameters[AuParmsMultiplyConstantConstant] = VOL;
1189 
1190 	p = p->next;
1191 	i++;
1192     }
1193 
1194     AuSetElementParameters(globals->aud, ElementCount, parms, NULL);
1195     free(parms);
1196 }
1197 
1198 static void
scrollProcCB(Widget w,XtPointer globalsp,XtPointer positionp)1199 scrollProcCB(Widget w, XtPointer globalsp, XtPointer positionp)
1200 {
1201     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1202     intptr_t        position = (intptr_t) positionp;
1203     int             newVolume;
1204     char            buf[50];
1205 
1206     newVolume = globals->volume + (position > 0 ? -1 : 1);
1207 
1208     if (newVolume < MIN_VOLUME)
1209 	newVolume = MIN_VOLUME;
1210     else if (newVolume > MAX_VOLUME)
1211 	newVolume = MAX_VOLUME;
1212 
1213     if (newVolume != globals->volume)
1214     {
1215 	globals->volume = newVolume;
1216 	sprintf(buf, VOLUME_FORMAT, globals->volume);
1217 	XtVaSetValues(globals->volumeLabel, XtNlabel, buf, NULL);
1218 
1219 	XawScrollbarSetThumb(globals->volumeBar,
1220 			     (float) globals->volume / MAX_VOLUME, -1.0);
1221 	adjustVolume(globals);
1222     }
1223 }
1224 
1225 static void
jumpProcCB(Widget w,XtPointer globalsp,XtPointer percentp)1226 jumpProcCB(Widget w, XtPointer globalsp, XtPointer percentp)
1227 {
1228     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1229     float          *percent = (float *) percentp;
1230     int             newVolume;
1231     char            buf[50];
1232 
1233     newVolume = *percent * MAX_VOLUME;
1234 
1235     if (newVolume < MIN_VOLUME)
1236 	newVolume = MIN_VOLUME;
1237 
1238     if (newVolume != globals->volume)
1239     {
1240 	globals->volume = newVolume;
1241 	sprintf(buf, VOLUME_FORMAT, globals->volume);
1242 	XtVaSetValues(globals->volumeLabel, XtNlabel, buf, NULL);
1243 	adjustVolume(globals);
1244     }
1245 }
1246 
1247 static void
adjustGain(GlobalDataPtr globals)1248 adjustGain(GlobalDataPtr globals)
1249 {
1250     AuDeviceAttributes a;
1251 
1252     AuDeviceValueMask(&a) = AuCompDeviceGainMask;
1253     AuDeviceGain(&a) = GAIN;
1254     AuSetDeviceAttributes(globals->aud, globals->inputDeviceId,
1255 			  AuCompDeviceGainMask, &a, NULL);
1256 }
1257 
1258 static void
gainScrollCB(Widget w,XtPointer globalsp,XtPointer positionp)1259 gainScrollCB(Widget w, XtPointer globalsp, XtPointer positionp)
1260 {
1261     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1262     intptr_t        position = (intptr_t) positionp;
1263     int             newGain;
1264     char            buf[50];
1265 
1266     newGain = globals->rec.gain + (position > 0 ? -1 : 1);
1267 
1268     if (newGain < MIN_GAIN)
1269 	newGain = MIN_GAIN;
1270     else if (newGain > MAX_GAIN)
1271 	newGain = MAX_GAIN;
1272 
1273     if (newGain != globals->rec.gain)
1274     {
1275 	globals->rec.gain = newGain;
1276 	sprintf(buf, GAIN_FORMAT, globals->rec.gain);
1277 	XtVaSetValues(globals->rec.gainLabel, XtNlabel, buf, NULL);
1278 
1279 	XawScrollbarSetThumb(globals->rec.gainBar,
1280 			     (float) globals->rec.gain / MAX_GAIN, -1.0);
1281 	adjustGain(globals);
1282     }
1283 }
1284 
1285 static void
gainJumpCB(Widget w,XtPointer globalsp,XtPointer percentp)1286 gainJumpCB(Widget w, XtPointer globalsp, XtPointer percentp)
1287 {
1288     GlobalDataPtr   globals = (GlobalDataPtr) globalsp;
1289     float          *percent = (float *) percentp;
1290     int             newGain;
1291     char            buf[50];
1292 
1293     newGain = *percent * MAX_GAIN;
1294 
1295     if (newGain < MIN_GAIN)
1296 	newGain = MIN_GAIN;
1297 
1298     if (newGain != globals->rec.gain)
1299     {
1300 	globals->rec.gain = newGain;
1301 	sprintf(buf, GAIN_FORMAT, globals->rec.gain);
1302 	XtVaSetValues(globals->rec.gainLabel, XtNlabel, buf, NULL);
1303 	adjustGain(globals);
1304     }
1305 }
1306 
1307 static void
setFileFormatMenuButton(Widget w,XtPointer globalsp,XtPointer call_data)1308 setFileFormatMenuButton(Widget w, XtPointer globalsp, XtPointer call_data)
1309 {
1310     GlobalDataPtr   g = (GlobalDataPtr) globalsp;
1311     String          string;
1312 
1313     XtVaGetValues(w, XtNlabel, &string, NULL);
1314     XtVaSetValues(g->save.fileFormatMenuButton, XtNlabel, string, NULL);
1315 
1316     if (g->inputDeviceId)
1317 	XtVaSetValues(g->rec.fileFormatMenuButton, XtNlabel, string, NULL);
1318 }
1319 
1320 static void
setDataFormatMenuButton(Widget w,XtPointer globalsp,XtPointer call_data)1321 setDataFormatMenuButton(Widget w, XtPointer globalsp, XtPointer call_data)
1322 {
1323     GlobalDataPtr   g = (GlobalDataPtr) globalsp;
1324     String          string;
1325 
1326     XtVaGetValues(w, XtNlabel, &string, NULL);
1327     XtVaSetValues(g->rec.dataFormatMenuButton, XtNlabel, string, NULL);
1328 }
1329 
1330 static void
makeSaveDialog(GlobalDataPtr g)1331 makeSaveDialog(GlobalDataPtr g)
1332 {
1333     SaveDialogDataPtr s = &g->save;
1334     Widget          w;
1335     char            tmp[50];
1336 
1337     MakePopup(s->popShell, g->topLevel, transientShellWidgetClass,
1338 	      "savePop");
1339     MakeWidget(s->form, s->popShell, formWidgetClass, NULL, "saveWin");
1340 
1341     /* filename */
1342     MakeLabel(w, s->form, "filenameLabel");
1343     MakeWidget(s->file, s->form, asciiTextWidgetClass, NULL, "filename");
1344 
1345     /* pass the address of the globals as an argument to the action */
1346     sprintf(tmp, "<Key>Return: ok(%p)", g);
1347     XtOverrideTranslations(s->file, XtParseTranslationTable(tmp));
1348 
1349     /* file format */
1350     MakeLabel(w, s->form, "fileFormatLabel");
1351     MakeWidget(s->fileFormatMenuButton, s->form, menuButtonWidgetClass, NULL,
1352 	       "fileFormatMenuButton");
1353     XtVaSetValues(s->fileFormatMenuButton, XtNlabel, SoundFileFormatToString(0),
1354 		  NULL);
1355 
1356     MakeButton(w, s->form, saveOk, "ok");
1357     MakeButton(w, s->form, saveCancel, "cancel");
1358 }
1359 
1360 static void
makeBucketDialog(GlobalDataPtr g)1361 makeBucketDialog(GlobalDataPtr g)
1362 {
1363     Widget          w;
1364     int             i;
1365     BucketDialogDataPtr b = &g->buf;
1366     char            tmp[100];
1367 
1368     makeSaveDialog(g);
1369 
1370     b->numBuckets = 0;
1371     MakePopup(b->popShell, g->topLevel, transientShellWidgetClass,
1372 	      "bucketPop");
1373     MakeWidget(b->form, b->popShell, formWidgetClass, NULL, "bucketWin");
1374 
1375     /* command buttons */
1376     MakeButton(b->query, b->form, bucketQueryCB, "query");
1377     MakeButton(b->play, b->form, bucketPlayCB, "play");
1378     MakeButton(b->record, b->form, bucketRecordCB, "record");
1379     MakeButton(b->load, b->form, bucketLoadCB, "load");
1380     MakeButton(b->save, b->form, bucketSaveCB, "save");
1381     MakeButton(b->delete, b->form, bucketDeleteCB, "delete");
1382     MakeButton(b->dismiss, b->form, bucketDismissCB, "dismiss");
1383 
1384     /* format menu */
1385     MakePopup(b->formatMenu, b->form, simpleMenuWidgetClass, "formatMenu");
1386     for (i = 0; i < AuServerNumFormats(g->aud); i++)
1387     {
1388 	sprintf(tmp, "%c - %s", 'A' + i,
1389 		AuFormatToString(AuServerFormat(g->aud, i)));
1390 	MakeMenuItem(b->formatMenu, tmp);
1391     }
1392     MakeWidget(b->formatMenuButton, b->form, menuButtonWidgetClass, NULL,
1393 	       "format");
1394 
1395     /* access menu */
1396     MakePopup(b->accessMenu, b->form, simpleMenuWidgetClass, "accessMenu");
1397     MakeMenuItem(b->accessMenu, "I - Importable");
1398     MakeMenuItem(b->accessMenu, "E - Exportable");
1399     MakeMenuItem(b->accessMenu, "D - Destroyable");
1400     MakeWidget(b->accessMenuButton, b->form, menuButtonWidgetClass, NULL,
1401 	       "access");
1402 
1403     MakeLabel(w, b->form, "header");
1404     MakeWidget(b->viewport, b->form, viewportWidgetClass, NULL, "viewport");
1405 
1406     MakeWidget(b->bucketList, b->viewport, listWidgetClass, bucketListCB,
1407 	       "list");
1408 }
1409 
1410 static void
makeRecordDialog(GlobalDataPtr g)1411 makeRecordDialog(GlobalDataPtr g)
1412 {
1413     Widget          w;
1414     RecordDialogDataPtr r = &g->rec;
1415 
1416     MakePopup(r->popShell, g->topLevel, transientShellWidgetClass, "recordPop");
1417     MakeWidget(r->form, r->popShell, formWidgetClass, NULL, "recordWin");
1418 
1419     /* filename */
1420     MakeLabel(w, r->form, "filenameLabel");
1421     MakeWidget(r->file, r->form, asciiTextWidgetClass, NULL, "filename");
1422 
1423     /* file format */
1424     MakeLabel(w, r->form, "fileFormatLabel");
1425     MakeWidget(r->fileFormatMenuButton, r->form, menuButtonWidgetClass, NULL,
1426 	       "fileFormatMenuButton");
1427     XtVaSetValues(r->fileFormatMenuButton, XtNlabel, SoundFileFormatToString(0),
1428 		  NULL);
1429 
1430     /* data format */
1431     MakeLabel(w, r->form, "dataFormatLabel");
1432     MakeWidget(r->dataFormatMenuButton, r->form, menuButtonWidgetClass, NULL,
1433 	       "dataFormatMenuButton");
1434     XtVaSetValues(r->dataFormatMenuButton, XtNlabel,
1435 		  AuFormatToString(AuServerFormat(g->aud, 0)), NULL);
1436 
1437     /* max duration */
1438     MakeLabel(w, r->form, "durationLabel");
1439     MakeWidget(r->duration, r->form, asciiTextWidgetClass, NULL, "duration");
1440 
1441     /* read only toggle */
1442     MakeWidget(r->readOnly, r->form, toggleWidgetClass, NULL, "readOnly");
1443 
1444     /* frequency */
1445     MakeLabel(w, r->form, "frequencyLabel");
1446     MakeWidget(r->rate, r->form, asciiTextWidgetClass, NULL, "frequency");
1447 
1448     /* mode toggle */
1449     MakeWidget(r->mode, r->form, toggleWidgetClass, modeCB, "lineMode");
1450 
1451     /* comment */
1452     MakeLabel(w, r->form, "commentLabel");
1453     MakeWidget(r->comment, r->form, asciiTextWidgetClass, NULL, "comment");
1454 
1455     /* gain */
1456     MakeLabel(r->gainLabel, r->form, "gainLabel");
1457     MakeWidget(r->gainBar, r->form, scrollbarWidgetClass, NULL, "gain");
1458     XtAddCallback(r->gainBar, XtNscrollProc, gainScrollCB, g);
1459     XtAddCallback(r->gainBar, XtNjumpProc, gainJumpCB, g);
1460 
1461     /* command buttons */
1462     MakeButton(r->record, r->form, recordStartCB, "record");
1463     MakeButton(r->monitor, r->form, monitorCB, "monitor");
1464     MakeButton(r->new, r->form, newBucketCB, "new");
1465     MakeButton(r->dismiss, r->form, NULL, "dismiss");
1466 }
1467 
1468 static void
createWidgets(GlobalDataPtr g,char * dir)1469 createWidgets(GlobalDataPtr g, char *dir)
1470 {
1471     Widget          w,
1472                     ww;
1473     int             i;
1474     char            buf[20];
1475 
1476     MakeWidget(g->form, g->topLevel, formWidgetClass, NULL, "mainWin");
1477 
1478     /* command buttons */
1479     MakeButton(g->play, g->form, playCB, "play");
1480     MakeButton(g->record, g->form, recordCB, "record");
1481     MakeButton(g->buckets, g->form, bucketsCB, "buckets");
1482     MakeWidget(g->meter, g->form, toggleWidgetClass, meterToggleCB,
1483 	       "meterToggle");
1484     MakeButton(g->rescan, g->form, rescanCB, "rescan");
1485     MakeButton(g->quit, g->form, quitCB, "quit");
1486 
1487     /* version label */
1488     MakeLabel(w, g->form, "version");
1489     sprintf(buf, "NAS %d.%d", AuServerProtocolMajorVersion(g->aud),
1490 	    AuServerProtocolMinorVersion(g->aud));
1491     XtVaSetValues(w, XtNlabel, buf, NULL);
1492 
1493     /* left meter */
1494     MakeLabel(w, g->form, "leftMeterLabel");
1495     MakeWidget(g->leftMeter, g->form, scrollbarWidgetClass, NULL, "leftMeter");
1496 
1497     /* right meter */
1498     MakeLabel(w, g->form, "rightMeterLabel");
1499     MakeWidget(g->rightMeter, g->form, scrollbarWidgetClass, NULL,
1500 	       "rightMeter");
1501 
1502     /* volume label */
1503     MakeLabel(g->volumeLabel, g->form, "volumeLabel");
1504     sprintf(buf, VOLUME_FORMAT, g->volume);
1505     XtVaSetValues(g->volumeLabel, XtNlabel, buf, NULL);
1506 
1507     /* volume slider */
1508     MakeWidget(g->volumeBar, g->form, scrollbarWidgetClass, NULL, "volume");
1509     XawScrollbarSetThumb(g->volumeBar,
1510 			 ((float) DEFAULT_VOLUME) / MAX_VOLUME, -1.0);
1511     XtAddCallback(g->volumeBar, XtNscrollProc, scrollProcCB, g);
1512     XtAddCallback(g->volumeBar, XtNjumpProc, jumpProcCB, g);
1513 
1514     /* info window */
1515     MakeWidget(g->info, g->form, asciiTextWidgetClass, NULL, "info");
1516 
1517     /* directory */
1518     MakeLabel(w, g->form, "directoryLabel");
1519     MakeWidget(g->directory, g->form, asciiTextWidgetClass, NULL, "directory");
1520     XtVaSetValues(g->directory, XtNstring, dir, NULL);
1521 
1522     /* template */
1523     MakeLabel(w, g->form, "templateLabel");
1524     MakeWidget(g->template, g->form, asciiTextWidgetClass, NULL, "template");
1525 
1526     /* samples window */
1527     MakeWidget(g->viewport, g->form, viewportWidgetClass, NULL, "viewport");
1528     MakeWidget(g->samples, g->viewport, listWidgetClass, samplesCB, "list");
1529 
1530     /* file format menu */
1531     MakePopup(w, g->topLevel, simpleMenuWidgetClass, "fileFormatMenu");
1532 
1533     for (i = 0; i < SoundNumFileFormats; i++)
1534 	MakeWidget(ww, w, smeBSBObjectClass, setFileFormatMenuButton,
1535 		   SoundFileFormatToString(i));
1536 
1537     /* data format menu */
1538     MakePopup(w, g->topLevel, simpleMenuWidgetClass, "dataFormatMenu");
1539 
1540     for (i = 0; i < AuServerNumFormats(g->aud); i++)
1541 	MakeWidget(ww, w, smeBSBObjectClass, setDataFormatMenuButton,
1542 		   AuFormatToString(AuServerFormat(g->aud, i)));
1543 
1544     makeBucketDialog(g);
1545 
1546     if (g->inputDeviceId)
1547     {
1548 	makeRecordDialog(g);
1549 
1550 	if (!(AuDeviceChangableMask(AuServerDevice(g->aud, g->inputDeviceNum)) &
1551 	      AuCompDeviceLineModeMask))
1552 	    XtVaSetValues(g->rec.mode, XtNsensitive, False, NULL);
1553     }
1554     else
1555     {
1556 	g->rec.popShell = (Widget) 0;
1557 	XtVaSetValues(g->record, XtNsensitive, False, NULL);
1558 	XtVaSetValues(g->buf.record, XtNsensitive, False, NULL);
1559     }
1560 
1561 }
1562 
1563 #define SET_WIDTH(_w)							       \
1564 {									       \
1565     XtVaGetValues(_w, XtNx, &x, NULL);					       \
1566     XtVaSetValues(_w, XtNwidth, maxX - x, NULL);			       \
1567 }
1568 
1569 static void
alignWidgets(GlobalDataPtr g)1570 alignWidgets(GlobalDataPtr g)
1571 {
1572     Position        maxX,
1573                     x;
1574     Dimension       width;
1575     BucketDialogDataPtr b = &g->buf;
1576     RecordDialogDataPtr r = &g->rec;
1577     SaveDialogDataPtr s = &g->save;
1578 
1579     /* main window */
1580     XtRealizeWidget(g->topLevel);
1581     XtVaGetValues(XtNameToWidget(g->form, "version"),
1582 		  XtNx, &x, XtNwidth, &width, NULL);
1583     maxX = x + width - 2;		       /* the -2 is a hack since
1584 					        * "version" has no border */
1585 
1586     SET_WIDTH(g->leftMeter);
1587     SET_WIDTH(g->rightMeter);
1588     SET_WIDTH(g->volumeBar);
1589     SET_WIDTH(g->info);
1590     SET_WIDTH(g->directory);
1591     SET_WIDTH(g->template);
1592     SET_WIDTH(g->viewport);
1593 
1594     /* buckets window */
1595     XtRealizeWidget(b->popShell);
1596     XtVaGetValues(b->accessMenuButton, XtNx, &x, XtNwidth, &width, NULL);
1597     maxX = x + width;
1598 
1599     SET_WIDTH(b->viewport);
1600 
1601     if (r->popShell)
1602     {
1603 	/* record window */
1604 	XtRealizeWidget(r->popShell);
1605 	XtVaGetValues(r->readOnly, XtNx, &x, XtNwidth, &width, NULL);
1606 	maxX = x + width;
1607 
1608 	SET_WIDTH(r->file);
1609 	SET_WIDTH(r->comment);
1610 	SET_WIDTH(r->fileFormatMenuButton);
1611 	SET_WIDTH(r->dataFormatMenuButton);
1612 	SET_WIDTH(r->gainBar);
1613 
1614 	XtVaSetValues(r->fileFormatMenuButton, XtNresizable, False, NULL);
1615 	XtVaSetValues(r->dataFormatMenuButton, XtNresizable, False, NULL);
1616     }
1617 
1618     XtRealizeWidget(s->popShell);
1619     XtVaGetValues(s->file, XtNx, &x, XtNwidth, &width, NULL);
1620     maxX = x + width;
1621     SET_WIDTH(s->fileFormatMenuButton);
1622     XtVaSetValues(s->fileFormatMenuButton, XtNresizable, False, NULL);
1623 
1624     XtVaSetValues(g->viewport, XtNresizable, False, NULL);
1625     XtVaSetValues(b->viewport, XtNresizable, False, NULL);
1626 }
1627 
1628 int
main(int argc,char ** argv)1629 main(int argc, char **argv)
1630 {
1631     int             i,
1632                     endian = 1;
1633     char           *audioServerString = NULL,
1634                    *dir = NULL;
1635     GlobalDataRec   globalData,
1636                    *globals;
1637     XtAppContext    appContext;
1638     extern int      AuMonitorFormat;
1639     static XtActionsRec Actions[] = {{"ok", okAction}};
1640 
1641     globals = &globalData;
1642 
1643     globals->topLevel = XtVaAppInitialize(&appContext, APP_CLASS, NULL, ZERO,
1644 					  &argc, argv, defaultResources,
1645 					  NULL, NULL);
1646 
1647     globals->volume = DEFAULT_VOLUME;
1648     globals->numFiles = 0;
1649     globals->dpy = XtDisplay(globals->topLevel);
1650 
1651     while (argc > 1)
1652     {
1653 	argv++;
1654 	argc--;
1655 
1656 	if (!strcmp("-a", *argv) || !strcmp("-audio", *argv))
1657 	{
1658 	    audioServerString = argv[1];
1659 	    argv++;
1660 	    argc--;
1661 	    if (!argc)
1662 	    {
1663 		printf("usage: audemo [ -toolkitoption ...] [-audio AUDIOSERVER] [directory]\n");
1664 		exit(1);
1665 	    }
1666 	}
1667 	else
1668 	    dir = *argv;
1669     }
1670 
1671     if (!dir)
1672 	dir = (char *) getcwd(NULL, 256);
1673 
1674     if (!(globals->aud =
1675 	  AuOpenServer(audioServerString, 0, NULL, 0, NULL, NULL)))
1676 	fatalError("Can't connect to audio server", NULL);
1677 
1678     globals->inputDeviceId = (AuDeviceID) 0;
1679 
1680     for (i = 0; i < AuServerNumDevices(globals->aud); i++)
1681 	if ((AuDeviceKind(AuServerDevice(globals->aud, i)) ==
1682 	     AuComponentKindPhysicalInput))
1683 	{
1684 	    globals->inputDeviceId =
1685 		AuDeviceIdentifier(AuServerDevice(globals->aud, i));
1686 	    globals->inputDeviceNum = i;
1687 	    break;
1688 	}
1689 
1690     XtAppAddActions(appContext, Actions, XtNumber(Actions));
1691 
1692     createWidgets(globals, dir);
1693     rescanCB(NULL, globals, NULL);
1694     alignWidgets(globals);
1695 
1696     AuMonitorFormat = *(char *) &endian ? AuFormatLinearSigned16LSB :
1697 	AuFormatLinearSigned16MSB;
1698 
1699     AuXtAppAddAudioHandler(appContext, globals->aud);
1700     XtAppMainLoop(appContext);
1701     return 0;
1702 }
1703 
1704 #endif						/* XT */
1705 
1706 static void
fatalError(const char * message,const char * arg)1707 fatalError(const char *message, const char *arg)
1708 {
1709     fprintf(stderr, message, arg);
1710     fprintf(stderr, "\n");
1711     exit(1);
1712 }
1713 
1714 static int
sortRoutine(const void * ap,const void * bp)1715 sortRoutine(const void *ap, const void *bp)
1716 {
1717     const char **a = (const char **)ap;
1718     const char **b = (const char **)bp;
1719 
1720     return strcmp(strrchr(*a, '/') + 1, strrchr(*b, '/') + 1);
1721 }
1722 
1723 static char   **
makeFileList(char ** fileNames,int nfiles)1724 makeFileList(char **fileNames, int nfiles)
1725 {
1726     char          **fileList,
1727                    *p;
1728     int             i;
1729 
1730     qsort(fileNames, nfiles, sizeof(char *), sortRoutine);
1731 
1732     if (!(fileList = (char **) malloc(sizeof(char *) * nfiles)))
1733 	fatalError("Can't malloc file list in makeFileList", NULL);
1734 
1735     for (i = 0; i < nfiles; i++)
1736     {
1737       if (p = strrchr(fileNames[i], '/'))
1738         p++;
1739       else
1740         p = fileNames[i];
1741 
1742 	fileList[i] = (char *) strdup(p);
1743 
1744 	if ((p = strrchr(fileList[i], '.')))
1745 	    *p = 0;
1746 
1747 	fileList[i] = (char *) realloc(fileList[i], strlen(fileList[i]) + 1);
1748 
1749 	if (!fileList[i])
1750 	    fatalError("Can't realloc file list in makeFileList", NULL);
1751     }
1752 
1753     return fileList;
1754 }
1755 
1756 #ifndef WIN32
1757 static FILE    *
startFind(char * dir,char * template)1758 startFind(char *dir, char *template)
1759 {
1760     char           *cmd,
1761                    *p;
1762     FILE           *fp;
1763     int             first = 1;
1764 
1765     cmd = (char *) malloc(2000);
1766 
1767     sprintf(cmd, "find %s -type f -a \\( ", dir);
1768 
1769     p = strtok(template, " ");
1770 
1771     while (p)
1772     {
1773 	if (!first)
1774 	    strcat(cmd, " -o -name '");
1775 	else
1776 	{
1777 	    strcat(cmd, " -name '");
1778 	    first = 0;
1779 	}
1780 
1781 	strcat(cmd, p);
1782 	strcat(cmd, "'");
1783 
1784 	p = strtok(NULL, " ");
1785     }
1786 
1787     strcat(cmd, " \\) -print");
1788 
1789     fp = popen(cmd, "r");
1790     free(cmd);
1791     return fp;
1792 }
1793 #endif /* !WIN32 */
1794 
1795 static int
getFileNames(char * dir,char *** fileNames,char * template)1796 getFileNames(char *dir, char ***fileNames, char *template)
1797 {
1798 #ifndef WIN32
1799     int             files;
1800     FILE           *fp;
1801     char            line[BUF_SIZE];
1802 
1803     /* prime the realloc pump */
1804     *fileNames = (char **) malloc(1);
1805 
1806     if (!fileNames)
1807 	fatalError("Can't malloc file names in getFileNames", NULL);
1808 
1809     if (!(fp = startFind(dir, template)))
1810     {
1811 	free(*fileNames);
1812 	return 0;
1813     }
1814 
1815     files = 0;
1816 
1817     while (fgets(line, BUF_SIZE, fp))
1818     {
1819 	*fileNames =
1820 	    (char **) realloc(*fileNames, sizeof(char *) * (files + 1));
1821 
1822 	if (!*fileNames)
1823 	    fatalError("Can't realloc file names in getFileNames", NULL);
1824 
1825 	line[strlen(line) - 1] = 0;	       /* zap the trailing newline */
1826 	(*fileNames)[files++] = (char *) strdup(line);
1827     }
1828 
1829     pclose(fp);
1830 
1831     if (!files)
1832 	free(*fileNames);
1833 
1834     return files;
1835 #else /* WIN32 */
1836     WIN32_FIND_DATA fileInfo;
1837     char *d;
1838     int files = 0;
1839     HANDLE h;
1840 
1841     *fileNames = NULL;
1842     if (!(d = (char *) malloc(strlen(dir) + 5)))
1843 	return 0;
1844 
1845     strcpy(d, dir);
1846     strcat(d, "/*.*");
1847     h = FindFirstFile(d, &fileInfo);
1848     free(d);
1849 
1850     if (!h)
1851 	return 0;
1852 
1853     do
1854     {
1855 	if (!(fileInfo.dwFileAttributes &
1856 	      (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM)))
1857 	{
1858 	    char *p = NULL;
1859 
1860 	    if (p = (char *) malloc(strlen(dir) + strlen(fileInfo.cFileName) +
1861 				    2))
1862 	    {
1863 		*fileNames = (char **) realloc(*fileNames, sizeof(char *) *
1864 					       (files + 1));
1865 		strcpy(p, dir);
1866 		strcat(p, "/");
1867 		strcat(p, fileInfo.cFileName);
1868 
1869 		(*fileNames)[files++] = p;
1870 	    }
1871 	}
1872     } while(FindNextFile(h, &fileInfo));
1873 
1874     FindClose(h);
1875     return files;
1876 #endif /* WIN32 */
1877 }
1878 
1879 #ifdef WIN32
1880 #include <sys/timeb.h>
1881 
1882 int
gettimeofday(struct timeval * tv,void * dummy)1883 gettimeofday(struct timeval *tv, void *dummy)
1884 {
1885     struct _timeb tm;
1886     _ftime(&tm);
1887     tv->tv_sec = tm.time;
1888     tv->tv_usec = tm.millitm;
1889     return 0;
1890 }
1891 #endif /* WIN32 */
1892