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, ¤t, 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