1 /* $Header: /home/cvs/wavplay/xltwavplay.c,v 1.2 1999/12/04 00:01:20 wwg Exp $
2 * Warren W. Gay VE3WWG Thu Feb 13 21:11:22 1997
3 *
4 * X LessTif WAV Play :
5 *
6 * Copyright (C) 1997 Warren W. Gay VE3WWG
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details (see enclosed file COPYING).
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * Send correspondance to:
22 *
23 * Warren W. Gay VE3WWG
24 *
25 * Email:
26 * ve3wwg@yahoo.com
27 * wgay@mackenziefinancial.com
28 *
29 * $Log: xltwavplay.c,v $
30 * Revision 1.2 1999/12/04 00:01:20 wwg
31 * Implement wavplay-1.4 release changes
32 *
33 * Revision 1.1.1.1 1999/11/21 19:50:56 wwg
34 * Import wavplay-1.3 into CVS
35 *
36 * Revision 1.4 1997/04/17 23:43:08 wwg
37 * Added #include <errno.h> for 1.0pl2 fix.
38 *
39 * Revision 1.3 1997/04/17 00:41:56 wwg
40 * Fixed to use member name .Errno vs original dangerous
41 * .errno reference. This presents a problem on platforms
42 * that make extern errno thread safe, but defining errno
43 * as a macro.
44 *
45 * Revision 1.2 1997/04/15 02:13:35 wwg
46 * A number of small pre-release fixes.
47 *
48 * Revision 1.1 1997/04/13 23:59:08 wwg
49 * Initial revision
50 */
51 #define TIMER1_MS 150 /* TimerProc1() */
52
53 #define LessTif_Bug_radioBehavior 1 /* XmNradioBehavior busted in RowColumn widget (T/F) */
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <stdarg.h>
59 #include <time.h>
60 #include <signal.h>
61 #include <string.h>
62 #ifndef FREEBSD
63 #include <getopt.h>
64 #endif
65 #include <errno.h>
66 #include <sys/types.h>
67 #include <sys/ipc.h>
68 #include <sys/sem.h>
69 #include <sys/wait.h>
70 #include <sys/stat.h>
71 #include <assert.h>
72
73 #ifndef NO_EDITRES
74 #include <X11/Xmu/Editres.h>
75 #endif
76
77 #include <Xm/Xm.h>
78 #include <Xm/MainW.h>
79 #include <Xm/Form.h>
80 #include <Xm/PushB.h>
81 #include <Xm/ArrowB.h>
82 #include <Xm/CascadeB.h>
83 #include <Xm/Text.h>
84 #include <Xm/Label.h>
85 #include <Xm/ToggleB.h>
86 #include <Xm/Separator.h>
87 #include <Xm/MessageB.h>
88 #include <Xm/ScrollBar.h>
89 #include <Xm/RowColumn.h>
90 #include <Xm/FileSB.h>
91 #include <Xm/List.h>
92 #include <Xm/Frame.h>
93
94 #include "xmsprint.h"
95 #include "xltwavplay.h"
96 #include "wavplay.h"
97 #include "client.h"
98
99 static char * const rcsid[] = {
100 "@(#)xltwavplay.c $Revision: 1.2 $",
101 _wavplay_h_,
102 "@(#)xltwavplay version " WAVPLAY_VERSION
103 };
104
105 #ifndef NO_EDITRES
106 extern void _XEditResCheckmessages(void); /* EditRes event handler */
107 #endif
108
109 extern pid_t svrPID; /* Process ID of the wavplay server */
110 extern int svrIPC; /* Message Queue ID for the wavplay server */
111 int cmdopt_x = 0; /* This is controlled by bOptionsDebug */
112 int bRecorded = 0; /* True after first record event */
113
114 char *env_WAVPLAYPATH = WAVPLAYPATH; /* Default pathname of executable /usr/local/bin/wavplay */
115 char *env_AUDIODEV = AUDIODEV; /* Default compiled in audio device */
116 unsigned long env_AUDIOLCK = AUDIOLCK; /* Default compiled in locking semaphore */
117
118 /* FALLBACK RESOURCES : */
119
120 static String fallback_resources[] = {
121 #ifndef INSTALLED_RESOURCE_FILE /* if not installed as file xltwavplay use.. */
122 "xltwavplay.geometry: +350+250",
123 "xltwavplay*background: grey70",
124 "xltwavplay*foreground: black",
125
126 "xltwavplay.*fontList: *helvetica-bold-r-normal--14*",
127
128 "xltwavplay.main.menubar.spacing: 10",
129 "xltwavplay.main.menubar.filemenucascade.labelString: File",
130 "xltwavplay*filemenu.select_files.labelString: Select Files..",
131 "xltwavplay*filemenu.exit.labelString: Exit",
132
133 "xltwavplay*filemenu.save_as.labelString: Save " RECORD_PATH " As..",
134
135 "xltwavplay.main.menubar.optsmenucascade.labelString: Options",
136 "xltwavplay*optsmenu.debug.labelString: Debug Mode",
137 "xltwavplay*optsmenu.sem_reset.labelString: Reset locking semaphores",
138
139 "xltwavplay.main.menubar.helpmenucascade.labelString: Help",
140 "xltwavplay*helpmenu.about.labelString: About",
141
142 /* Copyright notice: adjust font to suit */
143 "xltwavplay*AboutDlg.fontList: -adobe-helvetica-medium-r-normal-*-10-100-75-75-p-*-iso8859-1",
144
145 "xltwavplay*FilesDlg.dialogTitle: Select file(s) to play",
146 "xltwavplay*FilesDlg.pattern: *.wav",
147 "xltwavplay*FilesDlg.fileTypeMask: FILE_REGULAR",
148
149 "xltwavplay*SaveAsDlg.dialogTitle: Select a file name to write",
150 "xltwavplay*SaveAsDlg.pattern: *.wav",
151 "xltwavplay*SaveAsDlg.fileTypeMask: FILE_REGULAR",
152
153 "xltwavplay*listbox_label.labelString: Selection:",
154
155 "xltwavplay*FileLbl0.labelString: File:",
156 "xltwavplay*TypeLbl0.labelString: Type:",
157 "xltwavplay*DateLbl0.labelString: Date:",
158 "xltwavplay*SizeLbl0.labelString: Size:",
159 "xltwavplay*SamplingRateLbl0.labelString: Sampling Rate:",
160 "xltwavplay*SamplesLbl0.labelString: Samples:",
161 "xltwavplay*TimeLbl0.labelString: Duration:",
162 "xltwavplay*TimeDisplayLbl0.labelString: SMPTE Time:",
163
164 "xltwavplay*FileLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
165 "xltwavplay*FileLbl1.foreground: black",
166 "xltwavplay*TypeLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
167 "xltwavplay*TypeLbl1.foreground: black",
168 "xltwavplay*DateLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
169 "xltwavplay*DateLbl1.foreground: black",
170 "xltwavplay*SizeLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
171 "xltwavplay*SizeLbl1.foreground: black",
172 "xltwavplay*SamplingRateLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
173 "xltwavplay*SamplingRateLbl1.foreground: black",
174 "xltwavplay*SamplesLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
175 "xltwavplay*SamplesLbl1.foreground: black",
176 "xltwavplay*TimeLbl1.labelString: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
177 "xltwavplay*TimeLbl1.foreground: black",
178 "xltwavplay*TimeDisplayLbl1.labelString: 00:00:00.00",
179
180 "xltwavplay*Play.labelString: Play",
181 "xltwavplay*StopPlay.labelString: Stop",
182 "xltwavplay*Pause.labelString: Pause",
183 "xltwavplay*Restore.labelString: Restore",
184
185 "xltwavplay*Record.labelString: Record",
186 "xltwavplay*Stop.labelString: Stop",
187
188 "xltwavplay*8Bit.labelString: 8 bit",
189 "xltwavplay*16Bit.labelString: 16 bit",
190
191 "xltwavplay*rate_lbl.labelString: Sampling Rate:",
192
193 "xltwavplay*rec_rate_tgl.labelString: Record Sampling Rate:",
194 #endif /* ndef INSTALLED_RESOURCE_FILE */
195 NULL
196 };
197
198 #define NoTMR 0UL /* Value for "no timer" */
199
200 static void SampleRateChg_Show(int newValue);
201
202 /*
203 * WIDGETS:
204 */
205 Widget wParent; /* Parent shell widget */
206 XtAppContext aContext; /* Application context */
207
208 Widget wMain; /* Main widget */
209 Widget wMenuBar; /* Main menu bar */
210 Widget wFileMenuCascade; /* Cascade button for File Menu */
211 Widget wFileMenu; /* File menu itself */
212 Widget wSelect; /* File->Select */
213 Widget wSaveAs; /* File->SaveAs.. */
214 Widget wExit; /* File->Exit */
215
216 Widget wOptionsMenuCascade;
217 Widget wOptionsMenu;
218 Widget wOptions_DebugTgl;
219 Boolean bOptionsDebug = False; /* Debug mode when True */
220 Widget wOptions_SemReset; /* Options->Semaphore Reset */
221
222 Widget wHelpMenuCascade; /* Cascade button for Help Menu */
223 Widget wHelpMenu; /* Help menu itself */
224 Widget wAbout; /* Help->About */
225
226 Widget wErrDlg; /* Error Dialog Box */
227 XmString sErrDlgTitle; /* Title for the Error Dialog Box */
228
229 Widget wAboutDlg; /* About Dialog Box */
230 XmString sAboutDlgTitle; /* About Dialog Title String */
231 XmString sAboutDlgMessage; /* About Dialog message and copyright */
232
233 Widget wFilesDlg; /* File selection dialog */
234 Widget wSaveAsDlg; /* File "Save As..." dialog */
235
236 Widget wMainForm; /* Main form widget */
237
238 Widget wLeftRC; /* Left main Row/Column */
239 Widget wListFrame; /* Frame for Selections List Box */
240 Widget wListForm; /* Form within Selections List Box */
241 Widget wSelectionsLbl; /* Label for Selections List Box */
242 Widget wSelectionsListBox; /* The selections List Box */
243 Widget wTimeFrame; /* Frame for Time control */
244 Widget wTimeRC; /* Row Col widget for Time control */
245 Widget wTimeDisplayRC; /* Keeps display next to label */
246 Widget wTimeDisplayLbl0; /* Label for the time */
247 Widget wTimeDisplayLbl1; /* Actual time display */
248 Widget wTimeScrollBar; /* Time scrollbar */
249 Widget wRateSBFrame; /* Frame for the scroll bar */
250 Widget wRateRC; /* Row Col widget for Rate scroll bar */
251 Widget wRateLbl; /* Label for the scroll bar */
252 Widget wRateScrollBar; /* The Sampling Rate Scroll Bar */
253 int SampRate_value = 0; /* Current scroll bar value */
254
255 Widget wRecRateFrame; /* The record rate frame */
256 Widget wRecRateRC;
257 Widget wRecRateTgl;
258 Widget wRecRate;
259 int RecRateTgl_value = 0;
260
261 Widget wDetailsFrame; /* Frame for File Info Details */
262 Widget wDetailsRC; /* Horizontal RC for wBotLeftRC* */
263 Widget wBotLeftRC0; /* Holds labels File:, Type: etc. */
264 Widget wFileLbl0;
265 Widget wTypeLbl0;
266 Widget wDateLbl0;
267 Widget wSizeLbl0;
268 Widget wSamplingRateLbl0;
269 Widget wSamplesLbl0;
270 Widget wTimeLbl0;
271
272 Widget wBotLeftRC1; /* Holds labels to right of wBotLeftRC0 */
273 Widget wFileLbl1;
274 Widget wTypeLbl1;
275 Widget wDateLbl1;
276 Widget wSizeLbl1;
277 Widget wSamplingRateLbl1;
278 Widget wSamplesLbl1;
279 Widget wTimeLbl1;
280
281 Widget wRightRC; /* Right side Row/Column */
282 Widget wBitsFrame; /* Frame holding BitsRC */
283
284 Widget wBitsRC; /* RowColumn that holds 8, 12, & 16 bits */
285
286 Widget w8BitCB; /* 8-bit check box */
287 Widget w16BitCB; /* 16-bit check box */
288 int curBits = 0; /* The current setting */
289
290 Widget wPlayFrame; /* Frame for play buttons */
291 Widget wPlayRC; /* RC for play buttons */
292 Widget wPlayPB; /* Play push button */
293 Widget wPausePB; /* Pause push button */
294 Widget wStopPlayPB; /* StopPlay push button */
295 Widget wRestorePB; /* Restore push button */
296
297 Widget wStereoFrame; /* Frame for Stereo Toggle */
298 Widget wStereoCB; /* Stereo Check Box */
299
300 Widget wRecordFrame;
301 Widget wRecordRC;
302 Widget wRecordPB; /* Record push button */
303 Widget wStopPB; /* Stop push button */
304
305 XmString sRecorded_wav; /* XmString() of RECORD_PATH */
306
307 /* Info required by the time display */
308 #define SMPTE_FPS 25
309
310 typedef struct TimeDisplayInfo {
311 UInt32 sampling_rate;
312 UInt32 sample_num;
313 char sample_info_str[16];
314 } TimeDisplayInfo;
315
316 TimeDisplayInfo time_display;
317
318 /* Flag to show when the help text is displayed */
319 int help_text_showing = 1;
320
321 /*
322 * This is an internal conveniance routine:
323 * In order for EditRes to function, we must register all shell widgets.
324 */
325 #ifndef NO_EDITRES
326
327 static void
RegisterShellWidget(Widget w)328 RegisterShellWidget(Widget w) {
329 XtAddEventHandler(w,(EventMask)0,True,_XEditResCheckMessages,NULL);
330 }
331
332 #else
333
334 #define RegisterShellWidget(w) /* No EDITRES facility */
335
336 #endif /* ndef NO_EDITRES */
337
338 /*
339 * This function initiates the creation of an error dialog with
340 * a meaningful error text and an OK button. Note that the dialog
341 * box is not operationaly until we return back to the main loop.
342 */
343 static void
ReportError(const char * ErrMsg)344 ReportError(const char *ErrMsg) {
345 XmString s; /* XmString form of message */
346
347 if ( XtIsManaged(wErrDlg) )
348 return; /* The dialog is already in use */
349
350 /*
351 * Set the Error Dialog message text, and manage it so that it pops up:
352 */
353 s = XmStringCreateLtoR((char *)ErrMsg,XmSTRING_DEFAULT_CHARSET);/* We need an XmString */
354 XtVaSetValues(wErrDlg,XmNmessageString,s,NULL); /* Put msg in there */
355 XtManageChild(wErrDlg); /* Make it pop up */
356 XmStringFree(s); /* Release the string */
357 }
358
359 /*
360 * Error function to format a message and report it:
361 */
362 static void
v_erf(const char * format,va_list ap)363 v_erf(const char *format,va_list ap) {
364 char buf[1024];
365
366 vsprintf(buf,format,ap); /* Format the message */
367 fputs(buf,stderr); /* Send to stderr */
368 fputc('\n',stderr); /* ..with a newline */
369 ReportError(buf); /* Now popup error message if we can */
370 }
371
372 /*
373 * Format an error report:
374 */
375 void
ReportErrorf(const char * format,...)376 ReportErrorf(const char *format,...) {
377 va_list ap;
378
379 va_start(ap,format); /* Access the arguments list */
380 v_erf(format,ap); /* Go report the error */
381 va_end(ap); /* A formality for stdargs */
382 }
383
384 /*
385 * This function extracts the Record sampling rate from the text input widget:
386 */
387 static UInt32
GetRecRate(void)388 GetRecRate(void) {
389 char *pText = XmTextGetString(wRecRate); /* Get contents */
390 unsigned long ul;
391
392 if ( sscanf(pText,"%lu",&ul) != 1 ) { /* Convert to u_int */
393 if ( !*pText )
394 ReportErrorf("No sampling rate has been specified.");
395 else ReportErrorf("Record sampling rate '%s' must be an integer.",pText);
396 XtFree(pText); /* Release this now */
397 return 0UL; /* Return no sampling rate */
398 }
399 XtFree(pText); /* Release this now */
400 XmSprintfText(wRecRate,"%lu",ul); /* Reformat what we think we got */
401 return (UInt32) ul; /* Return sampling rate */
402 }
403
404 /*
405 * This callback is called when the Record Sampling Rate input text widget gets
406 * the focus. When this happens, we turn off the toggle that takes the sampling
407 * rate from the slider.
408 */
409 static void
RecRateFocus_CB(Widget w,XtPointer client_data,XtPointer call_data)410 RecRateFocus_CB(Widget w,XtPointer client_data,XtPointer call_data) {
411
412 XtVaSetValues(wRecRateTgl,XmNset,False,NULL); /* Disable toggle */
413 RecRateTgl_value = 0; /* And tracking value */
414 XmTextSetInsertionPosition(wRecRate,0); /* Cursor at start of field */
415 }
416
417 /*
418 * This callback is called when the Record Sampling Rate toggle gets altered.
419 */
420 static void
RecRateTgl_CB(Widget w,XtPointer client_data,XtPointer call_data)421 RecRateTgl_CB(Widget w,XtPointer client_data,XtPointer call_data) {
422 XmToggleButtonCallbackStruct *ptr = (XmToggleButtonCallbackStruct *)call_data;
423
424 if ( (RecRateTgl_value = ptr->set) != 0 ) /* Toggle set? */
425 XmSprintfText(wRecRate,"%d",SampRate_value); /* Yes, show rate from slider */
426 }
427
428 /*
429 * This callback is entered when the [Play] button is pressed.
430 */
431 static void
Play_CB(Widget w,XtPointer client_data,XtPointer call_data)432 Play_CB(Widget w,XtPointer client_data,XtPointer call_data) {
433 tosvr_play(0,v_erf); /* Tell server to play */
434 }
435
436 /*
437 * This callback is entered when the [Pause] button is pressed.
438 */
439 static void
Pause_CB(Widget w,XtPointer client_data,XtPointer call_data)440 Pause_CB(Widget w,XtPointer client_data,XtPointer call_data) {
441 tosvr_pause(0,v_erf); /* Tell server to pause */
442 }
443
444 /*
445 * This callback is entered when the [Stop] button is pressed.
446 */
447 static void
Stop_CB(Widget w,XtPointer client_data,XtPointer call_data)448 Stop_CB(Widget w,XtPointer client_data,XtPointer call_data) {
449 tosvr_stop(0,v_erf); /* Tell the server to stop */
450 }
451
452 /*
453 * This is the [Restore] settings button callback:
454 */
455 static void
Restore_CB(Widget w,XtPointer client_data,XtPointer call_data)456 Restore_CB(Widget w,XtPointer client_data,XtPointer call_data) {
457
458 tosvr_restore(0,v_erf); /* Tell server to "restore" */
459 XtVaSetValues(wTimeScrollBar,XmNvalue,0,NULL); /* Fix time slider */
460 time_display.sample_num = 0;
461 XmSprintfLabel(wTimeDisplayLbl1,"00:00:00.00");
462 XtVaSetValues(wRateScrollBar,XmNvalue,0,NULL); /* Fix slider */
463 SampleRateChg_Show(0); /* Update sampling rate etc. */
464 }
465
466 /*
467 * The [Record] button callback:
468 */
469 static void
Record_CB(Widget w,XtPointer client_data,XtPointer call_data)470 Record_CB(Widget w,XtPointer client_data,XtPointer call_data) {
471 Arg al[20]; /* Arg list */
472 Cardinal ac; /* Args count */
473 UInt32 RecRate = GetRecRate(); /* Sampling Rate */
474 Chan chan_mode; /* Stereo / Mono */
475 UInt16 data_bits = curBits < 8 ? 8 : curBits; /* Data bits */
476 Boolean b; /* Current setting of Stereo toggle */
477
478 if ( RecRate < 1 ) { /* We must have a sampling rate */
479 ReportErrorf("Please set a Sampling Rate\nand try again.");
480 return;
481 }
482
483 ac = 0;
484 XtSetArg(al[ac],XmNset,&b); ++ac;
485 XtGetValues(wStereoCB,al,ac); /* Get Stereo toggle setting */
486
487 chan_mode = b ? Stereo : Mono; /* Channels now known */
488
489 time_display.sampling_rate = RecRate; /* Store for use during record */
490 sprintf(time_display.sample_info_str,"%u-bit %s",
491 data_bits,
492 chan_mode == Stereo ? "stereo" : "mono");
493
494 /* Update a few fields */
495 XmSprintfLabel(wDateLbl1,""); /* Don't need for rec; blank if help msg still there */
496 XmSprintfLabel(wSamplingRateLbl1,"%lu Hz",
497 (unsigned long)RecRate);
498
499 tosvr_record(0,v_erf,chan_mode,RecRate,data_bits); /* Tell server to start recording */
500 }
501
502 /*
503 * This callback is entered when a selection has been made from the list box.
504 */
505 static void
PathSelected_CB(Widget w,XtPointer client_data,XtPointer call_data)506 PathSelected_CB(Widget w,XtPointer client_data,XtPointer call_data) {
507 XmListCallbackStruct *ptr = (XmListCallbackStruct *)call_data;
508 char *path; /* C string copy */
509
510 XmStringGetLtoR(ptr->item,XmSTRING_DEFAULT_CHARSET,&path); /* Get C string pathname */
511 tosvr_path(path,0,v_erf); /* Tell server the new path */
512 XtFree(path); /* Release this string */
513 }
514
515 /*
516 * Internal routine for updating the sampling rate display fields:
517 */
518 static void
SampleRateChg_Show(int newValue)519 SampleRateChg_Show(int newValue) {
520
521 if ( newValue < (int) DSP_MIN ) {
522 /*
523 * If the value is less than the minimum, assume no overrides here:
524 */
525 XmSprintfLabel(wRateLbl,"Sampling Rate Override: NONE");
526 SampRate_value = 0;
527 } else {
528 /*
529 * Otherwise display the overridden sampling rate:
530 */
531 XmSprintfLabel(wRateLbl,"Sampling Rate Override: %d Hz",(int)newValue);
532 SampRate_value = (int)newValue;
533 }
534
535 /*
536 * If the record toggle is set, then text box follows slider's value:
537 */
538 if ( RecRateTgl_value != 0 )
539 XmSprintfText(wRecRate,"%d",SampRate_value);
540 }
541
542 /*
543 * This callback is entered when the time has changed (via scroll bar):
544 */
545 static void
TimeChg_CB(Widget w,XtPointer client_data,XtPointer call_data)546 TimeChg_CB(Widget w,XtPointer client_data,XtPointer call_data) {
547 XmScrollBarCallbackStruct *ptr = (XmScrollBarCallbackStruct *)call_data;
548 time_display.sample_num = (UInt32)ptr->value;
549
550 /* wTimeDisplayLbl1 is updated as a result of the this server msg */
551 /* (since the server refreshes the client in response */
552 tosvr_start_sample(0,v_erf,(UInt32)ptr->value);
553 }
554
555 /*
556 * This callback is entered when the sampling rate has changed (via scroll bar):
557 */
558 static void
SampleRateChg_CB(Widget w,XtPointer client_data,XtPointer call_data)559 SampleRateChg_CB(Widget w,XtPointer client_data,XtPointer call_data) {
560 XmScrollBarCallbackStruct *ptr = (XmScrollBarCallbackStruct *)call_data;
561
562 SampleRateChg_Show(ptr->value);
563 tosvr_sampling_rate(0,v_erf,(UInt32)ptr->value);
564 }
565
566 /*
567 * This is the Stereo/Mono toggle callback:
568 */
569 static void
StereoChanged_CB(Widget w,XtPointer client_data,XtPointer call_data)570 StereoChanged_CB(Widget w,XtPointer client_data,XtPointer call_data) {
571 XmToggleButtonCallbackStruct *ptr = (XmToggleButtonCallbackStruct *)call_data;
572
573 tosvr_chan(0,v_erf,ptr->set ? Stereo : Mono); /* Tell the server about change */
574 }
575
576 /*
577 * This internal function is used to set the radio buttons:
578 */
579 static void
BitsShow(void)580 BitsShow(void) {
581 Widget wSet, wNot;
582
583 switch ( curBits ) {
584 default :
585 curBits = 8;
586 case 8 :
587 wSet = w8BitCB;
588 wNot = w16BitCB;
589 break;
590 case 16 :
591 wSet = w16BitCB;
592 wNot = w8BitCB;
593 }
594
595 XtVaSetValues(wSet,XmNset,True,NULL);
596 #if LessTif_Bug_radioBehavior
597 /*
598 * Unless I've goofed somewhere, LessTif does not properly handle radio buttons:
599 */
600 XtVaSetValues(wNot,XmNset,False,NULL);
601 #endif
602 }
603
604 /*
605 * This callback is made when the bits per sample radio buttons have changed:
606 */
607 static void
BitsChanged_CB(Widget w,XtPointer client_data,XtPointer call_data)608 BitsChanged_CB(Widget w,XtPointer client_data,XtPointer call_data) {
609 int bits = (int) (long) client_data;
610
611 tosvr_bits(0,v_erf,bits); /* Tell server the new bits setting */
612 }
613
614 /*
615 * This callback, is for the [OK] button in the About dialog:
616 */
617 static void
AboutDlgOkCB(Widget w,XtPointer client_data,XtPointer call_data)618 AboutDlgOkCB(Widget w,XtPointer client_data,XtPointer call_data) {
619 if ( XtIsManaged(wAboutDlg) )
620 XtUnmanageChild(wAboutDlg); /* Pop down the About dialog */
621 }
622
623 /*
624 * This callback is for the toggle in the Options->Debug menu selection:
625 */
626 static void
OptionsDebugCB(Widget w,XtPointer client_data,XtPointer call_data)627 OptionsDebugCB(Widget w,XtPointer client_data,XtPointer call_data) {
628 Arg al[4]; /* Argument list */
629 Cardinal ac; /* Argument count */
630
631 ac = 0;
632 XtSetArg(al[ac],XmNset,&bOptionsDebug); ++ac; /* Request Get Toggle Value */
633 XtGetValues(wOptions_DebugTgl,al,ac); /* Get value now */
634
635 cmdopt_x = bOptionsDebug != False ? 1 : 0; /* Update cmdopt_x also */
636 tosvr_debug(0,v_erf,cmdopt_x); /* And tell server the same */
637 }
638
639 /*
640 * This callback is invoked when menu item Options->Semaphore Reset is activated.
641 */
642 static void
OptionsSemResetCB(Widget w,XtPointer client_data,XtPointer call_data)643 OptionsSemResetCB(Widget w,XtPointer client_data,XtPointer call_data) {
644 tosvr_semreset(0,v_erf); /* Request server semaphore reset */
645 }
646
647 /*
648 * This callback is invoked by the Help->About menu selection:
649 */
650 static void
AboutCB(Widget w,XtPointer client_data,XtPointer call_data)651 AboutCB(Widget w,XtPointer client_data,XtPointer call_data) {
652
653 if ( XtIsManaged(wAboutDlg) ) /* For safety */
654 return; /* The dialog is already in use */
655
656 XtVaSetValues(wAboutDlg,XmNmessageString,sAboutDlgMessage,NULL);/* Supply message */
657 XtManageChild(wAboutDlg); /* Pop up the dialog now */
658 }
659
660 /*
661 * This callback is invoked by Menu File->Select, to pop up the file selections dialog box.
662 */
663 static void
PopupSelectFilesCB(Widget w,XtPointer client_data,XtPointer call_data)664 PopupSelectFilesCB(Widget w,XtPointer client_data,XtPointer call_data) {
665
666 if ( XtIsManaged(wFilesDlg) ) /* Already managed? */
667 return; /* yes, just return */
668 XtManageChild(wFilesDlg); /* else, make visible now */
669 }
670
671 /*
672 * This callback is invoked by menu File->Save As.., to start a dialog to allow
673 * saving the recorded.wav file somewhere else.
674 */
675 static void
PopupSaveAsCB(Widget w,XtPointer client_data,XtPointer call_data)676 PopupSaveAsCB(Widget w,XtPointer client_data,XtPointer call_data) {
677
678 if ( XtIsManaged(wSaveAsDlg) ) /* Is this already managed? */
679 return; /* Yes, just exit */
680 XtManageChild(wSaveAsDlg); /* else manage it now */
681 }
682
683 /*
684 * This callback is invoked by Menu File->Exit, to do the fatal thing..
685 */
686 static void
ExitCB(Widget w,XtPointer client_data,XtPointer call_data)687 ExitCB(Widget w,XtPointer client_data,XtPointer call_data) {
688
689 exit(0); /* This invokes some atexit() calls */
690 }
691
692 /*
693 * This callback is called when the user presses [OK] in the error dialog.
694 */
695 static void
ExitDlgCB(Widget w,XtPointer client_data,XtPointer call_data)696 ExitDlgCB(Widget w,XtPointer client_data,XtPointer call_data) {
697
698 if ( XtIsManaged(wErrDlg) ) /* Is it still managed? */
699 XtUnmanageChild(wErrDlg); /* Yes, unmanage it now */
700 }
701
702 /*
703 * This internal function is used to add a new pathname to the list box:
704 */
705 static void
PutFileSelection(XmString xms_pathname)706 PutFileSelection(XmString xms_pathname) {
707
708 if ( XmListItemExists(wSelectionsListBox,xms_pathname) == False )
709 XmListAddItemUnselected(wSelectionsListBox,xms_pathname,0); /* Append to list */
710 }
711
712 /*
713 * This callback is invoked by the file selection dialog, to add another pathname to the
714 * list box.
715 */
716 static void
FilesDlgOKCB(Widget w,XtPointer client_data,XtPointer call_data)717 FilesDlgOKCB(Widget w,XtPointer client_data,XtPointer call_data) {
718 XmFileSelectionBoxCallbackStruct *ptr = (XmFileSelectionBoxCallbackStruct *) call_data;
719
720 if ( !XmStringEmpty(ptr->value) )
721 PutFileSelection(ptr->value); /* Put this string into the selection box */
722 }
723
724 /*
725 * This call back pops down the dialog box (for any dialog). The widget used for the pop down
726 * is expected to be passed in the client_data argument.
727 */
728 static void
DialogCancelCB(Widget w,XtPointer client_data,XtPointer call_data)729 DialogCancelCB(Widget w,XtPointer client_data,XtPointer call_data) {
730 Widget wDlg = (Widget) client_data; /* Get dialog widget */
731
732 if ( XtIsManaged(wDlg) ) /* Is it still managed? */
733 XtUnmanageChild(wDlg); /* yep, unmanage it now */
734 }
735
736 /*
737 * This function dispatches wavplay server response messages:
738 */
739 static void
DispatchMsg(SVRMSG * msg)740 DispatchMsg(SVRMSG *msg) {
741 XmString s;
742 double seconds;
743
744 /* Erase the help text on receipt of first server message */
745 if (help_text_showing) {
746 help_text_showing = 0;
747 XmSprintfLabel(wFileLbl1,"");
748 XmSprintfLabel(wTypeLbl1,"");
749 XmSprintfLabel(wDateLbl1,"");
750 XmSprintfLabel(wSizeLbl1,"");
751 }
752
753 if ( msg->msg_type == ToClnt_Bits ) {
754 /*
755 * Server has responded with a new bits per sample setting:
756 */
757 if ( msg->u.toclnt_bits.DataBits != 0 ) { /* Ignore if value is zero.. */
758 curBits = msg->u.toclnt_bits.DataBits; /* Server indicates new setting */
759 BitsShow(); /* Update our client displays */
760 }
761 } else if ( msg->msg_type == ToClnt_Settings ) {
762 /*
763 * A bunch of settings have changed, according to the server:
764 */
765 if ( msg->u.toclnt_settings.DataBits != 0 ) { /* Ignore bits if zero.. */
766 curBits = msg->u.toclnt_settings.DataBits; /* Current server bit setting */
767 BitsShow(); /* Reflect in controls */
768 }
769 XtVaSetValues(wStereoCB,XmNset,
770 msg->u.toclnt_settings.Channels == Stereo ? True : False,
771 NULL); /* Show stereo setting */
772 /*
773 * Update label text:
774 */
775 XmSprintfLabel(wSamplingRateLbl1,"%lu Hz%s",
776 (unsigned long)msg->u.toclnt_settings.SamplingRate,
777 msg->u.toclnt_settings.bOvrSampling ? "*" : "");
778
779 if ( msg->u.toclnt_settings.bOvrSampling != 0 ) {
780 SampleRateChg_Show((int)msg->u.toclnt_settings.SamplingRate);
781 XtVaSetValues(wRateScrollBar,XmNvalue,msg->u.toclnt_settings.SamplingRate,NULL);
782 } else {
783 SampleRateChg_Show(0);
784 XtVaSetValues(wRateScrollBar,XmNvalue,0,NULL);
785 }
786
787 /* Store this for the time display */
788 time_display.sampling_rate = (int)msg->u.toclnt_settings.SamplingRate;
789
790 /*
791 * Currently, this value should be "PCM" (wav file type)
792 */
793 msg->u.toclnt_settings.WavType[sizeof msg->u.toclnt_settings.WavType - 1] = 0;
794 XmSprintfLabel(wTypeLbl1,"%s",msg->u.toclnt_settings.WavType);
795
796 /*
797 * Update samples, bits and channels:
798 */
799 sprintf(time_display.sample_info_str,"%u-bit%s %s%s",
800 (unsigned)msg->u.toclnt_settings.DataBits,
801 msg->u.toclnt_settings.bOvrBits ? "*" : "",
802 msg->u.toclnt_settings.Channels == Stereo ? "stereo" : "mono",
803 msg->u.toclnt_settings.bOvrMode ? "*" : "");
804
805 XmSprintfLabel(wSamplesLbl1,"%lu %s",
806 (unsigned long)msg->u.toclnt_settings.Samples,
807 time_display.sample_info_str);
808
809 /*
810 * Update duration time:
811 */
812 XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
813 (double)msg->u.toclnt_settings.Samples
814 / (double)msg->u.toclnt_settings.SamplingRate);
815
816 /* Update time display if required */
817 seconds = time_display.sample_num / (double)time_display.sampling_rate;
818 XmSprintfLabel(wTimeDisplayLbl1,"%02d:%02d:%02d.%02d",
819 (int)seconds/3600,((int)seconds/60)%60,
820 (int)seconds%60,
821 (int)((seconds-(int)seconds)*SMPTE_FPS));
822 XtVaSetValues(wTimeScrollBar,XmNincrement,
823 time_display.sampling_rate/SMPTE_FPS,
824 XmNpageIncrement,time_display.sampling_rate,NULL);
825
826 } else if ( msg->msg_type == ToClnt_WavInfo ) {
827
828 if ( msg->u.toclnt_wavinfo.Errno != 0 ) {
829 /*
830 * Format an error message where the pathname should go:
831 */
832 XmSprintfLabel(wFileLbl1,"<%s>",sys_errlist[msg->u.toclnt_wavinfo.Errno]);
833 XmSprintfLabel(wDateLbl1,"");
834 XmSprintfLabel(wSizeLbl1,"");
835 XmSprintfLabel(wSamplesLbl1,"");
836 XmSprintfLabel(wTimeLbl1,"");
837 } else {
838 if ( msg->u.toclnt_wavinfo.wavinfo.DataBits != 0 ) {
839 curBits = msg->u.toclnt_wavinfo.wavinfo.DataBits; /* Current server bit setting */
840 BitsShow(); /* Reflect in controls */
841 }
842 XtVaSetValues(wStereoCB,XmNset,
843 msg->u.toclnt_wavinfo.wavinfo.Channels == Stereo ? True : False,
844 NULL); /* Show stereo setting */
845 /*
846 * Update label text:
847 */
848 XmSprintfLabel(wSamplingRateLbl1,"%lu Hz",
849 (unsigned long)msg->u.toclnt_wavinfo.wavinfo.SamplingRate);
850
851 XmSprintfLabel(wTypeLbl1,"%s","PCM"); /* For now... */
852
853 XmSprintfLabel(wSamplesLbl1,"%lu %u-bit %s",
854 (unsigned long)msg->u.toclnt_wavinfo.wavinfo.Samples,
855 (unsigned)msg->u.toclnt_wavinfo.wavinfo.DataBits,
856 msg->u.toclnt_wavinfo.wavinfo.Channels == Stereo ? "stereo" : "mono");
857
858 XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
859 (double)msg->u.toclnt_wavinfo.wavinfo.Samples
860 / (double)msg->u.toclnt_wavinfo.wavinfo.SamplingRate);
861 /*
862 * Update time slider details:
863 */
864 time_display.sample_num = 0;
865 time_display.sampling_rate = msg->u.toclnt_wavinfo.wavinfo.SamplingRate;
866 XmSprintfLabel(wTimeDisplayLbl1,"00:00:00.00");
867 {
868 Arg al[5];
869 int size = msg->u.toclnt_wavinfo.wavinfo.Samples;
870
871 XtSetArg(al[0],XmNmaximum,size + (size/10));
872 XtSetArg(al[1],XmNsliderSize,size/10);
873 XtSetArg(al[2],XmNincrement,time_display.sampling_rate/SMPTE_FPS);
874 XtSetArg(al[3],XmNpageIncrement,time_display.sampling_rate);
875 XtSetArg(al[4],XmNvalue,0);
876 XtSetValues(wTimeScrollBar,al,5);
877 }
878 }
879 } else if ( msg->msg_type == ToClnt_Stat ) {
880 /*
881 * Update stat() info:
882 */
883 if ( msg->u.toclnt_stat.Errno > 0 ) {
884 /*
885 * Show stat() error:
886 */
887 XmSprintfLabel(wFileLbl1,"<%s>",sys_errlist[msg->u.toclnt_stat.Errno]);
888 XmSprintfLabel(wDateLbl1,"");
889 XmSprintfLabel(wSizeLbl1,"");
890 XmSprintfLabel(wSamplesLbl1,"");
891 XmSprintfLabel(wTimeLbl1,"");
892 } else {
893 /*
894 * Show stat() info:
895 */
896 XmSprintfLabel(wDateLbl1,"%s",StrDate(msg->u.toclnt_stat.sbuf.st_mtime));
897 XmSprintfLabel(wSizeLbl1,"%lu bytes",(unsigned long)msg->u.toclnt_stat.sbuf.st_size);
898 }
899
900 } else if ( msg->msg_type == ToClnt_Path ) {
901 /*
902 * Update pathname in panel display
903 */
904 msg->u.toclnt_path.path[msg->bytes] = 0;
905 XmSprintfLabel(wFileLbl1,"%s",msg->u.toclnt_path.path);
906
907 /*
908 * Highlight the pathname in the list box if we can (it should already be there)
909 */
910 s = XmStringCreate(msg->u.toclnt_path.path,XmSTRING_DEFAULT_CHARSET);
911 XmListSelectItem(wSelectionsListBox,s,False);
912 XmStringFree(s);
913
914 if ( !bRecorded && !strcmp(msg->u.toclnt_path.path,RECORD_PATH) ) {
915 s = XmStringCreateLtoR(RECORD_PATH,XmSTRING_DEFAULT_CHARSET);
916 PutFileSelection(s);
917 XmStringFree(s);
918 bRecorded = 1;
919 }
920 } else if ( msg->msg_type == ToClnt_PlayState ) {
921 /*
922 * Update time, duration and samples left during playback.
923 */
924 XmSprintfLabel(wSamplesLbl1,"%lu %s",
925 (unsigned long)msg->u.toclnt_playstate.SamplesLeft,
926 time_display.sample_info_str);
927
928 XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
929 (double)msg->u.toclnt_playstate.SamplesLeft
930 / (double)time_display.sampling_rate);
931 time_display.sample_num = msg->u.toclnt_playstate.CurrentSample;
932 seconds = time_display.sample_num / (double)time_display.sampling_rate;
933 XmSprintfLabel(wTimeDisplayLbl1,"%02d:%02d:%02d.%02d",
934 (int)seconds/3600,((int)seconds/60)%60,
935 (int)seconds%60,
936 (int)((seconds-(int)seconds)*SMPTE_FPS));
937 XtVaSetValues(wTimeScrollBar,XmNvalue,time_display.sample_num);
938 } else if ( msg->msg_type == ToClnt_RecState ) {
939 /*
940 * Update size, number of samples and duration during record.
941 */
942 XmSprintfLabel(wSizeLbl1,"%lu bytes",(unsigned long)msg->u.toclnt_recstate.bytes_written);
943 XmSprintfLabel(wSamplesLbl1,"%lu %s",
944 (unsigned long)msg->u.toclnt_recstate.num_samples,
945 time_display.sample_info_str);
946 XmSprintfLabel(wTimeLbl1,"%.2lf seconds",
947 (double)msg->u.toclnt_recstate.num_samples
948 / (double)time_display.sampling_rate);
949 } else if ( msg->msg_type == ToClnt_ErrMsg )
950 ReportError(msg->u.toclnt_errmsg.msg);
951 }
952
953 /*
954 * This timed procedure just looks for incoming messages from the server:
955 */
956 static void
TimerProc1(XtPointer client_data,XtIntervalId * timer_id)957 TimerProc1(XtPointer client_data,XtIntervalId *timer_id) {
958 SVRMSG msg;
959 int z;
960
961 /*
962 * Process messages until we run out of messages:
963 */
964 rpt: while ( (z = MsgFromServer(svrIPC,&msg,IPC_NOWAIT)) == 0 )
965 DispatchMsg(&msg); /* Act upon the server message */
966
967 if ( z != 0 && errno != ENOMSG ) {
968 ReportErrorf("%s: Internal error from MsgFromServer()",sys_errlist[errno]);
969 goto rpt; /* There may be more messages */
970 }
971
972 XtAppAddTimeOut(aContext,TIMER1_MS,TimerProc1,NULL);
973 }
974
975 /*
976 * The main program needs no introduction:
977 */
978 int
main(int argc,char ** argv)979 main(int argc,char **argv) {
980 Arg al[20]; /* Arg list */
981 Cardinal ac; /* Args count */
982 char *cp;
983
984 /*
985 * Set environmental values:
986 */
987 if ( (cp = getenv("WAVPLAYPATH")) != NULL )
988 env_WAVPLAYPATH = cp; /* Environment overrides compiled in default for WAVPLAYPATH */
989
990 if ( (cp = getenv("AUDIODEV")) != NULL )
991 env_AUDIODEV = cp; /* Environment overrides compiled in default for AUDIODEV */
992
993 if ( (cp = getenv("AUDIOLCK")) == NULL || sscanf(cp,"%lX",&env_AUDIOLCK) != 1 )
994 env_AUDIOLCK = AUDIOLCK; /* Use compiled in default, if no environment, or its bad */
995
996 /*
997 * CreateApplication Context:
998 */
999 ac = 0;
1000 XtSetArg(al[ac],XmNtitle,"X LessTif WAV Play version " WAVPLAY_VERSION); ++ac;
1001 wParent = XtAppInitialize(&aContext,
1002 "xltwavplay", /* app. class */
1003 (XrmOptionDescList) NULL, 0, /* options */
1004 &argc, argv, /* cmd line */
1005 fallback_resources, /* fallback resources */
1006 al,ac); /* hard coded resources */
1007
1008 /*
1009 * Create XmString() version of RECORD_PATH for list box processing:
1010 */
1011 sRecorded_wav = XmStringCreate(RECORD_PATH,XmSTRING_DEFAULT_CHARSET);
1012
1013 /*
1014 * Create Main widget:
1015 */
1016 ac = 0;
1017 wMain = XmCreateMainWindow(wParent,"main",al,ac);
1018 XtManageChild(wMain);
1019 RegisterShellWidget(wMain); /* This is for Editres */
1020
1021 /*
1022 * Create the menu bar at the top of the main window:
1023 */
1024 ac = 0;
1025 wMenuBar = XmCreateMenuBar(wMain,"menubar",al,ac);
1026 XtManageChild(wMenuBar);
1027
1028 /*
1029 * Create the FILE menu in the menu bar:
1030 */
1031 CreateMenu(wMenuBar,&wFileMenu,"filemenu",&wFileMenuCascade,"filemenucascade",
1032 "select_files",'M',PopupSelectFilesCB,&wSelect, /* File->Select */
1033 "save_as",'M',PopupSaveAsCB,&wSaveAs, /* File->Save As.. */
1034 "exit",'M',ExitCB,&wExit, /* File->Exit */
1035 NULL);
1036
1037 /*
1038 * Create the OPTIONS menu in the menu bar:
1039 */
1040 CreateMenu(wMenuBar,&wOptionsMenu,"optsmenu",&wOptionsMenuCascade,"optsmenucascade",
1041 "debug",'T',OptionsDebugCB,&wOptions_DebugTgl, /* Options->Debug (toggle) */
1042 "sem_reset",'M',OptionsSemResetCB,&wOptions_SemReset, /* Options->Semaphore Reset */
1043 NULL);
1044
1045 /*
1046 * Create the HELP menu in the menu bar:
1047 */
1048 CreateMenu(wMenuBar,&wHelpMenu,"helpmenu",&wHelpMenuCascade,"helpmenucascade",
1049 "about",'M',AboutCB,&wAbout, /* Help->About */
1050 NULL);
1051
1052 /*
1053 * Create the error message dialog:
1054 */
1055 sErrDlgTitle = XmStringCreate("X LessTif WAV Play : Error Message",XmSTRING_DEFAULT_CHARSET);
1056 ac = 0;
1057 XtSetArg(al[ac],XmNdefaultPosition,FALSE); ac++;
1058 XtSetArg(al[ac],XmNdialogStyle,XmDIALOG_APPLICATION_MODAL); ac++;
1059 XtSetArg(al[ac],XmNtitle,"Error Report"); ac++;
1060 XtSetArg(al[ac],XmNdefaultButtonType,XmDIALOG_OK_BUTTON); ++ac;
1061 XtSetArg(al[ac],XmNmessageAlignment,XmALIGNMENT_CENTER); ++ac;
1062 XtSetArg(al[ac],XmNdialogTitle,sErrDlgTitle); ++ac;
1063 XtSetArg(al[ac],XmNallowResize,FALSE); ++ac;
1064 wErrDlg = XmCreateErrorDialog(wParent,"ErrDlg",al,ac);
1065 XtAddCallback(wErrDlg,XmNokCallback,ExitDlgCB,NULL);
1066 XtUnmanageChild(XmMessageBoxGetChild(wErrDlg,XmDIALOG_CANCEL_BUTTON));
1067 XtUnmanageChild(XmMessageBoxGetChild(wErrDlg,XmDIALOG_HELP_BUTTON));
1068 RegisterShellWidget(wErrDlg);
1069
1070 /*
1071 * Create About Dialog Message String:
1072 */
1073 sAboutDlgTitle = XmStringCreate("About X LessTif WAV Play "WAVPLAY_VERSION,XmSTRING_DEFAULT_CHARSET);
1074 sAboutDlgMessage = XmStringCreateLtoR(
1075 "xltwavplay version " WAVPLAY_VERSION "\n"
1076 "was written by and is\n"
1077 "Copyright (C) 1997 by Warren W. Gay VE3WWG\n"
1078 "---\n"
1079 "Special thanks to Erik de Castro Lopo (erikd@zip.com.au)\n"
1080 "for his contributed WAV header code\n"
1081 "(files wavfile.c and wavfile.h)\n"
1082 "---\n"
1083 "Thanks also go to Andre Fuechsel for his original\n"
1084 "wavplay that inspired the current work (though\n"
1085 "this version does not contain his code)\n"
1086 "---\n"
1087 "Many thanks to the LINUX folks,\n"
1088 "the XFree86 folks,\n"
1089 "and the LessTif group\n"
1090 "for making this application possible.\n"
1091 "---\n"
1092 "This program comes with\n"
1093 "ABSOLUTELY NO WARRANTY.",
1094 XmSTRING_DEFAULT_CHARSET);
1095
1096 /*
1097 * Create the About Dialog box:
1098 */
1099 ac = 0;
1100 XtSetArg(al[ac],XmNdefaultPosition,FALSE); ac++;
1101 XtSetArg(al[ac],XmNtitle,"Error Report"); ac++;
1102 XtSetArg(al[ac],XmNdefaultButtonType,XmDIALOG_OK_BUTTON); ++ac;
1103 XtSetArg(al[ac],XmNmessageAlignment,XmALIGNMENT_CENTER); ++ac;
1104 XtSetArg(al[ac],XmNdialogTitle,sAboutDlgTitle); ++ac;
1105 XtSetArg(al[ac],XmNallowResize,FALSE); ++ac;
1106 wAboutDlg = XmCreateMessageDialog(wParent,"AboutDlg",al,ac);
1107 XtAddCallback(wAboutDlg,XmNokCallback,AboutDlgOkCB,NULL);
1108 XtUnmanageChild(XmMessageBoxGetChild(wAboutDlg,XmDIALOG_CANCEL_BUTTON));
1109 XtUnmanageChild(XmMessageBoxGetChild(wAboutDlg,XmDIALOG_HELP_BUTTON));
1110 RegisterShellWidget(wAboutDlg);
1111
1112 /*
1113 * Create Files Selection Dialog:
1114 */
1115 ac = 0;
1116 XtSetArg(al[ac],XmNfileTypeMask,XmFILE_REGULAR); ++ac;
1117 wFilesDlg = XmCreateFileSelectionDialog(wParent,"FilesDlg",al,ac);
1118 XtAddCallback(wFilesDlg,XmNokCallback,FilesDlgOKCB,NULL);
1119 XtAddCallback(wFilesDlg,XmNcancelCallback,DialogCancelCB,(XtPointer)wFilesDlg);
1120 XtUnmanageChild(XmFileSelectionBoxGetChild(wFilesDlg,XmDIALOG_HELP_BUTTON));
1121 RegisterShellWidget(wFilesDlg);
1122
1123 /*
1124 * Create "Save As..." Dialog
1125 */
1126 ac = 0;
1127 XtSetArg(al[ac],XmNfileTypeMask,XmFILE_REGULAR); ++ac;
1128 wSaveAsDlg = XmCreateFileSelectionDialog(wParent,"SaveAsDlg",al,ac);
1129 XtAddCallback(wSaveAsDlg,XmNokCallback,SaveAsDlgOKCB,NULL);
1130 XtAddCallback(wSaveAsDlg,XmNcancelCallback,DialogCancelCB,(XtPointer)wSaveAsDlg);
1131 XtUnmanageChild(XmFileSelectionBoxGetChild(wSaveAsDlg,XmDIALOG_HELP_BUTTON));
1132 RegisterShellWidget(wSaveAsDlg);
1133
1134 /*
1135 * Create the main form widget:
1136 */
1137 ac = 0;
1138 wMainForm = XmCreateForm(wMain,"main_form",al,ac);
1139 XtManageChild(wMainForm);
1140
1141 ac = 0;
1142 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1143 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1144 wLeftRC = XmCreateRowColumn(wMainForm,"LeftRC",al,ac);
1145 XtManageChild(wLeftRC);
1146
1147 ac = 0;
1148 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1149 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1150 wListFrame = XmCreateFrame(wLeftRC,"list_frame",al,ac);
1151 XtManageChild(wListFrame);
1152
1153 ac = 0;
1154 wListForm = XmCreateForm(wListFrame,"list_form",al,ac);
1155 XtManageChild(wListForm);
1156
1157 /*
1158 * Create the Selections List Box Label:
1159 */
1160 ac = 0;
1161 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1162 wSelectionsLbl = XmCreateLabel(wListForm,"listbox_label",al,ac);
1163 XtManageChild(wSelectionsLbl);
1164
1165 /*
1166 * Create the Selections List Box:
1167 */
1168 ac = 0;
1169 XtSetArg(al[ac],XmNvisibleItemCount,7); ++ac;
1170 XtSetArg(al[ac],XmNscrollingPolicy,XmSTATIC); ++ac;
1171 XtSetArg(al[ac],XmNvisualPolicy,XmCONSTANT); ++ac;
1172 XtSetArg(al[ac],XmNscrolledWindowMarginHeight,6); ++ac;
1173 XtSetArg(al[ac],XmNscrolledWindowMarginWidth,6); ++ac;
1174 XtSetArg(al[ac],XmNselectionPolicy,XmSINGLE_SELECT); ++ac;
1175 XtSetArg(al[ac],XmNwidth,420); ++ac;
1176 wSelectionsListBox = XmCreateScrolledList(wListForm,"lstbox",al,ac);
1177 XtManageChild(wSelectionsListBox);
1178 XtAddCallback(wSelectionsListBox,XmNsingleSelectionCallback,PathSelected_CB,NULL);
1179
1180 XtVaSetValues(wSelectionsLbl,
1181 XmNtopAttachment,XmATTACH_FORM,
1182 XmNleftAttachment,XmATTACH_FORM,
1183 NULL);
1184
1185 XtVaSetValues(XtParent(wSelectionsListBox),
1186 XmNtopAttachment,XmATTACH_WIDGET,XmNtopWidget,wSelectionsLbl,
1187 XmNleftAttachment,XmATTACH_FORM,
1188 NULL);
1189
1190 ac = 0;
1191 XtSetArg(al[ac],XmNmarginHeight,12); ++ac;
1192 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1193 wDetailsFrame = XmCreateFrame(wLeftRC,"DetailsFrame",al,ac);
1194 XtManageChild(wDetailsFrame);
1195
1196 ac = 0;
1197 XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1198 wDetailsRC = XmCreateRowColumn(wDetailsFrame,"DetailsRC",al,ac);
1199 XtManageChild(wDetailsRC);
1200
1201 ac = 0;
1202 XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1203 XtSetArg(al[ac],XmNentryAlignment,XmALIGNMENT_END); ++ac;
1204 wBotLeftRC0 = XmCreateRowColumn(wDetailsRC,"BotLeftRC0",al,ac);
1205 XtManageChild(wBotLeftRC0);
1206
1207 ac = 0;
1208 XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1209 XtSetArg(al[ac],XmNentryAlignment,XmALIGNMENT_BEGINNING); ++ac;
1210 wBotLeftRC1 = XmCreateRowColumn(wDetailsRC,"BotLeftRC1",al,ac);
1211 XtManageChild(wBotLeftRC1);
1212
1213 ac = 0;
1214 wFileLbl0 = XmCreateLabel(wBotLeftRC0,"FileLbl0",al,ac);
1215 XtManageChild(wFileLbl0);
1216
1217 ac = 0;
1218 wTypeLbl0 = XmCreateLabel(wBotLeftRC0,"TypeLbl0",al,ac);
1219 XtManageChild(wTypeLbl0);
1220
1221 ac = 0;
1222 wDateLbl0 = XmCreateLabel(wBotLeftRC0,"DateLbl0",al,ac);
1223 XtManageChild(wDateLbl0);
1224
1225 ac = 0;
1226 wSizeLbl0 = XmCreateLabel(wBotLeftRC0,"SizeLbl0",al,ac);
1227 XtManageChild(wSizeLbl0);
1228
1229 ac = 0;
1230 wSamplingRateLbl0 = XmCreateLabel(wBotLeftRC0,"SamplingRateLbl0",al,ac);
1231 XtManageChild(wSamplingRateLbl0);
1232
1233 ac = 0;
1234 wSamplesLbl0 = XmCreateLabel(wBotLeftRC0,"SamplesLbl0",al,ac);
1235 XtManageChild(wSamplesLbl0);
1236
1237 ac = 0;
1238 wTimeLbl0 = XmCreateLabel(wBotLeftRC0,"TimeLbl0",al,ac);
1239 XtManageChild(wTimeLbl0);
1240
1241 ac = 0;
1242 wFileLbl1 = XmCreateLabel(wBotLeftRC1,"FileLbl1",al,ac);
1243 XtManageChild(wFileLbl1);
1244
1245 ac = 0;
1246 wTypeLbl1 = XmCreateLabel(wBotLeftRC1,"TypeLbl1",al,ac);
1247 XtManageChild(wTypeLbl1);
1248
1249 ac = 0;
1250 wDateLbl1 = XmCreateLabel(wBotLeftRC1,"DateLbl1",al,ac);
1251 XtManageChild(wDateLbl1);
1252
1253 ac = 0;
1254 wSizeLbl1 = XmCreateLabel(wBotLeftRC1,"SizeLbl1",al,ac);
1255 XtManageChild(wSizeLbl1);
1256
1257 ac = 0;
1258 wSamplingRateLbl1 = XmCreateLabel(wBotLeftRC1,"SamplingRateLbl1",al,ac);
1259 XtManageChild(wSamplingRateLbl1);
1260
1261 ac = 0;
1262 wSamplesLbl1 = XmCreateLabel(wBotLeftRC1,"SamplesLbl1",al,ac);
1263 XtManageChild(wSamplesLbl1);
1264
1265 ac = 0;
1266 wTimeLbl1 = XmCreateLabel(wBotLeftRC1,"TimeLbl1",al,ac);
1267 XtManageChild(wTimeLbl1);
1268
1269 /*
1270 * Create the time scroll bar:
1271 */
1272 ac = 0;
1273 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1274 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1275 wTimeFrame = XmCreateFrame(wLeftRC,"timeframe",al,ac);
1276 XtManageChild(wTimeFrame);
1277
1278 ac = 0;
1279 wTimeRC = XmCreateRowColumn(wTimeFrame,"time_rolcol",al,ac);
1280 XtManageChild(wTimeRC);
1281
1282 ac = 0;
1283 XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1284 wTimeDisplayRC = XmCreateRowColumn(wTimeRC,"timedisplay_rolcol",al,ac);
1285 XtManageChild(wTimeDisplayRC);
1286
1287 ac = 0;
1288 wTimeDisplayLbl0 = XmCreateLabel(wTimeDisplayRC,"TimeDisplayLbl0",al,ac);
1289 XtManageChild(wTimeDisplayLbl0);
1290
1291 ac = 0;
1292 wTimeDisplayLbl1 = XmCreateLabel(wTimeDisplayRC,"TimeDisplayLbl1",al,ac);
1293 XtManageChild(wTimeDisplayLbl1);
1294
1295 ac = 0;
1296 XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1297 XtSetArg(al[ac],XmNminimum,0); ++ac;
1298 XtSetArg(al[ac],XmNsliderSize,4000); ++ac;
1299 XtSetArg(al[ac],XmNmaximum,0+4000); ++ac;
1300 XtSetArg(al[ac],XmNincrement,1); ++ac;
1301 wTimeScrollBar = XmCreateScrollBar(wTimeRC,"time_scrollbar",al,ac);
1302 XtManageChild(wTimeScrollBar);
1303 XtAddCallback(wTimeScrollBar,XmNvalueChangedCallback,TimeChg_CB,NULL);
1304
1305 /*
1306 * Create the sampling rate scroll bar:
1307 */
1308 ac = time_display.sample_num = 0;
1309 time_display.sampling_rate = 1;
1310 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1311 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1312 wRateSBFrame = XmCreateFrame(wLeftRC,"rateSBframe",al,ac);
1313 XtManageChild(wRateSBFrame);
1314
1315 ac = 0;
1316 wRateRC = XmCreateRowColumn(wRateSBFrame,"rate_rolcol",al,ac);
1317 XtManageChild(wRateRC);
1318
1319 ac = 0;
1320 wRateLbl = XmCreateLabel(wRateRC,"rate_lbl",al,ac);
1321 XtManageChild(wRateLbl);
1322
1323 ac = 0;
1324 XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1325 XtSetArg(al[ac],XmNminimum,0); ++ac; /* DSP_MIN is actual min */
1326 XtSetArg(al[ac],XmNsliderSize,4000); ++ac;
1327 XtSetArg(al[ac],XmNmaximum,DSP_MAX+4000); ++ac;
1328 XtSetArg(al[ac],XmNincrement,100); ++ac;
1329 wRateScrollBar = XmCreateScrollBar(wRateRC,"rate_scrollbar",al,ac);
1330 XtManageChild(wRateScrollBar);
1331 XtAddCallback(wRateScrollBar,XmNvalueChangedCallback,SampleRateChg_CB,NULL);
1332
1333 ac = 0;
1334 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1335 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1336 wRecRateFrame = XmCreateFrame(wRateRC,"rec_rate_frame",al,ac);
1337 XtManageChild(wRecRateFrame);
1338
1339 ac = 0;
1340 XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ++ac;
1341 XtSetArg(al[ac],XmNpacking,XmPACK_COLUMN); ++ac;
1342 XtSetArg(al[ac],XmNnumColumns,1); ++ac;
1343 wRecRateRC = XmCreateRowColumn(wRecRateFrame,"rec_rate_rc",al,ac);
1344 XtManageChild(wRecRateRC);
1345
1346 ac = 0;
1347 wRecRateTgl = XmCreateToggleButton(wRecRateRC,"rec_rate_tgl",al,ac);
1348 XtManageChild(wRecRateTgl);
1349 XtAddCallback(wRecRateTgl,XmNvalueChangedCallback,RecRateTgl_CB,NULL);
1350
1351 ac = 0;
1352 XtSetArg(al[ac],XmNeditable,True); ++ac;
1353 wRecRate = XmCreateText(wRecRateRC,"rec_rate",al,ac);
1354 XtManageChild(wRecRate);
1355 XtAddCallback(wRecRate,XmNfocusCallback,RecRateFocus_CB,NULL);
1356
1357 /*
1358 * Now build the right hand side:
1359 */
1360 ac = 0;
1361 XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1362 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1363 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1364 XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1365 wRightRC = XmCreateRowColumn(wMainForm,"RightRC",al,ac);
1366 XtManageChild(wRightRC);
1367
1368 ac = 0;
1369 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1370 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1371 wBitsFrame = XmCreateFrame(wRightRC,"BitsFrame",al,ac);
1372 XtManageChild(wBitsFrame);
1373
1374 ac = 0;
1375 XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1376 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1377 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1378 XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1379 XtSetArg(al[ac],XmNradioBehavior,True); ++ac;
1380 wBitsRC = XmCreateRowColumn(wBitsFrame,"BitsRC",al,ac);
1381 XtManageChild(wBitsRC);
1382
1383 ac = 0;
1384 w8BitCB = XmCreateToggleButton(wBitsRC,"8Bit",al,ac);
1385 XtManageChild(w8BitCB);
1386 XtAddCallback(w8BitCB,XmNvalueChangedCallback,BitsChanged_CB,(XtPointer)8UL);
1387
1388 ac = 0;
1389 w16BitCB = XmCreateToggleButton(wBitsRC,"16Bit",al,ac);
1390 XtManageChild(w16BitCB);
1391 XtAddCallback(w16BitCB,XmNvalueChangedCallback,BitsChanged_CB,(XtPointer)16UL);
1392
1393 ac = 0;
1394 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1395 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1396 wPlayFrame = XmCreateFrame(wRightRC,"PlayFrame",al,ac);
1397 XtManageChild(wPlayFrame);
1398
1399 ac = 0;
1400 XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1401 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1402 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1403 XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1404 wPlayRC = XmCreateRowColumn(wPlayFrame,"PlayRC",al,ac);
1405 XtManageChild(wPlayRC);
1406
1407 ac = 0;
1408 wPlayPB = XmCreatePushButton(wPlayRC,"Play",al,ac);
1409 XtManageChild(wPlayPB);
1410 XtAddCallback(wPlayPB,XmNactivateCallback,Play_CB,NULL);
1411
1412 ac = 0;
1413 wPausePB = XmCreatePushButton(wPlayRC,"Pause",al,ac);
1414 XtManageChild(wPausePB);
1415 XtAddCallback(wPausePB,XmNactivateCallback,Pause_CB,NULL);
1416
1417 ac = 0;
1418 wStopPlayPB = XmCreatePushButton(wPlayRC,"Stop",al,ac);
1419 XtManageChild(wStopPlayPB);
1420 XtAddCallback(wStopPlayPB,XmNactivateCallback,Stop_CB,NULL);
1421
1422 ac = 0;
1423 wRestorePB = XmCreatePushButton(wPlayRC,"Restore",al,ac);
1424 XtManageChild(wRestorePB);
1425 XtAddCallback(wRestorePB,XmNactivateCallback,Restore_CB,NULL);
1426
1427 ac = 0;
1428 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1429 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1430 wStereoFrame = XmCreateFrame(wRightRC,"StereoFrame",al,ac);
1431 XtManageChild(wStereoFrame);
1432
1433 ac = 0;
1434 wStereoCB = XmCreateToggleButton(wStereoFrame,"Stereo",al,ac);
1435 XtManageChild(wStereoCB);
1436 XtAddCallback(wStereoCB,XmNvalueChangedCallback,StereoChanged_CB,NULL);
1437
1438 ac = 0;
1439 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1440 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1441 wRecordFrame = XmCreateFrame(wRightRC,"RecordFrame",al,ac);
1442 XtManageChild(wRecordFrame);
1443
1444 ac = 0;
1445 XtSetArg(al[ac],XmNorientation,XmVERTICAL); ++ac;
1446 XtSetArg(al[ac],XmNmarginHeight,6); ++ac;
1447 XtSetArg(al[ac],XmNmarginWidth,6); ++ac;
1448 XtSetArg(al[ac],XmNpacking,XmPACK_TIGHT); ++ac;
1449 wRecordRC = XmCreateRowColumn(wRecordFrame,"RecordRC",al,ac);
1450 XtManageChild(wRecordRC);
1451
1452 ac = 0;
1453 wRecordPB = XmCreatePushButton(wRecordRC,"Record",al,ac);
1454 XtManageChild(wRecordPB);
1455 XtAddCallback(wRecordPB,XmNactivateCallback,Record_CB,NULL);
1456
1457 ac = 0;
1458 wStopPB = XmCreatePushButton(wRecordRC,"Stop",al,ac);
1459 XtManageChild(wStopPB);
1460 XtAddCallback(wStopPB,XmNactivateCallback,Stop_CB,NULL);
1461
1462 /*
1463 * Main form layout details:
1464 */
1465 XtVaSetValues(wLeftRC,
1466 XmNtopAttachment,XmATTACH_FORM,
1467 XmNleftAttachment,XmATTACH_FORM,
1468 NULL);
1469
1470 XtVaSetValues(wRightRC,
1471 XmNtopAttachment,XmATTACH_FORM,
1472 XmNleftAttachment,XmATTACH_WIDGET, XmNleftWidget,wLeftRC,
1473 XmNrightAttachment,XmATTACH_FORM,
1474 NULL);
1475
1476 /*
1477 * Realize the widgets now:
1478 */
1479 BitsShow(); /* Initialize bit settings */
1480 XtVaSetValues(wOptions_DebugTgl,XmNset,bOptionsDebug ? True : False,NULL);
1481 cmdopt_x = bOptionsDebug != False ? 1 : 0;
1482 XtRealizeWidget(wParent); /* OK, realize it all.. */
1483
1484 /*
1485 * Fork a new process, and try to start the server:
1486 */
1487 if ( tosvr_start(v_erf) < 0 ) {
1488 ReportErrorf("Failure to start the wavplay server is fatal.\n"
1489 "Please check the executability of the 'wavplay' command.");
1490 exit(3);
1491 }
1492
1493 /*
1494 * Tell the server our debug setting:
1495 */
1496 tosvr_debug(0,NULL,cmdopt_x);
1497
1498 /*
1499 * This work process receives and operates on messages
1500 * from the server to this client program:
1501 */
1502 XtAppAddTimeOut(aContext,TIMER1_MS,TimerProc1,NULL);
1503
1504 /*
1505 * Clear out the "XXXXXXXXXXXX" in the initial resources,
1506 * to set the width of those labels.
1507 */
1508 XmSprintfLabel(wFileLbl1,"No file(s) selected.");
1509 XmSprintfLabel(wTypeLbl1,"Choose [File] -> [Select Files...]");
1510 XmSprintfLabel(wDateLbl1,"then click on entries in the");
1511 XmSprintfLabel(wSizeLbl1,"list box.");
1512 XmSprintfLabel(wSamplingRateLbl1," ");
1513 XmSprintfLabel(wSamplesLbl1," ");
1514 XmSprintfLabel(wTimeLbl1," ");
1515
1516 /*
1517 * The MOTIF Main Loop:
1518 */
1519 XtAppMainLoop(aContext);
1520 return 0; /* A cookie for the compiler */
1521 }
1522
1523 /* $Source: /home/cvs/wavplay/xltwavplay.c,v $ */
1524