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