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: @(#)auedit.c,v 1.35 1995/12/06 01:10:29 greg Exp $
28  */
29 
30 #include <inttypes.h>
31 #include <stdio.h>
32 
33 #include "config.h"
34 
35 #if defined(HAVE_STDLIB_H)
36 # include <stdlib.h>
37 #endif
38 
39 #if defined(HAVE_MALLOC_H)
40 # include <malloc.h>
41 #endif
42 
43 
44 #ifndef WIN32
45 #include <unistd.h>
46 #else /* WIN32 */
47 #define access _access
48 #define R_OK 4
49 #endif /* WIN32 */
50 #include <limits.h>				/* for SHRT_MIN and SHRT_MAX */
51 #ifndef SYSV
52 #include <audio/Aos.h>				/* for string and other os
53 						 * stuff */
54 #endif
55 #include <audio/Afuncs.h>			/* for bcopy et. al. */
56 #include <audio/audiolib.h>
57 #include <audio/soundlib.h>
58 #include <X11/Intrinsic.h>
59 #include <X11/StringDefs.h>
60 #include <X11/Shell.h>
61 #include <X11/Xaw/Cardinals.h>
62 #include <audio/Xtutil.h>
63 
64 /* widgets */
65 #include <X11/Xaw/AsciiText.h>
66 #include <X11/Xaw/Form.h>
67 #include <X11/Xaw/Label.h>
68 #include <X11/Xaw/Command.h>
69 #include <X11/Xaw/SimpleMenu.h>
70 #include <X11/Xaw/MenuButton.h>
71 #include <X11/Xaw/SmeBSB.h>
72 #include <X11/Xaw/SmeLine.h>
73 #include <X11/Xaw/Dialog.h>
74 #include <X11/Xaw/Toggle.h>
75 
76 #include <Slider.h>
77 
78 #if XlibSpecificationRelease < 5
79 typedef char   *XPointer;
80 #endif
81 
82 #include "Graph.h"
83 
84 #include "play.xbm"
85 #include "stop.xbm"
86 #include "pause.xbm"
87 
88 #define	APP_CLASS		"Auedit"
89 #define NAS_LITTLE_ENDIAN 		(*(char *) &g->endian == 1)
90 #define NAS_BIG_ENDIAN 		(!NAS_LITTLE_ENDIAN)
91 #define SELECTION_HEADER_SIZE	4
92 #define DEFAULT_FREQUENCY	8000
93 #define ZOOM_SCALE		2
94 #define MALLOC_FAIL		"Malloc failed"
95 
96 #define USAGE "\
97 usage: auedit [-audio audioserver] [filename]"
98 
99 #define min(a, b) ((a) < (b) ? (a) : (b))
100 #define max(a, b) ((a) > (b) ? (a) : (b))
101 
102 #define MakeWidget(_w, _parent, _type, _name)				      \
103     (_w) = XtCreateManagedWidget(_name, _type, _parent, NULL, 0)	      \
104 
105 #define MakeWidgetCB(_w, _parent, _type, _callback, _name)		      \
106 {									      \
107     (_w) = XtCreateManagedWidget(_name, _type, _parent, NULL, 0);	      \
108 									      \
109     if ((void *) (_callback) != NULL)					      \
110 	XtAddCallback(_w, XtNcallback, _callback, g);			      \
111 }
112 
113 #define MakePopup(_w, _parent, _type, _name)				      \
114     (_w) = XtCreatePopupShell(_name, _type, _parent, NULL, 0)		      \
115 
116 #define MakeMenuPopup(_w, _parent, _name)				      \
117     MakePopup(_w, _parent, simpleMenuWidgetClass, _name)
118 
119 #define MakeMenuItemW(_w, _parent, _callback, _label)			      \
120 {									      \
121     _w = XtCreateManagedWidget(_label, smeBSBObjectClass, _parent, NULL, 0);  \
122 									      \
123     if ((void *) (_callback) != NULL)					      \
124 	XtAddCallback(_w, XtNcallback, _callback, g);			      \
125 }
126 
127 #define MakeMenuItem(_parent, _callback, _label)			      \
128 {									      \
129     Widget _ww;								      \
130     MakeMenuItemW(_ww, _parent, _callback, _label);			      \
131 }
132 
133 #define MakeMenuLine(_parent)						      \
134     XtCreateManagedWidget("line", smeLineObjectClass, _parent, NULL, 0)
135 
136 #define MakeMenuButton(_w, _parent, _name)				      \
137     MakeWidget(_w, _parent, menuButtonWidgetClass, _name)
138 
139 #define MakeButton(_w, _parent, _callback, _name)			      \
140     MakeWidgetCB(_w, _parent, commandWidgetClass, _callback, _name)
141 
142 #define MakeLabel(_w, _parent, _name)					      \
143     MakeWidget(_w, _parent, labelWidgetClass, _name)
144 
145 #define Invert(w)							      \
146 {									      \
147     Pixel fg, bg;							      \
148 									      \
149     XtVaGetValues(w, XtNforeground, &fg, XtNbackground, &bg, NULL);	      \
150     XtVaSetValues(w, XtNforeground, bg, XtNbackground, fg, NULL);	      \
151 }
152 
153 #define ReplaceCallback(w, cb)						      \
154 {									      \
155     XtRemoveAllCallbacks(w, XtNcallback);				      \
156     XtAddCallback(w, XtNcallback, cb, g);				      \
157 }
158 
159 #define AddToLinkedList(head, item)					      \
160 {									      \
161     (item)->prev = NULL;						      \
162     (item)->next = head;						      \
163     if (head)								      \
164 	(head)->prev = item;						      \
165     head = item;							      \
166 }
167 
168 #define RemoveFromLinkedList(head, item)				      \
169 {									      \
170     if ((item)->next)							      \
171 	(item)->next->prev = (item)->prev;				      \
172 									      \
173     if ((item)->prev)							      \
174 	(item)->prev->next = (item)->next;				      \
175     else								      \
176 	head = (item)->next;						      \
177 }
178 
179 #define DIALOG(_callback, _title, _label)				      \
180 {									      \
181     if (w)								      \
182     {									      \
183         XtVaSetValues(g->dialog, XtNvalue, "", NULL);			      \
184 	popupDialog(g, g->dialog, _callback, _title, _label);		      \
185 	return;								      \
186     }									      \
187 }
188 
189 #define WARNING(_callback, _label)					      \
190 {									      \
191     if (w)								      \
192     {									      \
193 	popupDialog(g, g->warning, _callback, "Warning", _label);	      \
194 	return;								      \
195     }									      \
196 }
197 
198 #define ERROR(_msg)							      \
199     popupDialog(g, g->error, NULL, "Error", _msg)
200 
201 #define ERRORf(_msg)							      \
202 {									      \
203     ERROR(_msg);							      \
204     return FALSE;							      \
205 }
206 
207 #define ERRORv(_msg)							      \
208 {									      \
209     ERROR(_msg);							      \
210     return;								      \
211 }
212 
213 typedef struct
214 {
215     short          *data;
216     Atom            atom;
217     XContext        context;
218     int             len;
219     void            (*callback) ();
220 }               SelectionRec, *SelectionPtr;
221 
222 #define UNDO_DEL	0
223 #define UNDO_INS	1
224 #define UNDO_REP	2
225 
226 typedef struct
227 {
228     GraphDataType  *data,
229                    *repData;
230     int             insStart,
231                     insLen,
232                     repStart,
233                     repLen,
234                     delStart,
235                     delLen;
236 }               UndoRec, *UndoPtr;
237 
238 typedef struct
239 {
240     Widget          top,
241                     form,
242                     gainSlider,
243                     duration,
244                     mode,
245                     freq;
246     int             inputDeviceNum,
247                     currentMode;
248     AuDeviceID      inputDevice;
249 }               RecordDialogRec, *RecordDialogPtr;
250 
251 typedef struct
252 {
253     Widget          top,
254                     form,
255                     play,
256                     stop,
257                     pause,
258                     record,
259                     fileMenuButton,
260                     editMenuButton,
261                     editPasteInsert,
262                     editPasteReplace,
263                     editPasteMix,
264                     editUndo,
265                     zoomMenuButton,
266                     effectsMenuButton,
267                     volumeSlider,
268                     leftTime,
269                     positionTime,
270                     rightTime,
271                     durationTime,
272                     fileFormatMenuButton,
273                     dataFormatMenuButton,
274                     frequency,
275                     comment,
276                     filename,
277                     dialogShell,
278                     dialog,
279                     errorShell,
280                     error,
281                     warningShell,
282                     warning,
283                     graph,
284                     popdownCause;
285     GraphDataType  *data;
286     AuServer       *aud;
287     int             monitorInc,
288                     numSamples,
289                     numTracks,
290                     sampleRate,
291                     endian;
292     SelectionRec    selection;
293     Display        *dpy;
294     Boolean         modified;
295     void            (*dialogCallback) (),
296                     (*modifiedCallback) ();
297     Atom            wm_delete_window;
298     UndoRec         undo;
299     RecordDialogRec recordDialog;
300 }               GlobalDataRec, *GlobalDataPtr;
301 
302 static String   defaultResources[] =
303 {
304 #include "resources.h"
305     NULL
306 };
307 
308 typedef struct
309 {
310     GlobalDataPtr   g;
311     void            (*callback) ();
312     int             data;
313 }               DonePrivRec, *DonePrivPtr;
314 
315 typedef struct _ElementList
316 {
317     AuFlowID        flow;
318     int             volumeElement;
319     struct _ElementList *prev,
320                    *next;
321 }               ElementListRec, *ElementListPtr;
322 
323 typedef ElementListPtr ElementListId;
324 
325 static ElementListPtr ElementList;
326 static int      ElementCount;
327 static GlobalDataPtr globals;			/* for actions */
328 static Boolean  loadFile(), saveFile();
329 static void     popupDialog(), clearUndo(), setUndo();
330 
331 static void
fatalError(message,arg)332 fatalError(message, arg)
333 char           *message,
334                *arg;
335 {
336     fprintf(stderr, message, arg);
337     fprintf(stderr, "\n");
338     exit(1);
339 }
340 
341 static void
RemoveFromElementList(p)342 RemoveFromElementList(p)
343 ElementListPtr  p;
344 {
345     RemoveFromLinkedList(ElementList, p);
346     ElementCount--;
347     free(p);
348 }
349 
350 static          ElementListId
AddToElementList(flow,volumeElement)351 AddToElementList(flow, volumeElement)
352 AuFlowID        flow;
353 int             volumeElement;
354 {
355     ElementListPtr  p;
356 
357     if (!(p = (ElementListPtr) malloc(sizeof(ElementListRec))))
358 	fatalError("malloc error in AddToElementList");
359 
360     p->flow = flow;
361     p->volumeElement = volumeElement;
362 
363     AddToLinkedList(ElementList, p);
364     ElementCount++;
365 
366     return (ElementListId) p;
367 }
368 
369 static void
setTime(g,w,p)370 setTime(g, w, p)
371 GlobalDataPtr   g;
372 Widget          w;
373 int             p;
374 {
375     char            buf[20];
376     float           t;
377     int             m;
378 
379     t = (float) p / g->sampleRate;
380     m = t / 60;
381     t -= m * 60;
382     sprintf(buf, "%02d:%05.2f", m, t);
383     XtVaSetValues(w, XtNlabel, buf, NULL);
384 }
385 
386 static void
leftMarker(w,gp,pp)387 leftMarker(w, gp, pp)
388 Widget          w;
389 XtPointer       gp;
390 XtPointer       pp;
391 {
392     GlobalDataPtr   g = (GlobalDataPtr) gp;
393     intptr_t        p = (intptr_t) pp;
394     int             n;
395 
396     XtVaGetValues(g->graph, XtNrightMarker, &n, NULL);
397 
398     setTime(g, g->leftTime, p);
399     setTime(g, g->durationTime, n - p);
400 }
401 
402 static void
rightMarker(w,gp,pp)403 rightMarker(w, gp, pp)
404 Widget          w;
405 XtPointer       gp;
406 XtPointer       pp;
407 {
408     GlobalDataPtr   g = (GlobalDataPtr) gp;
409     intptr_t        p = (intptr_t) pp;
410     int             n;
411 
412     XtVaGetValues(g->graph, XtNleftMarker, &n, NULL);
413 
414     setTime(g, g->rightTime, p);
415     setTime(g, g->durationTime, p - n);
416 }
417 
418 static void
handleMonitorEvent(aud,handler,ev,g)419 handleMonitorEvent(aud, handler, ev, g)
420 AuServer       *aud;
421 AuEventHandlerRec *handler;
422 AuEvent        *ev;
423 GlobalDataPtr   g;
424 {
425     int             current,
426                     end;
427 
428     XtVaGetValues(g->graph, XtNposition, &current, XtNrightMarker, &end, NULL);
429     current += g->monitorInc;
430 
431     if (current >= end)
432 	current = end - 1;
433 
434     GraphSetPosition(g->graph, current);
435     setTime(g, g->positionTime, current);
436 }
437 
438 static void
done(aud,handler,ev,datap)439 done(aud, handler, ev, datap)
440 AuServer       *aud;
441 AuEvent        *ev;
442 AuEventHandlerRec *handler;
443 XtPointer       datap;
444 {
445     DonePrivPtr     data = (DonePrivPtr) datap;
446 
447     if (ev->type == AuEventTypeMonitorNotify)
448 	handleMonitorEvent(aud, handler, ev, data->g);
449     else
450 	(*data->callback) (NULL, data->g, data->data);
451 }
452 
453 static void
adjustGain(w,gp,gainp)454 adjustGain(w, gp, gainp)
455 Widget          w;
456 XtPointer       gp;
457 XtPointer       gainp;
458 {
459     GlobalDataPtr   g = (GlobalDataPtr) gp;
460     intptr_t        gain = (intptr_t) gainp;
461     AuDeviceAttributes da;
462 
463     if (!AuDeviceChangableMask(AuServerDevice(g->aud,
464 					  g->recordDialog.inputDeviceNum)) &
465 	AuCompDeviceGainMask)
466 	return;
467 
468     AuDeviceGain(&da) = AuFixedPointFromSum(gain, 0);
469     AuSetDeviceAttributes(g->aud, g->recordDialog.inputDevice,
470 			  AuCompDeviceGainMask, &da, NULL);
471 }
472 
473 static void
recordStart(w,gp,call_data)474 recordStart(w, gp, call_data)
475 Widget          w;
476 XtPointer       gp;
477 XtPointer       call_data;
478 {
479     GlobalDataPtr   g = (GlobalDataPtr) gp;
480     static DonePrivRec priv;
481     static AuFlowID flow;
482     RecordDialogPtr r = &g->recordDialog;
483     String          st;
484     Sound           s;
485     int             duration,
486                     freq,
487                     gain;
488     char            buf[20];
489     static AuBool   recording;
490 
491     if (!recording)
492     {
493 	if (w != XtNameToWidget(g->recordDialog.form, "recordStart"))
494 	    return;
495 
496 	XtVaGetValues(r->duration, XtNstring, &st, NULL);
497 	duration = atoi(st);
498 	XtVaGetValues(r->freq, XtNstring, &st, NULL);
499 	freq = atoi(st);
500 
501 	if (!duration || !freq)
502 	    return;
503 
504 	if (g->data)
505 	    free(g->data);
506 
507 	g->numSamples = duration * freq;
508 	g->numTracks =
509 	    AuDeviceNumTracks(AuServerDevice(g->aud, r->inputDeviceNum));
510 
511 	if (!(g->data = (GraphDataType *)
512 	   calloc(1, sizeof(GraphDataType) * g->numTracks * g->numSamples)))
513 	    return;
514 
515 	g->sampleRate = freq;
516 	sprintf(buf, "%d", g->sampleRate);
517 	XtVaSetValues(g->frequency, XtNstring, buf, NULL);
518 
519 	s = SoundCreate(SoundFileFormatNone,
520 			NAS_LITTLE_ENDIAN ? AuFormatLinearSigned16LSB :
521 			AuFormatLinearSigned16MSB, g->numTracks,
522 			freq, g->numSamples, NULL);
523 
524 	priv.g = g;
525 	priv.callback = recordStart;
526 
527 	XtVaGetValues(r->gainSlider, XtNvalue, &gain, NULL);
528 
529 	AuSoundRecordToData(g->aud, s, g->data, r->inputDevice,
530 			    AuFixedPointFromSum(gain, 0),
531 			    done, &priv, r->currentMode, &flow, NULL, NULL);
532 	SoundDestroy(s);
533 	Invert(w);
534 	recording = AuTrue;
535     }
536     else if (w)
537 	AuStopFlow(g->aud, flow, NULL);
538     else
539     {
540 	XtVaSetValues(g->graph, XtNdata, g->data,
541 		      XtNnumSamples, g->numSamples,
542 		      XtNnumTracks, g->numTracks,
543 		      XtNstart, 0, XtNend, g->numSamples,
544 		      XtNleftMarker, 0, XtNrightMarker, g->numSamples,
545 		      NULL);
546 	GraphRedraw(g->graph);
547 	g->modified = TRUE;
548 
549 	XtVaSetValues(g->filename, XtNlabel, "new", NULL);
550 	XtVaSetValues(g->comment, XtNstring, "", NULL);
551 	clearUndo(g);
552 	Invert(XtNameToWidget(g->recordDialog.form, "recordStart"));
553 	recording = AuFalse;
554     }
555 }
556 
557 static void
recordMonitor(w,gp,call_data)558 recordMonitor(w, gp, call_data)
559 Widget          w;
560 XtPointer       gp;
561 XtPointer       call_data;
562 {
563     GlobalDataPtr   g = (GlobalDataPtr) gp;
564     RecordDialogPtr r = &g->recordDialog;
565     static Boolean  monitoring;
566     static AuFlowID flow;
567     static DonePrivRec priv;
568     String          st;
569     int             freq;
570 
571     if (monitoring)
572     {
573 	if (w)
574 	    /* user requested stop */
575 	    AuStopFlow(g->aud, flow, NULL);
576 	else
577 	{
578 	    /* got a done callback */
579 	    monitoring = False;
580 	    Invert(XtNameToWidget(r->form, "monitor"));
581 	}
582 
583 	return;
584     }
585 
586     if (w != XtNameToWidget(r->form, "monitor"))
587 	return;
588 
589     XtVaGetValues(r->freq, XtNstring, &st, NULL);
590     freq = atoi(st);
591 
592     priv.g = g;
593     priv.callback = recordMonitor;
594 
595     if (AuMonitorDevice(g->aud, freq, r->inputDevice, AuNone,
596 			AuFixedPointFromSum(1, 0),
597 			done, &priv, &flow, NULL, NULL, NULL))
598     {
599 	monitoring = True;
600 	Invert(w);
601     }
602 }
603 
604 static void
recordDismiss(w,gp,call_data)605 recordDismiss(w, gp, call_data)
606 Widget          w;
607 XtPointer       gp;
608 XtPointer       call_data;
609 {
610     GlobalDataPtr   g = (GlobalDataPtr) gp;
611 
612     recordMonitor((Widget) 1, g, 0);
613     recordStart((Widget) 1, g, 0);
614 
615     XtPopdown(g->recordDialog.top);
616     XtSetSensitive(g->record, True);
617 }
618 
619 static void
recordMode(w,gp,call_data)620 recordMode(w, gp, call_data)
621 Widget          w;
622 XtPointer       gp;
623 XtPointer       call_data;
624 {
625     GlobalDataPtr   g = (GlobalDataPtr) gp;
626     AuDeviceAttributes da;
627     RecordDialogPtr r = &g->recordDialog;
628 
629     XtCallActionProc(w, "reset", NULL, NULL, 0);
630 
631     if (r->currentMode == AuDeviceLineModeLow)
632     {
633 	XtVaSetValues(r->mode, XtNlabel, "Mic", NULL);
634 	r->currentMode = AuDeviceLineModeHigh;
635     }
636     else
637     {
638 	XtVaSetValues(r->mode, XtNlabel, "Line", NULL);
639 	r->currentMode = AuDeviceLineModeLow;
640     }
641 
642     AuDeviceLineMode(&da) = r->currentMode;
643     AuSetDeviceAttributes(g->aud, r->inputDevice, AuCompDeviceLineModeMask,
644 			  &da, NULL);
645 }
646 
647 #define PLAY_PLAY	0
648 #define PLAY_STOP	1
649 #define PLAY_PAUSE	2
650 
651 static void
setPlaySensitive(g,v)652 setPlaySensitive(g, v)
653 GlobalDataPtr   g;
654 Boolean         v;
655 {
656     XtSetSensitive(g->frequency, v);
657     XtSetSensitive(g->graph, v);
658     XtSetSensitive(g->play, v);
659     XtSetSensitive(g->fileMenuButton, v);
660     XtSetSensitive(g->editMenuButton, v);
661     XtSetSensitive(g->zoomMenuButton, v);
662     XtSetSensitive(g->effectsMenuButton, v);
663 
664     if (g->recordDialog.inputDevice)
665 	XtSetSensitive(g->record, v);
666 }
667 
668 static void
doPlay(w,g,mode)669 doPlay(w, g, mode)
670 Widget          w;
671 GlobalDataPtr   g;
672 int             mode;
673 {
674     static AuFlowID flow;
675     static DonePrivRec priv;
676     static AuBool   paused,
677                     playing,
678                     userStop;
679     static ElementListId listid;
680     extern int      AuMonitorRate;
681 
682     switch (mode)
683     {
684 	case PLAY_PLAY:
685 	    {
686 		int             start,
687 		                end,
688 		                multiplier,
689 		                monitor,
690 		                vol;
691 		Sound           s;
692 
693 		priv.g = g;
694 		priv.callback = doPlay;
695 		priv.data = PLAY_STOP;
696 		userStop = AuFalse;
697 
698 		XtVaGetValues(g->graph, XtNleftMarker, &start,
699 			      XtNrightMarker, &end, NULL);
700 
701 		GraphSetPosition(g->graph, start);
702 		setTime(g, g->positionTime, start);
703 
704 		s = SoundCreate(SoundFileFormatNone,
705 				NAS_LITTLE_ENDIAN ? AuFormatLinearSigned16LSB :
706 				AuFormatLinearSigned16MSB,
707 			    g->numTracks, g->sampleRate, end - start, NULL);
708 
709 		g->monitorInc = g->sampleRate / AuMonitorRate;
710 
711 		XtVaGetValues(g->volumeSlider, XtNvalue, &vol, NULL);
712 
713 		if (AuSoundPlayFromData(g->aud, s,
714 					g->data + start * g->numTracks,
715 					AuNone,
716 					AuFixedPointFromFraction(vol, 100),
717 					done, &priv, &flow,
718 					&multiplier, &monitor, NULL))
719 		{
720 		    listid = AddToElementList(flow, multiplier);
721 		    setPlaySensitive(g, FALSE);
722 		    Invert(g->play);
723 		    playing = AuTrue;
724 		}
725 
726 		SoundDestroy(s);
727 		break;
728 	    }
729 	case PLAY_STOP:
730 	    if (!playing)
731 		break;
732 
733 	    if (w)
734 	    {
735 		userStop = AuTrue;
736 		AuStopFlow(g->aud, flow, NULL);/* user requested stop */
737 	    }
738 	    else
739 	    {
740 		if (!userStop)
741 		    /* fake a monitor event to bump the position to the end */
742 		    handleMonitorEvent(g->aud, (AuEventHandlerRec *) 0,
743 				       (AuEvent *) 0, g);
744 
745 		/* got a done callback */
746 		RemoveFromElementList(listid);
747 		setPlaySensitive(g, TRUE);
748 		Invert(g->play);
749 
750 		if (paused)
751 		    Invert(g->pause);
752 
753 		playing = paused = AuFalse;
754 	    }
755 	    break;
756 	case PLAY_PAUSE:
757 	    if (!playing)
758 		break;
759 
760 	    if (paused)
761 		AuStartFlow(g->aud, flow, NULL);
762 	    else
763 		AuPauseFlow(g->aud, flow, NULL);
764 
765 	    Invert(g->pause);
766 	    paused = !paused;
767 	    break;
768     }
769 }
770 
771 static void
stop(w,gp,call_data)772 stop(w, gp, call_data)
773 Widget          w;
774 XtPointer       gp;
775 XtPointer       call_data;
776 {
777     GlobalDataPtr   g = (GlobalDataPtr) gp;
778 
779     doPlay(w, g, PLAY_STOP);
780 }
781 
782 static void
Pause(w,gp,call_data)783 Pause(w, gp, call_data)
784 Widget          w;
785 XtPointer       gp;
786 XtPointer       call_data;
787 {
788     GlobalDataPtr   g = (GlobalDataPtr) gp;
789 
790     doPlay(w, g, PLAY_PAUSE);
791 }
792 
793 static void
play(w,gp,call_data)794 play(w, gp, call_data)
795 Widget          w;
796 XtPointer       gp;
797 XtPointer       call_data;
798 {
799     GlobalDataPtr   g = (GlobalDataPtr) gp;
800 
801     doPlay(w, g, PLAY_PLAY);
802 }
803 
804 #define MODIFIED(_x)							      \
805 {									      \
806     if (g->modified)							      \
807     {									      \
808 	g->modifiedCallback = _x;					      \
809 	checkModified(w, g);						      \
810 	return;								      \
811     }									      \
812 }
813 
814 static void
checkModified(w,gp,call_data)815 checkModified(w, gp, call_data)
816 Widget          w;
817 XtPointer       gp;
818 XtPointer       call_data;
819 {
820     GlobalDataPtr   g = (GlobalDataPtr) gp;
821 
822     WARNING(checkModified, "File has been modified - Are you sure?");
823 
824     g->modified = FALSE;
825     (*g->modifiedCallback) ((Widget) 1, g);
826 }
827 
828 static void
record(w,gp,call_data)829 record(w, gp, call_data)
830 Widget          w;
831 XtPointer       gp;
832 XtPointer       call_data;
833 {
834     GlobalDataPtr   g = (GlobalDataPtr) gp;
835     MODIFIED(record);
836 
837     XtSetSensitive(g->record, False);
838     XtPopup(g->recordDialog.top, XtGrabExclusive);
839 }
840 
841 static void
fileExit(w,gp,call_data)842 fileExit(w, gp, call_data)
843 Widget          w;
844 XtPointer       gp;
845 XtPointer       call_data;
846 {
847     GlobalDataPtr   g = (GlobalDataPtr) gp;
848     MODIFIED(fileExit);
849     exit(0);
850 }
851 
852 static void
fileRevert(w,gp,call_data)853 fileRevert(w, gp, call_data)
854 Widget          w;
855 XtPointer       gp;
856 XtPointer       call_data;
857 {
858     GlobalDataPtr   g = (GlobalDataPtr) gp;
859     String          name;
860 
861     MODIFIED(fileRevert);
862     XtVaGetValues(g->filename, XtNlabel, &name, NULL);
863     loadFile(g, name);
864 }
865 
866 static void
newFile(g,numTracks)867 newFile(g, numTracks)
868 GlobalDataPtr   g;
869 int             numTracks;
870 {
871     g->numSamples = 1;
872     g->numTracks = numTracks;
873 
874     if (!(g->data =
875 	  (GraphDataType *) malloc(sizeof(GraphDataType) * numTracks)))
876 	fatalError("malloc error in newFile");
877 
878     *g->data = 0;
879 
880     XtVaSetValues(g->graph, XtNdata, g->data,
881 		  XtNnumSamples, g->numSamples,
882 		  XtNnumTracks, g->numTracks,
883 		  XtNstart, 0, XtNend, g->numSamples,
884 		  XtNleftMarker, 0, XtNrightMarker, g->numSamples,
885 		  NULL);
886 
887     XtVaSetValues(g->filename, XtNlabel, "new", NULL);
888     XtVaSetValues(g->comment, XtNstring, "", NULL);
889     clearUndo(g);
890 }
891 
892 static void
fileNew(w,gp,call_data)893 fileNew(w, gp, call_data)
894 Widget          w;
895 XtPointer       gp;
896 XtPointer       call_data;
897 {
898     GlobalDataPtr   g = (GlobalDataPtr) gp;
899     int             numTracks;
900 
901     MODIFIED(fileNew);
902     DIALOG(fileNew, "New File", "Number of tracks:");
903 
904     numTracks = atoi(XawDialogGetValueString(g->dialog));
905 
906     if (numTracks < 1 || numTracks > AuServerMaxTracks(g->aud))
907     {
908 	ERROR("Illegal number of tracks");
909 	numTracks = 1;
910     }
911 
912     newFile(g, numTracks);
913 }
914 
915 static Boolean
noFile(g)916 noFile(g)
917 GlobalDataPtr   g;
918 {
919     char            buf[20];
920 
921     g->sampleRate = DEFAULT_FREQUENCY;
922     sprintf(buf, "%d", g->sampleRate);
923     XtVaSetValues(g->frequency, XtNstring, buf, NULL);
924     newFile(g, 1);
925     return FALSE;
926 }
927 
928 static void
checkSelection(w,gp,call_data)929 checkSelection(w, gp, call_data)
930 Widget          w;
931 XtPointer       gp;
932 XtPointer       call_data;
933 {
934     GlobalDataPtr   g = (GlobalDataPtr) gp;
935     Boolean         canPaste;
936 
937     canPaste = XGetSelectionOwner(g->dpy, g->selection.atom) != None;
938 
939     XtSetSensitive(g->editPasteInsert, canPaste);
940     XtSetSensitive(g->editPasteReplace, canPaste);
941     XtSetSensitive(g->editPasteMix, canPaste);
942 }
943 
944 static void
placePopup(w,gp,call_data)945 placePopup(w, gp, call_data)
946 Widget          w;
947 XtPointer       gp;
948 XtPointer       call_data;
949 {
950     GlobalDataPtr   g = (GlobalDataPtr) gp;
951     Dimension       width,
952                     height;
953     Position        x,
954                     y,
955                     oldX,
956                     oldY;
957 
958     XtVaGetValues(g->form, XtNwidth, &width, XtNheight, &height, NULL);
959     XtTranslateCoords(g->form, width / 2, height / 2, &x, &y);
960     XtVaGetValues(w, XtNwidth, &width, XtNheight, &height, NULL);
961     x -= width / 2;
962     y -= height / 2;
963     XtVaGetValues(w, XtNx, &oldX, XtNy, &oldY, NULL);
964 
965     if (x != oldX || y != oldY)
966 	XtVaSetValues(w, XtNx, x, XtNy, y, NULL);
967 }
968 
969 typedef struct
970 {
971     unsigned char  *p;
972     int             len;
973 }               SelRec, *SelPtr;
974 
975 static Boolean
selectionSend(w,selection,target,type,value,length,format,maxLength,gp,id)976 selectionSend(w, selection, target, type, value, length, format, maxLength,
977 	      gp, id)
978 Widget          w;
979 Atom           *selection;
980 Atom           *target;
981 Atom           *type;
982 XtPointer      *value;
983 AuUint32       *length;
984 int            *format;
985 AuUint32       *maxLength;
986 XtPointer       gp;
987 XtRequestId    *id;
988 {
989     GlobalDataPtr   g = (GlobalDataPtr) gp;
990     SelPtr          con;
991     int             len;
992     XID             xid = (XID) * id;
993 
994     if (*selection != g->selection.atom || *target != g->selection.atom)
995 	return FALSE;
996 
997     if (XFindContext(g->dpy, xid, g->selection.context, (XPointer *) & con))
998     {					       /* first call */
999 	if (!(con = (SelPtr) malloc(sizeof(SelRec))))
1000 	    return FALSE;
1001 
1002 	con->len = g->selection.len;
1003 	con->p = (unsigned char *) g->selection.data;
1004     }
1005 
1006     len = min(*maxLength, con->len);
1007 
1008     if (!len)				       /* out of data */
1009     {
1010 	free(con);
1011 	XDeleteContext(g->dpy, xid, g->selection.context);
1012 
1013 	*value = (XtPointer) XtMalloc(1);
1014 	*length = 0;
1015 	return TRUE;
1016     }
1017 
1018     if (!(*value = (XtPointer) XtMalloc(len)))
1019 	return FALSE;
1020 
1021     bcopy(con->p, *value, len);
1022 
1023     con->len -= len;
1024     con->p += len;
1025     XSaveContext(g->dpy, xid, g->selection.context, (XPointer) con);
1026 
1027     *type = g->selection.atom;
1028     *format = 16;
1029     *length = len >> 1;
1030     return TRUE;
1031 }
1032 
1033 static short   *
convertRate(data,fromRate,toRate,numSamples,numTracks)1034 convertRate(data, fromRate, toRate, numSamples, numTracks)
1035 short          *data;
1036 int             fromRate,
1037                 toRate,
1038                *numSamples,
1039                 numTracks;
1040 {
1041     int             n,
1042                     len,
1043                     phase;
1044     short          *new,
1045                    *last;
1046     unsigned char  *s,
1047                    *d;
1048 
1049     n = (float) toRate / fromRate * *numSamples;
1050 
1051     if (!(new = (short *) XtMalloc(n * sizeof(short) * numTracks)))
1052 	return NULL;
1053 
1054     len = sizeof(short) * numTracks;
1055 
1056     if (!(last = (short *) XtMalloc(len)))
1057     {
1058 	XtFree((char *) new);
1059 	return NULL;
1060     }
1061 
1062     *numSamples = n;
1063     phase = 0;
1064     s = (unsigned char *) data;
1065     d = (unsigned char *) new;
1066 
1067     while (n--)
1068     {
1069 	while (phase >= 0)
1070 	{
1071 	    phase -= toRate;
1072 	    bcopy(s, last, len);
1073 	    s += len;
1074 	}
1075 
1076 	phase += fromRate;
1077 	bcopy(last, d, len);
1078 	d += len;
1079     }
1080 
1081     XtFree((char *) last);
1082     XtFree((char *) data);
1083     return new;
1084 }
1085 
1086 static void
selectionReceive(w,gp,selection,type,value,length,format)1087 selectionReceive(w, gp, selection, type, value, length, format)
1088 Widget          w;
1089 XtPointer       gp;
1090 Atom           *selection;
1091 Atom           *type;
1092 XtPointer       value;
1093 AuUint32       *length;
1094 int            *format;
1095 {
1096     GlobalDataPtr   g = (GlobalDataPtr) gp;
1097     unsigned short *p;
1098     short          *data;
1099 
1100     p = (unsigned short *) value;
1101     data = (short *) p + SELECTION_HEADER_SIZE;
1102 
1103     if (g->selection.callback)
1104 	if (p[0] != g->numTracks)
1105 	    ERROR("Selection has a different number of tracks");
1106 	else
1107 	{
1108 	    int             numSamples;
1109 
1110 	    /*
1111 	     * due to a bug in Xt we can't believe the length parameter.  So
1112 	     * the actual data length is included in the data.
1113 	     */
1114 	    numSamples = (((p[2] << 16) + p[3]) / sizeof(short) -
1115 			  SELECTION_HEADER_SIZE) / g->numTracks;
1116 
1117 	    if (p[1] != g->sampleRate)
1118 		if (!(data = convertRate(data, p[1], g->sampleRate,
1119 					 &numSamples, g->numTracks)))
1120 		    ERROR(MALLOC_FAIL);
1121 
1122 	    if (data)
1123 	    {
1124 		Boolean         zoomOut = g->numSamples == 1;
1125 
1126 		(*g->selection.callback) (g, data, numSamples);
1127 		g->modified = TRUE;
1128 
1129 		if (zoomOut)
1130 		    XtVaSetValues(g->graph, XtNstart, 0, XtNleftMarker, 0,
1131 				  XtNend, g->numSamples, XtNrightMarker,
1132 				  g->numSamples, NULL);
1133 	    }
1134 	}
1135 
1136     XtSetSensitive(g->form, TRUE);
1137     XtFree(value);
1138 }
1139 
1140 static void
pasteInsert(g,data,numSamples)1141 pasteInsert(g, data, numSamples)
1142 GlobalDataPtr   g;
1143 GraphDataType  *data;
1144 int             numSamples;
1145 {
1146     GraphDataType  *new;
1147     unsigned char  *p;
1148     int             left,
1149                     numBytes,
1150                     bytesPerSample = sizeof(GraphDataType) * g->numTracks;
1151 
1152     if (!(new = (GraphDataType *)
1153 	  realloc(g->data, (g->numSamples + numSamples) * bytesPerSample)))
1154 	ERRORv(MALLOC_FAIL);
1155 
1156     XtVaGetValues(g->graph, XtNleftMarker, &left, NULL);
1157 
1158     p = (unsigned char *) (new + left * g->numTracks);
1159     numBytes = numSamples * bytesPerSample;
1160     bcopy(p, p + numBytes, (g->numSamples - left) * bytesPerSample);
1161     bcopy(data, p, numBytes);
1162 
1163     g->data = new;
1164     g->numSamples += numSamples;
1165 
1166     XtVaSetValues(g->graph, XtNdata, g->data, XtNnumSamples, g->numSamples,
1167 		  NULL);
1168     clearUndo(g);
1169     setUndo(g, left, left + numSamples, UNDO_INS);
1170 }
1171 
1172 static void
pasteReplace(g,data,numSamples)1173 pasteReplace(g, data, numSamples)
1174 GlobalDataPtr   g;
1175 GraphDataType  *data;
1176 int             numSamples;
1177 {
1178     int             left,
1179                     n,
1180                     bytesPerSample = sizeof(GraphDataType) * g->numTracks;
1181     Boolean         newValues = FALSE;
1182 
1183     XtVaGetValues(g->graph, XtNleftMarker, &left, NULL);
1184     clearUndo(g);
1185 
1186     /* see if we need to insert any */
1187     n = g->numSamples - left;
1188 
1189     if (n < numSamples)
1190     {
1191 	GraphDataType  *new;
1192 	int             needed = numSamples - n;
1193 
1194 	if (!(new = (GraphDataType *)
1195 	      realloc(g->data, (g->numSamples + needed) * bytesPerSample)))
1196 	    ERRORv(MALLOC_FAIL);
1197 
1198 	setUndo(g, g->numSamples, g->numSamples + needed, UNDO_INS);
1199 
1200 	g->data = new;
1201 	g->numSamples += needed;
1202 	newValues = TRUE;
1203     }
1204 
1205     setUndo(g, left, left + numSamples, UNDO_REP);
1206     bcopy(data, g->data + left * g->numTracks, numSamples * bytesPerSample);
1207 
1208     if (newValues)
1209 	XtVaSetValues(g->graph, XtNdata, g->data, XtNnumSamples, g->numSamples,
1210 		      NULL);
1211     else
1212 	GraphRedraw(g->graph);
1213 }
1214 
1215 static void
pasteMix(g,data,numSamples)1216 pasteMix(g, data, numSamples)
1217 GlobalDataPtr   g;
1218 GraphDataType  *data;
1219 int             numSamples;
1220 {
1221     GraphDataType  *p;
1222     int             left,
1223                     n,
1224                     bytesPerSample = sizeof(GraphDataType) * g->numTracks;
1225     Boolean         newValues = FALSE;
1226 
1227     XtVaGetValues(g->graph, XtNleftMarker, &left, NULL);
1228     clearUndo(g);
1229 
1230     /* see if we need to insert any */
1231     n = g->numSamples - left;
1232 
1233     if (n < numSamples)
1234     {
1235 	GraphDataType  *new;
1236 	int             needed = numSamples - n;
1237 
1238 	if (!(new = (GraphDataType *)
1239 	      realloc(g->data, (g->numSamples + needed) * bytesPerSample)))
1240 	    ERRORv(MALLOC_FAIL);
1241 
1242 	setUndo(g, g->numSamples, g->numSamples + needed, UNDO_INS);
1243 	bzero(new + g->numSamples * g->numTracks, needed * bytesPerSample);
1244 
1245 	g->data = new;
1246 	g->numSamples += needed;
1247 	newValues = TRUE;
1248     }
1249 
1250     setUndo(g, left, left + numSamples, UNDO_REP);
1251     p = g->data + left * g->numTracks;
1252 
1253     while (numSamples--)
1254 	*p++ = (*p + *data++) / 2;
1255 
1256     if (newValues)
1257 	XtVaSetValues(g->graph, XtNdata, g->data, XtNnumSamples, g->numSamples,
1258 		      NULL);
1259     else
1260 	GraphRedraw(g->graph);
1261 }
1262 
1263 static void
editPaste(w,gp,call_data)1264 editPaste(w, gp, call_data)
1265 Widget          w;
1266 XtPointer       gp;
1267 XtPointer       call_data;
1268 {
1269     GlobalDataPtr   g = (GlobalDataPtr) gp;
1270 
1271     if (w == g->editPasteInsert)
1272 	g->selection.callback = pasteInsert;
1273     else if (w == g->editPasteReplace)
1274 	g->selection.callback = pasteReplace;
1275     else if (w == g->editPasteMix)
1276 	g->selection.callback = pasteMix;
1277     else
1278 	g->selection.callback = NULL;
1279 
1280     XtSetSensitive(g->form, FALSE);
1281     XtGetSelectionValue(g->top, g->selection.atom, g->selection.atom,
1282 		     selectionReceive, g, XtLastTimestampProcessed(g->dpy));
1283 }
1284 
1285 static Boolean
copyToSelection(g)1286 copyToSelection(g)
1287 GlobalDataPtr   g;
1288 {
1289     int             start,
1290                     end;
1291 
1292     if (g->selection.data)
1293     {
1294 	free(g->selection.data);
1295 	g->selection.data = NULL;
1296     }
1297 
1298     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1299     g->selection.len = (end - start) * sizeof(GraphDataType) * g->numTracks +
1300 	SELECTION_HEADER_SIZE * sizeof(short);
1301 
1302     if (!(g->selection.data = (short *) malloc(g->selection.len)))
1303 	ERRORf(MALLOC_FAIL);
1304 
1305     g->selection.data[0] = g->numTracks;
1306     g->selection.data[1] = g->sampleRate;
1307     g->selection.data[2] = g->selection.len >> 16;
1308     g->selection.data[3] = g->selection.len & 0xffff;
1309 
1310     bcopy(g->data + start * g->numTracks,
1311 	  g->selection.data + SELECTION_HEADER_SIZE, g->selection.len -
1312 	  SELECTION_HEADER_SIZE * sizeof(short));
1313 
1314     if (!XtOwnSelectionIncremental(g->top, g->selection.atom,
1315 				   XtLastTimestampProcessed(g->dpy),
1316 				   selectionSend, NULL, NULL, NULL, g))
1317     {
1318 	free(g->selection.data);
1319 	g->selection.data = NULL;
1320 	ERRORf("Couldn't get selection");
1321     }
1322 
1323     return TRUE;
1324 }
1325 
1326 static void
clearUndo(g)1327 clearUndo(g)
1328 GlobalDataPtr   g;
1329 {
1330     if (g->undo.data)
1331 	free(g->undo.data);
1332 
1333     if (g->undo.repData)
1334 	free(g->undo.repData);
1335 
1336     bzero(&g->undo, sizeof(UndoRec));
1337     XtSetSensitive(g->editUndo, FALSE);
1338 }
1339 
1340 static void
setUndo(g,start,end,type)1341 setUndo(g, start, end, type)
1342 GlobalDataPtr   g;
1343 int             start,
1344                 end,
1345                 type;
1346 {
1347     int             len;
1348 
1349     len = (end - start) * sizeof(GraphDataType) * g->numTracks;
1350 
1351     switch (type)
1352     {
1353 	case UNDO_DEL:
1354 	    g->undo.delLen = len;
1355 
1356 	    if (!(g->undo.data = (GraphDataType *) malloc(g->undo.delLen)))
1357 		ERRORv(MALLOC_FAIL);
1358 
1359 	    bcopy(g->data + start * g->numTracks, g->undo.data,
1360 		  g->undo.delLen);
1361 
1362 	    g->undo.delStart = start;
1363 	    break;
1364 	case UNDO_INS:
1365 	    g->undo.insLen = len;
1366 	    g->undo.insStart = start;
1367 	    break;
1368 	case UNDO_REP:
1369 	    g->undo.repLen = len;
1370 
1371 	    if (!(g->undo.repData = (GraphDataType *) malloc(g->undo.repLen)))
1372 		ERRORv(MALLOC_FAIL);
1373 
1374 	    bcopy(g->data + start * g->numTracks, g->undo.repData,
1375 		  g->undo.repLen);
1376 
1377 	    g->undo.repStart = start;
1378 	    break;
1379     }
1380 
1381     XtSetSensitive(g->editUndo, TRUE);
1382 }
1383 
1384 static void
editCut(w,gp,call_data)1385 editCut(w, gp, call_data)
1386 Widget          w;
1387 XtPointer       gp;
1388 XtPointer       call_data;
1389 {
1390     GlobalDataPtr   g = (GlobalDataPtr) gp;
1391     int             start,
1392                     end,
1393                     n;
1394 
1395     if (!copyToSelection(g))
1396 	return;
1397 
1398     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1399     clearUndo(g);
1400     setUndo(g, start, end, UNDO_DEL);
1401 
1402     n = (g->numSamples - end) * sizeof(GraphDataType) * g->numTracks;
1403 
1404     if (n)
1405 	bcopy(g->data + end * g->numTracks,
1406 	      g->data + start * g->numTracks, n);
1407 
1408     g->numSamples -= end - start;
1409 
1410     if (!g->numSamples)
1411 	g->numSamples = 1;
1412 
1413     g->data = (GraphDataType *)
1414 	realloc(g->data, g->numSamples * sizeof(GraphDataType) *
1415 		g->numTracks);
1416 
1417     XtVaSetValues(g->graph, XtNdata, g->data, XtNnumSamples, g->numSamples,
1418 		  NULL);
1419     g->modified = TRUE;
1420 }
1421 
1422 static void
editCopy(w,gp,call_data)1423 editCopy(w, gp, call_data)
1424 Widget          w;
1425 XtPointer       gp;
1426 XtPointer       call_data;
1427 {
1428     GlobalDataPtr   g = (GlobalDataPtr) gp;
1429 
1430     copyToSelection(g);
1431 }
1432 
1433 static void
editUndo(w,gp,call_data)1434 editUndo(w, gp, call_data)
1435 Widget          w;
1436 XtPointer       gp;
1437 XtPointer       call_data;
1438 {
1439     GlobalDataPtr   g = (GlobalDataPtr) gp;
1440     UndoRec         u;
1441     int             numSamples,
1442                     n;
1443     GraphDataType  *p;
1444 
1445     bzero(&u, sizeof(u));
1446 
1447     if (g->undo.delLen)
1448     {
1449 	Boolean         zoomOut = g->numSamples == 1;
1450 	GraphDataType  *new;
1451 
1452 	numSamples = g->undo.delLen / sizeof(GraphDataType) / g->numTracks;
1453 
1454 	if (!(new = (GraphDataType *)
1455 	      realloc(g->data, (g->numSamples + numSamples) *
1456 		      sizeof(GraphDataType) * g->numTracks)))
1457 	    ERRORv(MALLOC_FAIL);
1458 
1459 	p = new + g->undo.delStart * g->numTracks;
1460 	n = (g->numSamples - g->undo.delStart) *
1461 	    sizeof(GraphDataType) * g->numTracks;
1462 
1463 	if (n)
1464 	    bcopy(p, p + numSamples * g->numTracks, n);
1465 
1466 	bcopy(g->undo.data, p, g->undo.delLen);
1467 
1468 	free(g->undo.data);
1469 
1470 	g->data = new;
1471 	g->numSamples += numSamples;
1472 
1473 	XtVaSetValues(g->graph, XtNdata, g->data, XtNnumSamples, g->numSamples,
1474 		      NULL);
1475 
1476 	if (zoomOut)
1477 	    XtVaSetValues(g->graph, XtNstart, 0, XtNleftMarker, 0,
1478 			  XtNend, g->numSamples, XtNrightMarker,
1479 			  g->numSamples, NULL);
1480 
1481 	u.insLen = g->undo.delLen;
1482 	u.insStart = g->undo.delStart;
1483     }
1484 
1485     if (g->undo.repLen)
1486     {
1487 	GraphDataType  *a,
1488 	               *b,
1489 	                t;
1490 	int             i;
1491 
1492 	a = g->data + g->undo.repStart * g->numTracks;
1493 	b = g->undo.repData;
1494 	i = g->undo.repLen / sizeof(GraphDataType);
1495 
1496 	while (i--)
1497 	{
1498 	    t = *a;
1499 	    *a++ = *b;
1500 	    *b++ = t;
1501 	}
1502 
1503 	u.repStart = g->undo.repStart;
1504 	u.repLen = g->undo.repLen;
1505 	u.repData = g->undo.repData;
1506 
1507 	GraphRedraw(g->graph);
1508     }
1509 
1510     if (g->undo.insLen)
1511     {
1512 	if (!(u.data = (GraphDataType *) malloc(g->undo.insLen)))
1513 	    ERRORv(MALLOC_FAIL);
1514 
1515 	p = g->data + g->undo.insStart * g->numTracks;
1516 
1517 	bcopy(p, u.data, g->undo.insLen);
1518 
1519 	u.delLen = g->undo.insLen;
1520 	u.delStart = g->undo.insStart;
1521 
1522 	numSamples = g->undo.insLen / sizeof(GraphDataType) / g->numTracks;
1523 
1524 	n = (g->numSamples - (g->undo.insStart + numSamples)) *
1525 	    sizeof(GraphDataType) * g->numTracks;
1526 
1527 	if (n)
1528 	    bcopy(p + numSamples * g->numTracks, p, n);
1529 
1530 	if (!(g->numSamples -= numSamples))
1531 	    g->numSamples = 1;
1532 
1533 	g->data = (GraphDataType *)
1534 	    realloc(g->data, g->numSamples * sizeof(GraphDataType) *
1535 		    g->numTracks);
1536 
1537 	XtVaSetValues(g->graph, XtNdata, g->data, XtNnumSamples, g->numSamples,
1538 		      NULL);
1539     }
1540 
1541     g->undo = u;
1542 }
1543 
1544 static void
fileLoad(w,gp,call_data)1545 fileLoad(w, gp, call_data)
1546 Widget          w;
1547 XtPointer       gp;
1548 XtPointer       call_data;
1549 {
1550     GlobalDataPtr   g = (GlobalDataPtr) gp;
1551 
1552     MODIFIED(fileLoad);
1553     DIALOG(fileLoad, "Load File", "Filename:");
1554     loadFile(g, XawDialogGetValueString(g->dialog));
1555 }
1556 
1557 static void
fileSaveInterval(w,gp,call_data)1558 fileSaveInterval(w, gp, call_data)
1559 Widget          w;
1560 XtPointer       gp;
1561 XtPointer       call_data;
1562 {
1563     GlobalDataPtr   g = (GlobalDataPtr) gp;
1564     int             start,
1565                     end;
1566 
1567     DIALOG(fileSaveInterval, "Save Interval", "Filename:");
1568     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1569     saveFile(g, XawDialogGetValueString(g->dialog), start, end);
1570 }
1571 
1572 static void
fileSaveAs(w,gp,call_data)1573 fileSaveAs(w, gp, call_data)
1574 Widget          w;
1575 XtPointer       gp;
1576 XtPointer       call_data;
1577 {
1578     GlobalDataPtr   g = (GlobalDataPtr) gp;
1579     int             numSamples;
1580     String          string;
1581 
1582     DIALOG(fileSaveAs, "Save As", "Filename:");
1583 
1584     string = XawDialogGetValueString(g->dialog);
1585     XtVaGetValues(g->graph, XtNnumSamples, &numSamples, NULL);
1586     XtVaSetValues(g->filename, XtNlabel, string, NULL);
1587     saveFile(g, string, 0, numSamples);
1588     g->modified = FALSE;
1589 }
1590 
1591 static void
fileSave(w,gp,call_data)1592 fileSave(w, gp, call_data)
1593 Widget          w;
1594 XtPointer       gp;
1595 XtPointer       call_data;
1596 {
1597     GlobalDataPtr   g = (GlobalDataPtr) gp;
1598     int             numSamples;
1599     String          name;
1600 
1601     XtVaGetValues(g->graph, XtNnumSamples, &numSamples, NULL);
1602     XtVaGetValues(g->filename, XtNlabel, &name, NULL);
1603     saveFile(g, name, 0, numSamples);
1604     g->modified = FALSE;
1605 }
1606 
1607 static void
zoomIn(w,gp,call_data)1608 zoomIn(w, gp, call_data)
1609 Widget          w;
1610 XtPointer       gp;
1611 XtPointer       call_data;
1612 {
1613     GlobalDataPtr   g = (GlobalDataPtr) gp;
1614     int             start,
1615                     end,
1616                     n;
1617 
1618     XtVaGetValues(g->graph, XtNstart, &start, XtNend, &end, NULL);
1619     n = (end - start) / ZOOM_SCALE / 2;
1620     start += n;
1621     end -= n;
1622     XtVaSetValues(g->graph, XtNstart, start, XtNend, end, NULL);
1623 }
1624 
1625 static void
zoomOut(w,gp,call_data)1626 zoomOut(w, gp, call_data)
1627 Widget          w;
1628 XtPointer       gp;
1629 XtPointer       call_data;
1630 {
1631     GlobalDataPtr   g = (GlobalDataPtr) gp;
1632     int             start,
1633                     end,
1634                     n;
1635 
1636     XtVaGetValues(g->graph, XtNstart, &start, XtNend, &end, NULL);
1637     n = (end - start) * ZOOM_SCALE / 2;
1638     start -= n;
1639     end += n;
1640     XtVaSetValues(g->graph, XtNstart, start, XtNend, end, NULL);
1641 }
1642 
1643 static void
zoomMarkers(w,gp,call_data)1644 zoomMarkers(w, gp, call_data)
1645 Widget          w;
1646 XtPointer       gp;
1647 XtPointer       call_data;
1648 {
1649     GlobalDataPtr   g = (GlobalDataPtr) gp;
1650     int             start,
1651                     end;
1652 
1653     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1654     XtVaSetValues(g->graph, XtNstart, start, XtNend, end, NULL);
1655 }
1656 
1657 static void
zoomFull(w,gp,call_data)1658 zoomFull(w, gp, call_data)
1659 Widget          w;
1660 XtPointer       gp;
1661 XtPointer       call_data;
1662 {
1663     GlobalDataPtr   g = (GlobalDataPtr) gp;
1664     int             numSamples;
1665 
1666     XtVaGetValues(g->graph, XtNnumSamples, &numSamples, NULL);
1667     XtVaSetValues(g->graph, XtNstart, 0, XtNend, numSamples - 1, NULL);
1668 }
1669 
1670 static void
effectsReverse(w,gp,call_data)1671 effectsReverse(w, gp, call_data)
1672 Widget          w;
1673 XtPointer       gp;
1674 XtPointer       call_data;
1675 {
1676     GlobalDataPtr   g = (GlobalDataPtr) gp;
1677     int             start,
1678                     end;
1679     GraphDataType  *a,
1680                    *b,
1681                     t;
1682 
1683     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1684     clearUndo(g);
1685     setUndo(g, start, end, UNDO_REP);
1686     a = g->data + start * g->numTracks;
1687     b = g->data + (end - 1) * g->numTracks;
1688 
1689     while (a < b)
1690     {
1691 	t = *a;
1692 	*a++ = *b;
1693 	*b-- = t;
1694     }
1695 
1696     GraphRedraw(g->graph);
1697     g->modified = TRUE;
1698 }
1699 
1700 static void
effectsFadeIn(w,gp,call_data)1701 effectsFadeIn(w, gp, call_data)
1702 Widget          w;
1703 XtPointer       gp;
1704 XtPointer       call_data;
1705 {
1706     GlobalDataPtr   g = (GlobalDataPtr) gp;
1707     int             start,
1708                     end,
1709                     n;
1710     GraphDataType  *p;
1711     float           scale,
1712                     delta;
1713 
1714     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1715     clearUndo(g);
1716     setUndo(g, start, end, UNDO_REP);
1717 
1718     p = g->data + start * g->numTracks;
1719     n = (end - start) * g->numTracks;
1720     delta = 1.0 / n;
1721     scale = 0;
1722 
1723     while (n--)
1724     {
1725 	*p++ *= scale;
1726 	scale += delta;
1727     }
1728 
1729     GraphRedraw(g->graph);
1730     g->modified = TRUE;
1731 }
1732 
1733 static void
effectsFadeOut(w,gp,call_data)1734 effectsFadeOut(w, gp, call_data)
1735 Widget          w;
1736 XtPointer       gp;
1737 XtPointer       call_data;
1738 {
1739     GlobalDataPtr   g = (GlobalDataPtr) gp;
1740     int             start,
1741                     end,
1742                     n;
1743     GraphDataType  *p;
1744     float           scale,
1745                     delta;
1746 
1747     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1748     clearUndo(g);
1749     setUndo(g, start, end, UNDO_REP);
1750 
1751     p = g->data + start * g->numTracks;
1752     n = (end - start) * g->numTracks;
1753     delta = 1.0 / n;
1754     scale = 1;
1755 
1756     while (n--)
1757     {
1758 	*p++ *= scale;
1759 	scale -= delta;
1760     }
1761 
1762     GraphRedraw(g->graph);
1763     g->modified = TRUE;
1764 }
1765 
1766 static void
effectsMaxAmplitude(w,gp,call_data)1767 effectsMaxAmplitude(w, gp, call_data)
1768 Widget          w;
1769 XtPointer       gp;
1770 XtPointer       call_data;
1771 {
1772     GlobalDataPtr   g = (GlobalDataPtr) gp;
1773     int             start,
1774                     end,
1775                     n;
1776     GraphDataType  *p,
1777                     m;
1778     float           scale;
1779 
1780     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1781     clearUndo(g);
1782     setUndo(g, start, end, UNDO_REP);
1783 
1784     p = g->data + start * g->numTracks;
1785     n = (end - start) * g->numTracks;
1786     m = 0;
1787 
1788     while (n--)
1789     {
1790 	m = max(m, abs(*p));
1791 	*p++;
1792     }
1793 
1794     scale = 32767.0 / m;
1795 
1796     p = g->data + start * g->numTracks;
1797     n = (end - start) * g->numTracks;
1798 
1799     while (n--)
1800     {
1801 	int x = *p * scale;
1802 	*p++ = x < SHRT_MIN ? SHRT_MIN : x > SHRT_MAX ? SHRT_MAX : x;
1803     }
1804 
1805     GraphRedraw(g->graph);
1806     g->modified = TRUE;
1807 }
1808 
1809 static void
effectsAmplitude(w,gp,call_data)1810 effectsAmplitude(w, gp, call_data)
1811 Widget          w;
1812 XtPointer       gp;
1813 XtPointer       call_data;
1814 {
1815     GlobalDataPtr   g = (GlobalDataPtr) gp;
1816     int             start,
1817                     end,
1818                     n;
1819     GraphDataType  *p;
1820     float           scale;
1821     String          s;
1822 
1823     DIALOG(effectsAmplitude, "Change Amplitude", "Amplitude scale:");
1824     s = XawDialogGetValueString(g->dialog);
1825     sscanf(s, "%f", &scale);
1826 
1827     XtVaGetValues(g->graph, XtNleftMarker, &start, XtNrightMarker, &end, NULL);
1828     clearUndo(g);
1829     setUndo(g, start, end, UNDO_REP);
1830     p = g->data + start * g->numTracks;
1831     n = (end - start) * g->numTracks;
1832 
1833     while (n--)
1834 	*p++ *= scale;
1835 
1836     GraphRedraw(g->graph);
1837     g->modified = TRUE;
1838 }
1839 
1840 static void
adjustVolume(w,gp,valuep)1841 adjustVolume(w, gp, valuep)
1842 Widget          w;
1843 XtPointer       gp;
1844 XtPointer       valuep;
1845 {
1846     GlobalDataPtr   g = (GlobalDataPtr) gp;
1847     intptr_t        value = (intptr_t) valuep;
1848     AuElementParameters *parms;
1849     ElementListPtr  p = ElementList;
1850     int             i = 0;
1851 
1852     if (!ElementCount)
1853 	return;
1854 
1855     if (!(parms = (AuElementParameters *)
1856 	  malloc(sizeof(AuElementParameters) * ElementCount)))
1857 	fatalError("malloc error in adjustVolume");
1858 
1859     while (p)
1860     {
1861 	parms[i].flow = p->flow;
1862 	parms[i].element_num = p->volumeElement;
1863 	parms[i].num_parameters = AuParmsMultiplyConstant;
1864 	parms[i].parameters[AuParmsMultiplyConstantConstant] =
1865 	    AuFixedPointFromFraction(value, 100);
1866 
1867 	p = p->next;
1868 	i++;
1869     }
1870 
1871     AuSetElementParameters(g->aud, ElementCount, parms, NULL);
1872     free(parms);
1873 }
1874 
1875 static void
setFileFormatMenuButton(w,gp,call_data)1876 setFileFormatMenuButton(w, gp, call_data)
1877 Widget          w;
1878 XtPointer       gp;
1879 XtPointer       call_data;
1880 {
1881     GlobalDataPtr   g = (GlobalDataPtr) gp;
1882     String          string;
1883 
1884     XtVaGetValues(w, XtNlabel, &string, NULL);
1885     XtVaSetValues(g->fileFormatMenuButton, XtNlabel, string, NULL);
1886 }
1887 
1888 static void
setDataFormatMenuButton(w,gp,call_data)1889 setDataFormatMenuButton(w, gp, call_data)
1890 Widget          w;
1891 XtPointer       gp;
1892 XtPointer       call_data;
1893 {
1894     GlobalDataPtr   g = (GlobalDataPtr) gp;
1895     String          string;
1896 
1897     XtVaGetValues(w, XtNlabel, &string, NULL);
1898     XtVaSetValues(g->dataFormatMenuButton, XtNlabel, string, NULL);
1899 }
1900 
1901 static void
popupDialog(g,dialog,callback,title,label)1902 popupDialog(g, dialog, callback, title, label)
1903 GlobalDataPtr   g;
1904 Widget          dialog;
1905 void            (*callback) ();
1906 char           *title,
1907                *label;
1908 {
1909     g->dialogCallback = callback;
1910     XtVaSetValues(XtParent(dialog), XtNtitle, title, NULL);
1911     XtVaSetValues(dialog, XtNlabel, label, NULL);
1912     XtPopup(XtParent(dialog), XtGrabExclusive);
1913 }
1914 
1915 static void
shellCancel(w,gp,call_data)1916 shellCancel(w, gp, call_data)
1917 Widget          w;
1918 XtPointer       gp;
1919 XtPointer       call_data;
1920 {
1921     GlobalDataPtr   g = (GlobalDataPtr) gp;
1922 
1923     g->popdownCause = w;
1924     XtPopdown(XtParent(XtParent(w)));
1925 }
1926 
1927 static void
shellOk(w,gp,call_data)1928 shellOk(w, gp, call_data)
1929 Widget          w;
1930 XtPointer       gp;
1931 XtPointer       call_data;
1932 {
1933     GlobalDataPtr   g = (GlobalDataPtr) gp;
1934 
1935     shellCancel(w, g);
1936 
1937     if (g->dialogCallback)
1938 	(*g->dialogCallback) ((Widget) 0, g);
1939 }
1940 
1941 static void
createWidgets(g)1942 createWidgets(g)
1943 GlobalDataPtr   g;
1944 {
1945     Widget          w;
1946     int             i;
1947     Pixmap          bitmap;
1948     RecordDialogPtr r;
1949 
1950     MakeWidget(g->form, g->top, formWidgetClass, "form");
1951 
1952     /* file menu */
1953     MakeMenuPopup(w, g->form, "fileMenu");
1954     MakeMenuItem(w, fileNew, "fileNew");
1955     MakeMenuItem(w, fileLoad, "fileLoad");
1956     MakeMenuItem(w, fileSave, "fileSave");
1957     MakeMenuItem(w, fileSaveAs, "fileSaveAs");
1958     MakeMenuItem(w, fileSaveInterval, "fileSaveInterval");
1959     MakeMenuItem(w, fileRevert, "fileRevert");
1960     MakeMenuLine(w);
1961     MakeMenuItem(w, fileExit, "fileExit");
1962     MakeMenuButton(g->fileMenuButton, g->form, "fileMenuButton");
1963 
1964     /* edit menu */
1965     MakeMenuPopup(w, g->form, "editMenu");
1966     XtAddCallback(w, XtNpopupCallback, checkSelection, g);
1967     MakeMenuItem(w, editCut, "editCut");
1968     MakeMenuItem(w, editCopy, "editCopy");
1969     MakeMenuItemW(g->editPasteInsert, w, editPaste, "editPasteInsert");
1970     MakeMenuItemW(g->editPasteReplace, w, editPaste, "editPasteReplace");
1971     MakeMenuItemW(g->editPasteMix, w, editPaste, "editPasteMix");
1972     MakeMenuLine(w);
1973     MakeMenuItemW(g->editUndo, w, editUndo, "editUndo");
1974     MakeMenuButton(g->editMenuButton, g->form, "editMenuButton");
1975 
1976     /* zoom menu */
1977     MakeMenuPopup(w, g->form, "zoomMenu");
1978     MakeMenuItem(w, zoomIn, "zoomIn");
1979     MakeMenuItem(w, zoomOut, "zoomOut");
1980     MakeMenuItem(w, zoomMarkers, "zoomMarkers");
1981     MakeMenuItem(w, zoomFull, "zoomFull");
1982     MakeMenuButton(g->zoomMenuButton, g->form, "zoomMenuButton");
1983 
1984     /* effects menu */
1985     MakeMenuPopup(w, g->form, "effectsMenu");
1986     MakeMenuItem(w, effectsAmplitude, "effectsAmplitude");
1987     MakeMenuItem(w, effectsMaxAmplitude, "effectsMaxAmplitude");
1988     MakeMenuItem(w, effectsReverse, "effectsReverse");
1989     MakeMenuItem(w, effectsFadeIn, "effectsFadeIn");
1990     MakeMenuItem(w, effectsFadeOut, "effectsFadeOut");
1991     MakeMenuButton(g->effectsMenuButton, g->form, "effectsMenuButton");
1992 
1993     /* record button */
1994     MakeButton(g->record, g->form, record, "record");
1995 
1996     /* filename */
1997     MakeLabel(g->filename, g->form, "filename");
1998 
1999     /* volume slider */
2000     MakeWidget(g->volumeSlider, g->form, sliderWidgetClass, "volumeSlider");
2001     XtAddCallback(g->volumeSlider, XtNcallback, adjustVolume, g);
2002 
2003     MakeWidget(g->graph, g->form, graphWidgetClass, "graph");
2004     XtAddCallback(g->graph, XtNleftProc, leftMarker, g);
2005     XtAddCallback(g->graph, XtNrightProc, rightMarker, g);
2006 
2007     MakeLabel(w, g->form, "leftLabel");
2008     MakeLabel(g->leftTime, g->form, "leftTime");
2009 
2010     MakeLabel(w, g->form, "durationLabel");
2011     MakeLabel(g->durationTime, g->form, "durationTime");
2012 
2013     MakeLabel(w, g->form, "rightLabel");
2014     MakeLabel(g->rightTime, g->form, "rightTime");
2015 
2016     MakeLabel(w, g->form, "positionLabel");
2017     MakeLabel(g->positionTime, g->form, "positionTime");
2018 
2019     /* control buttons */
2020     MakeButton(g->play, g->form, play, "play");
2021     bitmap = XCreateBitmapFromData(g->dpy,
2022 				   RootWindow(g->dpy, DefaultScreen(g->dpy)),
2023 				   (char *) play_bits,
2024 				   play_width, play_height);
2025     XtVaSetValues(g->play, XtNbitmap, bitmap, NULL);
2026 
2027     MakeButton(g->stop, g->form, stop, "stop");
2028     bitmap = XCreateBitmapFromData(g->dpy,
2029 				   RootWindow(g->dpy, DefaultScreen(g->dpy)),
2030 				   (char *) stop_bits,
2031 				   stop_width, stop_height);
2032     XtVaSetValues(g->stop, XtNbitmap, bitmap, NULL);
2033 
2034     MakeButton(g->pause, g->form, Pause, "pause");
2035     bitmap = XCreateBitmapFromData(g->dpy,
2036 				   RootWindow(g->dpy, DefaultScreen(g->dpy)),
2037 				   (char *) pause_bits,
2038 				   pause_width, pause_height);
2039     XtVaSetValues(g->pause, XtNbitmap, bitmap, NULL);
2040 
2041     /* file format menu */
2042     MakeLabel(w, g->form, "fileFormatLabel");
2043     MakeMenuPopup(w, g->form, "fileFormatMenu");
2044 
2045     for (i = 0; i < SoundNumFileFormats; i++)
2046 	MakeMenuItem(w, setFileFormatMenuButton, SoundFileFormatToString(i));
2047 
2048     MakeMenuButton(g->fileFormatMenuButton, g->form, "fileFormatMenuButton");
2049     XtVaSetValues(g->fileFormatMenuButton, XtNlabel, SoundFileFormatToString(0),
2050 		  NULL);
2051 
2052     /* data format menu */
2053     MakeLabel(w, g->form, "dataFormatLabel");
2054     MakeMenuPopup(w, g->form, "dataFormatMenu");
2055 
2056     for (i = 0; i < AuServerNumFormats(g->aud); i++)
2057 	MakeMenuItem(w, setDataFormatMenuButton,
2058 		     AuFormatToString(AuServerFormat(g->aud, i)));
2059 
2060     MakeMenuButton(g->dataFormatMenuButton, g->form, "dataFormatMenuButton");
2061     XtVaSetValues(g->dataFormatMenuButton, XtNlabel,
2062 		  AuFormatToString(AuServerFormat(g->aud, 0)), NULL);
2063 
2064     /* frequency */
2065     MakeLabel(w, g->form, "frequencyLabel");
2066     MakeWidget(g->frequency, g->form, asciiTextWidgetClass, "frequency");
2067 
2068     /* comment */
2069     MakeLabel(w, g->form, "commentLabel");
2070     MakeWidget(g->comment, g->form, asciiTextWidgetClass, "comment");
2071 
2072     /* dialog box */
2073     MakePopup(g->dialogShell, g->top, transientShellWidgetClass, "dialogShell");
2074     XtAddCallback(g->dialogShell, XtNpopupCallback, placePopup, g);
2075     MakeWidget(g->dialog, g->dialogShell, dialogWidgetClass, "dialog");
2076     XawDialogAddButton(g->dialog, "okButton", shellOk, g);
2077     XawDialogAddButton(g->dialog, "cancelButton", shellCancel, g);
2078 
2079     /* warning box */
2080     MakePopup(g->warningShell, g->top, transientShellWidgetClass,
2081 	      "warningShell");
2082     XtAddCallback(g->warningShell, XtNpopupCallback, placePopup, g);
2083     MakeWidget(g->warning, g->warningShell, dialogWidgetClass, "warning");
2084     XawDialogAddButton(g->warning, "okButton", shellOk, g);
2085     XawDialogAddButton(g->warning, "cancelButton", shellCancel, g);
2086 
2087     /* error box */
2088     MakePopup(g->errorShell, g->top, transientShellWidgetClass, "errorShell");
2089     XtAddCallback(g->errorShell, XtNpopupCallback, placePopup, g);
2090     MakeWidget(g->error, g->errorShell, dialogWidgetClass, "error");
2091     XawDialogAddButton(g->error, "okButton", shellCancel, g);
2092 
2093     /* record box */
2094     r = &g->recordDialog;
2095     MakePopup(r->top, g->top, transientShellWidgetClass, "recordShell");
2096     XtAddCallback(r->top, XtNpopupCallback, placePopup, g);
2097     MakeWidget(r->form, r->top, formWidgetClass, "form");
2098 
2099     MakeLabel(w, r->form, "recordDurationLabel");
2100     MakeWidget(r->duration, r->form, asciiTextWidgetClass, "duration");
2101 
2102     MakeLabel(w, r->form, "recordFreqLabel");
2103     MakeWidget(r->freq, r->form, asciiTextWidgetClass, "recordFreq");
2104 
2105     MakeLabel(w, r->form, "modeLabel");
2106     MakeWidget(r->mode, r->form, toggleWidgetClass, "mode");
2107     XtAddCallback(r->mode, XtNcallback, recordMode, g);
2108 
2109     /* gain slider */
2110     MakeWidget(r->gainSlider, r->form, sliderWidgetClass, "gainSlider");
2111     XtAddCallback(r->gainSlider, XtNcallback, adjustGain, g);
2112 
2113     MakeButton(w, r->form, recordStart, "recordStart");
2114     MakeButton(w, r->form, recordMonitor, "monitor");
2115     MakeButton(w, r->form, recordDismiss, "dismiss");
2116 }
2117 
2118 static Boolean
loadFile(g,name)2119 loadFile(g, name)
2120 GlobalDataPtr   g;
2121 char           *name;
2122 {
2123     Sound           s;
2124     char            buf[20];
2125 
2126     if (g->data)
2127     {
2128 	free(g->data);
2129 	g->data = NULL;
2130     }
2131 
2132     if (!(s = SoundOpenFileForReading(name)))
2133     {
2134 	ERROR("Can't open file");
2135 	return noFile(g);
2136     }
2137 
2138     g->numSamples = SoundNumSamples(s);
2139     g->numTracks = SoundNumTracks(s);
2140     g->sampleRate = SoundSampleRate(s);
2141 
2142     if (g->numSamples != SoundUnknownNumSamples)
2143     {
2144 	if (!(g->data = (GraphDataType *)
2145 	      malloc(sizeof(GraphDataType) * SoundNumSamples(s) *
2146 		     SoundNumTracks(s))))
2147 	{
2148 	    SoundCloseFile(s);
2149 	    return noFile(g);
2150 	}
2151 
2152 	if (SoundReadFile((char *) g->data, SoundNumBytes(s), s) !=
2153 	    SoundNumBytes(s) ||
2154 	    AuConvertDataToShort(SoundDataFormat(s), SoundNumBytes(s),
2155 				 g->data) == -1)
2156 	{
2157 	    free(g->data);
2158 	    SoundCloseFile(s);
2159 	    g->data = NULL;
2160 	    return noFile(g);
2161 	}
2162     }
2163     else
2164 #define CHUNK		(10 * 1024)
2165 #define DATA_BYTES(_n)	((_n) * sizeof(GraphDataType) * SoundNumTracks(s))
2166 #define FILE_BYTES(_n)	((_n) * SoundBytesPerSample(s) * SoundNumTracks(s))
2167     {
2168 	int             n,
2169 	                offset = 0;
2170 	AuBool          done = AuFalse;
2171 	GraphDataType  *p;
2172 
2173 	g->data = (GraphDataType *) malloc(1);
2174 	g->numSamples = 0;
2175 
2176 	while (!done)
2177 	{
2178 	    if (!(p = (GraphDataType *)
2179 		  realloc(g->data, DATA_BYTES(g->numSamples + CHUNK))))
2180 	    {
2181 		free(g->data);
2182 		SoundCloseFile(s);
2183 		g->data = NULL;
2184 		return noFile(g);
2185 	    }
2186 
2187 	    g->data = p;
2188 	    n = SoundReadFile(((char *) g->data) + offset,
2189 			      FILE_BYTES(CHUNK), s);
2190 
2191 	    offset += n;
2192 	    g->numSamples += n / SoundBytesPerSample(s) / SoundNumTracks(s);
2193 	    done = n < FILE_BYTES(CHUNK);
2194 	}
2195 
2196 	if (AuConvertDataToShort(SoundDataFormat(s), FILE_BYTES(g->numSamples),
2197 				 g->data) == -1)
2198 	{
2199 	    free(g->data);
2200 	    SoundCloseFile(s);
2201 	    g->data = NULL;
2202 	    return noFile(g);
2203 	}
2204     }
2205 
2206     sprintf(buf, "%d", g->sampleRate);
2207 
2208     XtVaSetValues(g->filename, XtNlabel, name, NULL);
2209     XtVaSetValues(g->comment, XtNstring, SoundComment(s), NULL);
2210     XtVaSetValues(g->frequency, XtNstring, buf, NULL);
2211     XtVaSetValues(g->dataFormatMenuButton, XtNlabel, SoundDataFormatString(s),
2212 		  NULL);
2213     XtVaSetValues(g->fileFormatMenuButton, XtNlabel, SoundFileFormatString(s),
2214 		  NULL);
2215 
2216     XtVaSetValues(g->graph, XtNdata, g->data,
2217 		  XtNnumSamples, g->numSamples,
2218 		  XtNnumTracks, g->numTracks,
2219 		  XtNstart, 0, XtNend, g->numSamples,
2220 		  XtNleftMarker, 0, XtNrightMarker, g->numSamples,
2221 		  NULL);
2222 
2223     SoundCloseFile(s);
2224     clearUndo(g);
2225 
2226     return TRUE;
2227 }
2228 
2229 static Boolean
saveFile(g,name,start,end)2230 saveFile(g, name, start, end)
2231 String          name;
2232 GlobalDataPtr   g;
2233 int             start,
2234                 end;
2235 {
2236     Sound           s;
2237     char           *tmpName,
2238                    *mktemp();
2239     short          *p;
2240     String          st;
2241     int             n,
2242                     fileFormat,
2243                     dataFormat,
2244 #if defined(HAS_MKSTEMP)
2245                     fd = -1,
2246 #endif
2247                     status;
2248 
2249     XtVaGetValues(g->fileFormatMenuButton, XtNlabel, &st, NULL);
2250     fileFormat = SoundStringToFileFormat(st);
2251 
2252     XtVaGetValues(g->dataFormatMenuButton, XtNlabel, &st, NULL);
2253     dataFormat = AuStringToFormat(st);
2254     XtVaGetValues(g->comment, XtNstring, &st, NULL);
2255 
2256     s = SoundCreate(fileFormat, dataFormat, g->numTracks, g->sampleRate,
2257 		    end - start, st);
2258 
2259     if (!s)
2260 	ERRORf("File/data format mismatch");
2261 
2262     if (!(tmpName = (char *) malloc(strlen(name) + 7)))
2263     {
2264 	SoundDestroy(s);
2265 	ERRORf(MALLOC_FAIL);
2266     }
2267 
2268     sprintf(tmpName, "%sXXXXXX", name);
2269 
2270 #if defined(HAS_MKSTEMP)
2271     fd = mkstemp(tmpName);
2272     if (-1 == fd || !SoundOpenFileForWriting(tmpName, s))
2273 #else
2274     tmpName = mktemp(tmpName);
2275     if (!SoundOpenFileForWriting(tmpName, s))
2276 #endif
2277     {
2278 	free(tmpName);
2279 	SoundDestroy(s);
2280 	ERRORf("Can't create output file");
2281     }
2282 
2283     p = g->data + start * g->numTracks;
2284     n = (end - start) * g->numTracks;
2285 
2286     AuConvertShortToData(SoundDataFormat(s), n * sizeof(short), p);
2287     n *= SoundBytesPerSample(s);
2288     status = SoundWriteFile((char *) p, n, s) != n;
2289     AuConvertDataToShort(SoundDataFormat(s), n, p);
2290     GraphRedraw(g->graph);
2291 
2292     if (status || SoundCloseFile(s))
2293     {
2294 #if defined(HAS_MKSTEMP)
2295       if (fd != -1)
2296 	close(fd);
2297 #endif
2298       free(tmpName);
2299       ERRORf("Error writing output file");
2300     }
2301 
2302     /* if the file exists then back it up */
2303     if (!access(name, R_OK))
2304     {
2305 	char           *backup;
2306 
2307 	if (!(backup = (char *) malloc(strlen(name) + 2)))
2308 	{
2309 	    free(tmpName);
2310 	    ERRORf(MALLOC_FAIL);
2311 	}
2312 
2313 	sprintf(backup, "%s~", name);
2314 
2315 	if (rename(name, backup))
2316 	{
2317 	    free(backup);
2318 	    free(tmpName);
2319 	    ERRORf("Can't create backup file");
2320 	}
2321     }
2322 
2323     if (rename(tmpName, name))
2324     {
2325 	free(tmpName);
2326 	ERRORf("Can't rename temp file");
2327     }
2328 
2329     free(tmpName);
2330     return TRUE;
2331 }
2332 
2333 #define SET_WIDTH(_w)							      \
2334 {									      \
2335     XtVaGetValues(_w, XtNx, &x, NULL);					      \
2336     XtVaSetValues(_w, XtNwidth, maxX - x, NULL);			      \
2337 }
2338 
2339 static void
alignWidgets(g)2340 alignWidgets(g)
2341 GlobalDataPtr   g;
2342 {
2343     Position        maxX,
2344                     x;
2345     Dimension       width;
2346 
2347     XtRealizeWidget(g->top);
2348     XtRealizeWidget(g->dialogShell);
2349     XtRealizeWidget(g->errorShell);
2350     XtRealizeWidget(g->warningShell);
2351     XtRealizeWidget(g->recordDialog.top);
2352 
2353     XtVaGetValues(g->graph, XtNx, &x, XtNwidth, &width, NULL);
2354     maxX = x + width;
2355 
2356     SET_WIDTH(g->filename);
2357     SET_WIDTH(g->volumeSlider);
2358     SET_WIDTH(g->comment);
2359 
2360     XtVaSetValues(g->filename, XtNresizable, FALSE, NULL);
2361 }
2362 
2363 /* Actions */
2364 
2365 static void
updateFrequency(w,event,params,num_params)2366 updateFrequency(w, event, params, num_params)
2367 Widget          w;
2368 XEvent         *event;
2369 String         *params;
2370 Cardinal       *num_params;
2371 {
2372     GlobalDataPtr   g = globals;
2373     String          s;
2374     int             f;
2375 
2376     XtVaGetValues(w, XtNstring, &s, NULL);
2377     f = atoi(s);
2378 
2379     if (f != g->sampleRate)
2380     {
2381 	int             l,
2382 	                r,
2383 	                p;
2384 
2385 	if (f < 1 || f > 100000)
2386 	{
2387 	    char            buf[20];
2388 
2389 	    sprintf(buf, "%d", g->sampleRate);
2390 	    XtVaSetValues(g->frequency, XtNstring, buf, NULL);
2391 	    ERROR("Illegal frequency");
2392 	}
2393 	else
2394 	{
2395 	    g->sampleRate = f;
2396 
2397 	    XtVaGetValues(g->graph, XtNleftMarker, &l, XtNrightMarker, &r,
2398 			  XtNposition, &p, NULL);
2399 	    setTime(g, g->leftTime, l);
2400 	    setTime(g, g->rightTime, r);
2401 	    setTime(g, g->durationTime, r - l);
2402 	    setTime(g, g->positionTime, p);
2403 	}
2404     }
2405 }
2406 
2407 static void
popdownCallback(w,gp,call_data)2408 popdownCallback(w, gp, call_data)
2409 Widget          w;
2410 XtPointer       gp;
2411 XtPointer       call_data;
2412 {
2413     GlobalDataPtr   g = (GlobalDataPtr) gp;
2414 
2415     if (g->popdownCause == XtNameToWidget(g->warning, "cancelButton"))
2416 	fileExit(w, g);
2417 }
2418 
2419 static void
offerToSave(w,g)2420 offerToSave(w, g)
2421 Widget          w;
2422 GlobalDataPtr   g;
2423 {
2424     g->modified = FALSE;
2425     XtAddCallback(g->warningShell, XtNpopdownCallback, popdownCallback, g);
2426     WARNING(offerToSave, "File has been modified - Save it?");
2427 
2428     g->modified = TRUE;
2429     fileSave(w, g);
2430     fileExit(w, g);
2431 }
2432 
2433 static void
quit(w,event,params,num_params)2434 quit(w, event, params, num_params)
2435 Widget          w;
2436 XEvent         *event;
2437 String         *params;
2438 Cardinal       *num_params;
2439 {
2440     GlobalDataPtr   g = globals;
2441 
2442     if (event->type == ClientMessage &&
2443 	event->xclient.data.l[0] != g->wm_delete_window)
2444 	XBell(g->dpy, 0);
2445     else
2446     {
2447 	if (w == g->top)
2448 	{
2449 	    if (g->modified)
2450 		offerToSave(w, g);
2451 	    else
2452 		fileExit(w, g);
2453 	}
2454 	else if (w == g->dialogShell)
2455 	    shellCancel(XtNameToWidget(g->dialog, "cancelButton"), g);
2456 	else if (w == g->errorShell)
2457 	    shellCancel(XtNameToWidget(g->error, "okButton"), g);
2458 	else if (w == g->recordDialog.top)
2459 	    recordDismiss(XtNameToWidget(g->recordDialog.top, "dismiss"), g, 0);
2460     }
2461 }
2462 
2463 static void
ok(w,event,params,num_params)2464 ok(w, event, params, num_params)
2465 Widget          w;
2466 XEvent         *event;
2467 String         *params;
2468 Cardinal       *num_params;
2469 {
2470     GlobalDataPtr   g = globals;
2471 
2472     XtCallCallbacks(XtNameToWidget(g->dialog, "okButton"), XtNcallback, g);
2473 }
2474 
2475 int
main(argc,argv)2476 main(argc, argv)
2477 int             argc;
2478 char          **argv;
2479 {
2480     XtAppContext    appContext;
2481     GlobalDataRec   globalData,
2482                    *g;
2483     char           *filename = NULL,
2484                    *arg,
2485                    *audioServer = NULL;
2486     int             i;
2487     static XtActionsRec actions[] =
2488     {
2489 	{"quit", quit},
2490 	{"updateFrequency", updateFrequency},
2491 	{"ok", ok},
2492     };
2493     RecordDialogPtr r;
2494 
2495     g = globals = &globalData;
2496     r = &g->recordDialog;
2497     g->top = XtVaAppInitialize(&appContext, APP_CLASS, NULL, ZERO,
2498 			       &argc, argv, defaultResources, NULL, NULL);
2499     XtAppAddActions(appContext, actions, XtNumber(actions));
2500 
2501     while (--argc)
2502     {
2503 	arg = *++argv;
2504 
2505 	if (!strncmp(arg, "-a", 2))
2506 	{
2507 	    if (--argc)
2508 		audioServer = *++argv;
2509 	    else
2510 		fatalError(USAGE);
2511 	}
2512 	else if (!strncmp(arg, "-h", 2) || !strncmp(arg, "-?", 2))
2513 	    fatalError(USAGE);
2514 	else
2515 	    filename = arg;
2516     }
2517 
2518     if (!(g->aud = AuOpenServer(audioServer, 0, NULL, 0, NULL, NULL)))
2519 	fatalError("Can't connect to audio server");
2520 
2521     g->selection.data = NULL;
2522     g->selection.callback = NULL;
2523     g->dpy = XtDisplay(g->top);
2524     g->data = NULL;
2525     g->modified = FALSE;
2526     g->endian = 1;
2527     g->undo.data = NULL;
2528     g->undo.repData = NULL;
2529 
2530     createWidgets(g);
2531 
2532     if (filename)
2533 	loadFile(g, filename);
2534     else
2535 	noFile(g);
2536 
2537     alignWidgets(g);
2538 
2539     /* handle delete window message */
2540     g->wm_delete_window = XInternAtom(g->dpy, "WM_DELETE_WINDOW", FALSE);
2541     XSetWMProtocols(g->dpy, XtWindow(g->top), &g->wm_delete_window, 1);
2542     XSetWMProtocols(g->dpy, XtWindow(g->dialogShell), &g->wm_delete_window, 1);
2543     XSetWMProtocols(g->dpy, XtWindow(g->errorShell), &g->wm_delete_window, 1);
2544     XSetWMProtocols(g->dpy, XtWindow(g->warningShell), &g->wm_delete_window, 1);
2545     XSetWMProtocols(g->dpy, XtWindow(r->top), &g->wm_delete_window, 1);
2546 
2547     /* get atoms for selections */
2548     g->selection.atom = XInternAtom(g->dpy, APP_CLASS, FALSE);
2549 
2550     /* get contexts for selections */
2551     g->selection.context = XUniqueContext();
2552 
2553     r->inputDevice = (AuDeviceID) 0;
2554 
2555     for (i = 0; i < AuServerNumDevices(g->aud); i++)
2556 	if ((AuDeviceKind(AuServerDevice(g->aud, i)) ==
2557 	     AuComponentKindPhysicalInput))
2558 	{
2559 	    r->inputDevice =
2560 		AuDeviceIdentifier(AuServerDevice(globals->aud, i));
2561 	    r->inputDeviceNum = i;
2562 	    break;
2563 	}
2564 
2565     if (r->inputDevice)
2566     {
2567 	if (AuDeviceChangableMask(AuServerDevice(g->aud, r->inputDeviceNum)) &
2568 	    AuCompDeviceGainMask)
2569 	    XtVaSetValues(r->gainSlider, XtNvalue,
2570 			  AuFixedPointRoundUp(AuDeviceGain(AuServerDevice(g->aud, r->inputDeviceNum))),
2571 			  NULL);
2572 	else
2573 	    XtSetSensitive(r->gainSlider, False);
2574 
2575 	if (AuDeviceChangableMask(AuServerDevice(g->aud, r->inputDeviceNum)) &
2576 	    AuCompDeviceLineModeMask)
2577 	{
2578 	    r->currentMode =
2579 		AuDeviceLineMode(AuServerDevice(g->aud, r->inputDeviceNum));
2580 	    XtVaSetValues(r->mode, XtNlabel, r->currentMode ==
2581 			  AuDeviceLineModeLow ? "Line" : "Mic", NULL);
2582 	}
2583 	else
2584 	{
2585 	    XtSetSensitive(r->mode, False);
2586 	    XtSetSensitive(XtNameToWidget(r->form, "modeLabel"), False);
2587 	}
2588     }
2589     else
2590 	XtSetSensitive(g->record, False);
2591 
2592     clearUndo(g);
2593 
2594     AuXtAppAddAudioHandler(appContext, g->aud);
2595     XtAppMainLoop(appContext);
2596     return 0;
2597 }
2598