1 /* $TOG: piano.c /main/11 1997/05/14 13:42:25 bill $ */
2 /*
3  * Motif
4  *
5  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
6  *
7  * These libraries and programs are free software; you can
8  * redistribute them and/or modify them under the terms of the GNU
9  * Lesser General Public License as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  *
13  * These libraries and programs are distributed in the hope that
14  * they will be useful, but WITHOUT ANY WARRANTY; without even the
15  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16  * PURPOSE. See the GNU Lesser General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with these librararies and programs; if not, write
21  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
22  * Floor, Boston, MA 02110-1301 USA
23  *
24  */
25 /*
26  * HISTORY
27  */
28 /****************************************************************************
29  ****************************************************************************
30  **
31  **   File:         piano.c
32  **
33  **   Version:      2.0
34  **
35  **   By:           Andrew deBlois
36  **
37  **   This application won't be able to play tunes on the pmax and sun
38  **   since you can't change the tones generated by XBell.
39  **
40  ****************************************************************************
41  ****************************************************************************/
42 
43 #include <math.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <Xm/XmP.h>
47 #include <Xm/Xm.h>
48 #include <X11/Shell.h>
49 #include <Xm/BulletinB.h>
50 #include <Xm/CascadeB.h>
51 #include <Xm/DrawingAP.h>
52 #include <Xm/DrawingA.h>
53 #include <Xm/FileSB.h>
54 #include <Xm/Form.h>
55 #include <Xm/Label.h>
56 #include <Xm/MainW.h>
57 #include <Xm/MenuShell.h>
58 #include <Xm/MessageB.h>
59 #include <Xm/PanedW.h>
60 #include <Xm/PushBP.h>
61 #include <Xm/PushB.h>
62 #include <Xm/RowColumn.h>
63 #include <Xm/Scale.h>
64 #include <Xm/ScrolledW.h>
65 #include <Xm/SelectioB.h>
66 #include <Xm/Separator.h>
67 #include <Xm/XpmP.h>
68 #include "piano.images"
69 
70 
71 /* note that anything after REST must be some type of rest. */
72 
73 typedef enum {EIGHTH,  EIGHTHDOT,  EIGHTHSHARP,  EIGHTHDOTSHARP,
74 	      QUARTER, QUARTERDOT, QUARTERSHARP, QUARTERDOTSHARP,
75 	      HALF,    HALFDOT,    HALFSHARP,    HALFDOTSHARP,
76 	      REST,    RESTDOT,    LAST_NOTE} NoteType;
77 
78 char *noteName[] = {"eighth",  "eighthdot",  "eighthsharp",  "eighthdotsharp",
79                     "quarter", "quarterdot", "quartersharp", "quarterdotsharp",
80                     "half",    "halfdot",    "halfsharp",    "halfdotsharp",
81                     "rest",    "restdot"};
82 
83 typedef enum { MENU_QUIT, MENU_HELP } MenuFunction;
84 
85 
86 /*-------------------------------------------------------------*
87  |		               Types			       |
88  *-------------------------------------------------------------*/
89 
90 
91 /*****
92  * NoteDescription:	Structure to hold the description of all note types supported.
93  */
94 typedef struct _NoteDescription
95 {
96    Pixmap		image;
97    Pixmap		mask;
98 } NoteDescription;
99 
100 
101 /*****
102  * NoteRec:		Data stored in each note after it is added to the staff.
103  */
104 typedef struct _NoteRec
105 {
106    Display	       *display;
107    NoteType		noteType;       /* type of note or rest. */
108    int			noteDuration;
109    int			noteNumber;     /* number of note to play */
110    int			noteIndex;      /* index used for positioning. */
111    int			ledgerLine;
112    struct _NoteRec *next;
113 } NoteRec;
114 
115 
116 /*****
117  * StaffRec:		Data used to manipulate the staff containing a list of notes.
118  */
119 typedef struct _StaffRec
120 {
121    Display		*display;
122    Widget		 staff;
123    NoteRec		*notes;
124    Widget		divider;	/* seperator above this staff. */
125    struct _StaffRec	*prev, *next;
126 } StaffRec;
127 
128 
129 /*****
130  * AppData:		Data used throughout the app to hold all necessary data.
131  */
132 typedef struct _AppData
133 {
134    /* resources */
135    int			baseDuration; 	/* equal to one quarter note */
136    float		baseFrequency;	/* frequency assigned to new staffs. */
137    Boolean		useKeyboard;	/* if true, keyboard played with voice(s). */
138    int			wkeyCount;	/* number of white keys on the keyboard. */
139    int			keyHeight;	/* white key keight - also sets black keys */
140    int			keyWidth;	/* width of each black key. */
141 
142    /* data */
143    GC			noteGC;
144    NoteType		activeNoteType;		/* index into the noteTable */
145    NoteDescription	noteTable[LAST_NOTE];	/* definition info for each note type. */
146    StaffRec            *staffList;              /* holds list of all staffs (voices). */
147    Widget               score;                  /* the rowcolumn holding the staffs. */
148 } AppData;
149 
150 
151 /*-------------------------------------------------------------*
152  |		              defines			       |
153  *-------------------------------------------------------------*/
154 
155 #define APP_NAME  "piano"
156 #define APP_CLASS "Piano"
157 
158 /*
159  * default resource settings.
160  */
161 #define DEFAULT_BASE_FREQUENCY	"246.9413"
162 #define DEFAULT_BASE_DURATION	200
163 #define DEFAULT_WKEY_COUNT	28            /* number of white keys */
164 #define DEFAULT_KEY_HEIGHT	160
165 #define DEFAULT_KEY_WIDTH	20
166 
167 #define LOCAL_NAME		"local"       /* just used for a label */
168 
169 #define EMSG1 "Fatal Error -- Cannot allocate memory for resources.\n"
170 
171 
172 /*-------------------------------------------------------------*
173  |		            Resources			       |
174  *-------------------------------------------------------------*/
175 
176 XtResource appRes[] =
177 {
178   {"baseDuration", "BaseDuration", XtRInt, sizeof(int),
179      XtOffsetOf(AppData, baseDuration), XtRImmediate,
180      (XtPointer)DEFAULT_BASE_DURATION},
181 
182   {"baseFrequency", "BaseFrequency", XtRFloat, sizeof(float),
183      XtOffsetOf(AppData, baseFrequency), XmRString,
184      DEFAULT_BASE_FREQUENCY},
185 
186   {"useKeyboard", "UseKeyboard", XtRBoolean, sizeof(Boolean),
187      XtOffsetOf(AppData, useKeyboard), XmRImmediate,
188      (XtPointer)TRUE},
189 
190   {"wkeyCount", "WkeyCount", XtRInt, sizeof(int),
191      XtOffsetOf(AppData, wkeyCount), XtRImmediate,
192      (XtPointer)DEFAULT_WKEY_COUNT},
193 
194   {"keyHeight", "KeyHeight", XtRInt, sizeof(int),
195      XtOffsetOf(AppData, keyHeight), XtRImmediate,
196      (XtPointer)DEFAULT_KEY_HEIGHT},
197 
198   {"keyWidth", "KeyWidth", XtRInt, sizeof(int),
199      XtOffsetOf(AppData, keyWidth), XtRImmediate,
200      (XtPointer)DEFAULT_KEY_WIDTH},
201 };
202 
203 
204 
205 /*-------------------------------------------------------------*
206  |		      	Function Declarations     	       |
207  *-------------------------------------------------------------*/
208 
209 /* KEYBOARD */
210 void   BuildKeys         (Widget);
211 Widget CreateKeyboard    (Widget);
212 
213 /* SCORE */
214 StaffRec *GetStaffData      (Widget);
215 void      DrawNotes         (Widget, int, int);
216 void      DrawStaffCB       (Widget, XtPointer, XtPointer);
217 void      SetIcon           (Widget, Pixmap);
218 void      DrawNote          (Widget, NoteRec *, int, int);
219 void      SetActiveNote     (Widget, NoteType);
220 void      AddNoteAtPosn     (Widget, int, NoteType);
221 void      AddNoteToStaffCB  (Widget, XtPointer, XtPointer);
222 void      AddNewStaff       (Display *, char *);
223 void      PostStaffMenu     (Widget, XtPointer, XEvent *, Boolean *);
224 void	  CreateStaffMenu   (Widget, Widget, char *);
225 
226 /* OTHER */
227 void   AddVoiceCB        (Widget, XtPointer, XtPointer);
228 void   RemoveVoiceCB     (Widget, XtPointer, XtPointer);
229 void   ClearVoiceCB      (Widget, XtPointer, XtPointer);
230 void   PlayVoiceCB       (Widget, XtPointer, XtPointer);
231 void   PlayAllCB         (Widget, XtPointer, XtPointer);
232 void   SetAppIcon        (Widget);
233 void   GetBell           (Display *);
234 void   SetBell           (Display *, int, int);
235 int    Pitch             (int);
236 void   PlayNote          (XtPointer, XtIntervalId *);
237 void   SoundCB           (Widget, XtPointer, XtPointer);
238 void   SetNoteCB         (Widget, XtPointer, XtPointer);
239 void   CreateScore       (Widget);
240 Widget CreateNotebook    (Widget);
241 void   CvtStrToFloat     (XrmValue *, Cardinal *, XrmValue *, XrmValue *);
242 
243 
244 
245 /* Globals */
246 AppData      *appData;
247 XtAppContext  context;
248 int           orig_percent, orig_pitch, orig_duration;
249 
250 Widget        key[1000];
251 
252 
253 String fallback[] = {
254    "Piano*highlightThickness:     		0",
255    "Piano*borderWidth:      			0",
256    "Piano*iconImage:     			Piano.bmp",
257    "Piano*borderWidth:     			0",
258    "Piano*margin:     			0",
259    "Piano*wKey.background:     		white",
260    "Piano*bKey.background:     		black",
261    "Piano*wKey.foreground:     		black",
262    "Piano*bKey.foreground:     		white",
263    "Piano*wKey.shadowThickness:     		2",
264    "Piano*bKey.shadowThickness:     		4",
265    "Piano*wKey.armColor:     			grey85",
266    "Piano*bKey.armColor:     			grey0",
267    "Piano*wKey.topShadowColor:     		white",
268    "Piano*bKey.bottomShadowColor:     	black",
269    "Piano*wKey.topShadowColor:     		grey60",
270    "Piano*bKey.bottomShadowColor:     	grey20",
271    "Piano*bKey.labelString:     		",
272    "Piano*wKey.labelString:     		",
273 
274    "Piano*keyboard.marginWidth:		10",
275    "Piano*keyboard.marginHeight:		10",
276 
277    "Piano*scoreWin.height:			111",
278    "Piano*staff.width:			920",
279    "Piano*staff.height:			100",
280 
281    "Piano*popupBtn1.labelString:		Add Voice",
282    "Piano*popupBtn2.labelString:		Remove Voice",
283    "Piano*popupBtn3.labelString:		Clear Voice",
284    "Piano*popupBtn4.labelString:		Play Voice",
285    "Piano*popupBtn5.labelString:		Play All",
286    "Piano*popupBtn6.labelString:		Save Voice",
287    "Piano*popupBtn7.labelString:		Load Voice",
288 
289    "Piano*cascade1.labelString:		File",
290    "Piano*cascade2.labelString:		Help",
291 
292    "Piano*b1.labelString:			Quit",
293 
294    "Piano*notebook.orientation:		horizontal",
295    "Piano*notebook.adjustLast:		false",
296    "Piano*notebook*paneMaximum:		40",
297 
298    "Piano*dspPromptDlog.labelString:		Enter name of display to connect to:",
299    "Piano*warnDlog.messageString:		Error in connecting to display",
300 
301    "Piano*helpDlog*messageString:\
302 Piano Demo\\n\
303 ----------\\n\
304 Press Btn3 on a staff to post an associated menu\\n\
305 containing the following items:\\n\
306    Add Voice - Add a new staff and voice. Each voice may\\n\
307                connect to a different display.\\n\
308    Remove Voice - Removes a staff and voice from the score.\\n\
309    Clear Voice - Removes all notes in a staff.\\n\
310    Play Voice - Plays the notes in the selected staff.\\n\
311    Play All - Plays all voices in the score together.\\n\
312    Save Voice - Saves the selected voice to a file.\\n\
313    Load Voice - Loads a voice from a file. This will\\n\
314                 append to any existing notes in the voice.\\n\\n\
315 To delete a note, press Btn2 over the desired note in a staff.\\n\\n\
316 Settable resources are:\\n\
317    baseDuration - sets the duration of a quarter note. (msec)\\n\
318    baseFrequency - sets the frequency of bottom note. (Hz)\\n\
319    useKeyboard - specifies if keyboard should play along.\\n\
320    wkeyCount - number of white keys on the keyboard.\\n\
321    keyHeight - initial height in pixels of the white keys.\\n\
322    keyWidth - initial width in pixels of the white keys.",
323 
324    NULL
325   };
326 
327 
328 
329 /***********************************************************************/
330 
331 
332 /*----------------------------------------------------------------*
333  |                         MyErrorHandler                         |
334  *----------------------------------------------------------------*/
MyErrorHandler(Display * display,XErrorEvent * errorEvent)335 int MyErrorHandler (Display *display, XErrorEvent *errorEvent)
336 {
337   /* this is most likely invoked when the frequency selected is out of range. */
338   printf("X Error!\n");
339 
340   return 0;			/* Ignored by X. */
341 }
342 
343 
344 /*----------------------------------------------------------------*
345  |                             DoQuit                             |
346  *----------------------------------------------------------------*/
DoQuit()347 void DoQuit ()
348 {
349   exit(0);
350 }
351 
352 
353 /*----------------------------------------------------------------*
354  |                             DoHelp                             |
355  *----------------------------------------------------------------*/
DoHelp()356 void DoHelp ()
357 {
358   static Widget dlog = NULL;
359   Arg           args[3];
360   int           n;
361 
362   if (dlog == NULL)
363     {
364       dlog = XmCreateInformationDialog(appData->score, "helpDlog", NULL, 0);
365       XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_HELP_BUTTON) );
366       XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_CANCEL_BUTTON) );
367     }
368 
369   XtManageChild(dlog);
370 }
371 
372 
373 /*----------------------------------------------------------------*
374  |                             MenuCB                             |
375  *----------------------------------------------------------------*/
MenuCB(Widget w,XtPointer clientData,XtPointer callData)376 void MenuCB (Widget w, XtPointer clientData, XtPointer callData)
377 {
378   switch ((long)clientData)
379     {
380     case MENU_QUIT:   DoQuit(); break;
381     case MENU_HELP:   DoHelp(); break;
382     }
383 }
384 
385 
386 /*--------------------------------------------------------------------*
387  |                            DoAddVoiceCB                            |
388  *--------------------------------------------------------------------*/
DoAddVoiceCB(Widget w,XtPointer clientData,XtPointer callData)389 void DoAddVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
390 {
391   XmSelectionBoxCallbackStruct *cb = (XmSelectionBoxCallbackStruct *)callData;
392   String        dspName;
393   Display      *newDisplay;
394   String        appName, appClass;
395   static Widget dlog = NULL;
396   int           n, argc = 0;
397   Arg           args[5];
398 
399 
400   XtGetApplicationNameAndClass(XtDisplay(w), &appName, &appClass);
401   XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &dspName);
402 
403   newDisplay = XtOpenDisplay(context, dspName, appName, appClass, NULL, 0, &argc, NULL);
404 
405   if (newDisplay != NULL)
406     AddNewStaff(newDisplay, dspName);
407   else
408     {
409       if (dlog == NULL)
410 	{
411 	  n = 0;
412 	  XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
413 	  XtSetArg(args[n], XmNdialogType,  XmDIALOG_ERROR);                  n++;
414 	  dlog = XmCreateWarningDialog(appData->score, "warnDlog", args, n);
415 	}
416       XtManageChild(dlog);
417     }
418   if (dspName) XtFree(dspName);
419 }
420 
421 
422 /*--------------------------------------------------------------------*
423  |                            AddVoiceCB                              |
424  *--------------------------------------------------------------------*/
AddVoiceCB(Widget w,XtPointer clientData,XtPointer callData)425 void AddVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
426 {
427   Cardinal      n;
428   Arg           args[5];
429   static Widget dlog = NULL;
430 
431 
432   if (dlog == NULL)
433     {
434       n = 0;
435       XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
436       XtSetArg(args[n], XmNdialogType,  XmDIALOG_PROMPT);                 n++;
437       dlog = XmCreatePromptDialog(appData->score, "dspPromptDlog", args, n);
438 
439       XtAddCallback(dlog, XmNokCallback, DoAddVoiceCB, NULL);
440     }
441 
442   XtManageChild(dlog);
443 }
444 
445 
446 /*--------------------------------------------------------------------*
447  |                           RemoveVoiceCB                            |
448  *--------------------------------------------------------------------*/
RemoveVoiceCB(Widget w,XtPointer clientData,XtPointer callData)449 void RemoveVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
450 {
451   Widget    staff = (Widget)clientData;
452   StaffRec *staffData;
453 
454   staffData = GetStaffData(staff);
455 
456   if (staffData->divider != NULL)
457     XtDestroyWidget(staffData->divider);
458   XtDestroyWidget(staff);
459 
460   if (staffData->next != NULL)
461     staffData->next->prev = staffData->prev;
462 
463   if (staffData->prev != NULL)
464     staffData->prev->next = staffData->next;
465   else
466     appData->staffList = staffData->next;
467 
468   XtFree((char *)staffData);
469 }
470 
471 
472 /*--------------------------------------------------------------------*
473  |                            ClearVoiceCB                            |
474  *--------------------------------------------------------------------*/
ClearVoiceCB(Widget w,XtPointer clientData,XtPointer callData)475 void ClearVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
476 {
477   Widget   staff = (Widget)clientData;
478   StaffRec *staffData;
479   NoteRec  *notes, *np;
480 
481 
482   staffData = GetStaffData(staff);
483 
484   if (staffData != NULL)
485     {
486       while (staffData->notes != NULL)
487 	{
488 	  np = staffData->notes;
489 	  staffData->notes = staffData->notes->next;
490 	  XtFree((char *)np);
491 	}
492 
493       XClearArea(XtDisplay(staff), XtWindow(staff), 0, 0, 0, 0, TRUE);
494     }
495 }
496 
497 
498 /*--------------------------------------------------------------------*
499  |                              ArmKey                                |
500  *--------------------------------------------------------------------*/
ArmKey(XtPointer clientData,XtIntervalId * id)501 void ArmKey (XtPointer clientData, XtIntervalId *id)
502 {
503   Widget key = (Widget) clientData;
504   XEvent event;
505   XtCallbackList cbList;
506 
507   XtVaGetValues(key, XmNarmCallback, &cbList, NULL);
508   XtVaSetValues(key, XmNarmCallback, NULL, NULL);
509   XtCallActionProc(key, "Arm", &event, NULL, 0);
510   XtVaSetValues(key, XmNarmCallback, cbList, NULL);
511 }
512 
513 
514 /*--------------------------------------------------------------------*
515  |                            DisarmKey                               |
516  *--------------------------------------------------------------------*/
DisarmKey(XtPointer clientData,XtIntervalId * id)517 void DisarmKey (XtPointer clientData, XtIntervalId *id)
518 {
519   Widget key = (Widget) clientData;
520   XEvent event;
521 
522   XtCallActionProc(key, "Disarm", &event, NULL, 0);
523 }
524 
525 
526 /*--------------------------------------------------------------------*
527  |                             PlayNotes                              |
528  | Note that bell duration and interval timing are defined in X as    |
529  | based on milliseconds.  Add a timeout for each note to play, then  |
530  | let things go.                                                     |
531  *--------------------------------------------------------------------*/
PlayNotes(XtPointer clientData,XtIntervalId * id)532 void PlayNotes (XtPointer clientData, XtIntervalId *id)
533 {
534   NoteRec *note = (NoteRec *)clientData;
535   XEvent event;
536   XtCallbackList tempCallbackList;
537   int dt = 0;
538 
539 
540   while (note != NULL)
541     {
542       if (note->noteType < REST)
543 	{
544 	  XtAppAddTimeOut(context, dt, PlayNote, note);
545 
546 	  /* now to press the keys. */
547 	  if (appData->useKeyboard)
548 	    {
549 	      XtAppAddTimeOut(context, dt, ArmKey, key[note->noteNumber]);
550 	      XtAppAddTimeOut(context, dt+note->noteDuration, DisarmKey,
551 			      key[note->noteNumber]);
552 	    }
553 	}
554       dt += note->noteDuration;
555 
556       note = note->next;
557     }
558 }
559 
560 /*--------------------------------------------------------------------*
561  |                            PlayVoiceCB                             |
562  *--------------------------------------------------------------------*/
PlayVoiceCB(Widget w,XtPointer clientData,XtPointer callData)563 void PlayVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
564 {
565   Widget    staff = (Widget)clientData;
566   StaffRec *staffData;
567   NoteRec  *notes;
568 
569 
570   staffData = GetStaffData(staff);
571   if (staffData != NULL)
572     XtAppAddTimeOut(context, 1, PlayNotes, staffData->notes);
573 }
574 
575 
576 /*--------------------------------------------------------------------*
577  |                             PlayAllCB                              |
578  *--------------------------------------------------------------------*/
PlayAllCB(Widget staff,XtPointer clientData,XtPointer callData)579 void PlayAllCB (Widget staff, XtPointer clientData, XtPointer callData)
580 {
581   StaffRec *sPtr;
582 
583   for (sPtr = appData->staffList; sPtr != NULL; sPtr = sPtr->next)
584     {
585       XtAppAddTimeOut(context, 1, PlayNotes, sPtr->notes);
586     }
587 }
588 
589 
590 /*--------------------------------------------------------------------*
591  |                            DoSaveVoiceCB                           |
592  *--------------------------------------------------------------------*/
DoSaveVoiceCB(Widget w,XtPointer clientData,XtPointer callData)593 void DoSaveVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
594 {
595   XmFileSelectionBoxCallbackStruct *fdata =
596     (XmFileSelectionBoxCallbackStruct *)callData;
597   Widget    staff = (Widget)clientData;
598   StaffRec *staffData;
599   NoteRec  *note;
600   FILE     *fp;
601   char     *fileName;
602   static Widget errDlog = NULL;
603 
604 
605   if (fdata->length > 0)
606     {
607       XmStringGetLtoR(fdata->value, XmSTRING_DEFAULT_CHARSET, &fileName);
608 
609       staffData = GetStaffData(staff);
610       if (staffData != NULL)
611 	{
612 	  fp = fopen(fileName, "w");
613 
614 	  for (note = staffData->notes;  note != NULL;  note = note->next)
615 	    {
616 	      fprintf(fp, "%d %d %d %d %d\n",
617 		      note->noteType,
618 		      note->noteDuration,
619 		      note->noteNumber,
620 		      note->noteIndex,
621 		      note->ledgerLine);
622 	    }
623 	  fclose(fp);
624 	}
625 
626       if (fileName) XtFree(fileName);
627     }
628 }
629 
630 
631 
632 /*--------------------------------------------------------------------*
633  |                            SaveVoiceCB                             |
634  *--------------------------------------------------------------------*/
SaveVoiceCB(Widget w,XtPointer clientData,XtPointer callData)635 void SaveVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
636 {
637   Widget        staff = (Widget)clientData;
638   static Widget fsdlog = NULL;
639   static Widget oldStaff;
640 
641 
642   if (fsdlog == NULL)
643     {
644       fsdlog = XmCreateFileSelectionDialog(staff, "saveDlog", NULL, 0);
645       XtVaSetValues(fsdlog,
646 		    XmNautoUnmanage, True,
647 		    XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
648 		    NULL);
649     }
650   else
651     {
652       XtRemoveCallback(fsdlog, XmNokCallback, DoSaveVoiceCB, oldStaff);
653     }
654 
655   XtAddCallback(fsdlog, XmNokCallback, DoSaveVoiceCB, staff);
656   oldStaff = staff;
657 
658   XtManageChild(fsdlog);
659 }
660 
661 
662 
663 /*--------------------------------------------------------------------*
664  |                           DoLoadVoiceCB                            |
665  *--------------------------------------------------------------------*/
DoLoadVoiceCB(Widget w,XtPointer clientData,XtPointer callData)666 void DoLoadVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
667 {
668   XmFileSelectionBoxCallbackStruct *fdata =
669     (XmFileSelectionBoxCallbackStruct *)callData;
670   Widget    staff = (Widget)clientData;
671   StaffRec *staffData;
672   NoteRec  *note, *tail;
673   FILE     *fp;
674   Boolean   done = FALSE;
675   int       noteOffset;
676   char     *fileName;
677 
678 
679   if (fdata->length > 0)
680     {
681       XmStringGetLtoR(fdata->value, XmSTRING_DEFAULT_CHARSET, &fileName);
682 
683       fp = fopen(fileName, "r");
684       if (fileName) XtFree(fileName);
685 
686       if (fp != NULL)
687 	{
688 	  staffData = GetStaffData(staff);
689 	  if (staffData->notes == NULL)
690 	    {
691 	      tail = NULL;
692 	      noteOffset = 0;
693 	    }
694 	  else
695 	    for (tail=staffData->notes, noteOffset = 1;
696 		 tail->next != NULL;
697 		 tail = tail->next, noteOffset++);
698 
699 	  while (!done)
700 	    {
701 	      note = (NoteRec *) XtMalloc(sizeof(NoteRec));
702 	      if (fscanf(fp, "%d %d %d %d %d\n",
703 			 (int *)&note->noteType,
704 			 &note->noteDuration,
705 			 &note->noteNumber,
706 			 &note->noteIndex,
707 			 &note->ledgerLine)
708 		  > 0)
709 		{
710 		  note->noteIndex += noteOffset;
711 		  note->display = staffData->display;
712 		  note->next = NULL;
713 
714 		  if (tail == NULL)
715 		    {
716 		      staffData->notes = note;
717 		      tail = note;
718 		    }
719 		  else
720 		    {
721 		      tail->next = note;
722 		      tail = tail->next;
723 		    }
724 		}
725 	      else
726 		{
727 		  XtFree((XtPointer)note);
728 		  done = TRUE;
729 		}
730 	    }
731 	  fclose(fp);
732 	}
733 
734       XClearArea(XtDisplay(w), XtWindow(staff), 0, 0, 0, 0, TRUE);
735     }
736 }
737 
738 
739 
740 
741 /*--------------------------------------------------------------------*
742  |                            LoadVoiceCB                             |
743  *--------------------------------------------------------------------*/
LoadVoiceCB(Widget w,XtPointer clientData,XtPointer callData)744 void LoadVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
745 {
746   Widget        staff = (Widget)clientData;
747   static Widget fsdlog = NULL;
748   static Widget oldStaff;
749 
750 
751   if (fsdlog == NULL)
752     {
753       fsdlog = XmCreateFileSelectionDialog(staff, "loadDlog", NULL, 0);
754       XtVaSetValues(fsdlog,
755 		    XmNautoUnmanage, True,
756 		    XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
757 		    NULL);
758     }
759   else
760     {
761       XtRemoveCallback(fsdlog, XmNokCallback, DoLoadVoiceCB, oldStaff);
762     }
763 
764   XtAddCallback(fsdlog, XmNokCallback, DoLoadVoiceCB, staff);
765   oldStaff = staff;
766 
767   XtManageChild(fsdlog);
768 }
769 
770 
771 /*--------------------------------------------------------------------*
772  |                              DrawNotes                             |
773  | x1,x2 specify the clipping width. If they are the same value, no   |
774  | clipping is performed.                                             |
775  *--------------------------------------------------------------------*/
DrawNotes(Widget staff,int x1,int x2)776 void DrawNotes (Widget staff, int x1, int x2)
777 {
778   StaffRec *staffData;
779   NoteRec  *notes, *np;
780 
781   staffData = GetStaffData(staff);
782   if (staffData != NULL)
783     for (np = staffData->notes;  np != NULL;  np = np->next)
784       DrawNote(staff, np, x1, x2);
785 }
786 
787 
788 
789 /*--------------------------------------------------------------------*
790  |                             DrawStaffCB                            |
791  *--------------------------------------------------------------------*/
DrawStaffCB(Widget staff,XtPointer clientData,XtPointer callData)792 void DrawStaffCB (Widget staff, XtPointer clientData, XtPointer callData)
793 {
794   XExposeEvent *expEvt =
795     (XExposeEvent *)((XmDrawingAreaCallbackStruct*)callData)->event;
796   int       i, y;
797   Dimension width, height;
798 
799 
800   if (expEvt->count > 1)
801     return;
802 
803   XtVaGetValues(XtParent(staff), XmNwidth, &width, NULL);
804   XtVaGetValues(staff, XmNwidth, &width, XmNheight, &height, NULL);
805 
806   for (i=4; i<=12; i+=2)
807     {
808       y = i*(int)height / 16;
809       XDrawLine(XtDisplay(staff), XtWindow(staff),
810 		DefaultGCOfScreen(XtScreen(staff)),
811 		0, y, width, y);
812     }
813 
814   DrawNotes(staff, expEvt->x, expEvt->x + expEvt->width);
815 }
816 
817 
818 
819 
820 /*-------------------------------------------------------------*
821  |                          SetAppIcon()                       |
822  *-------------------------------------------------------------*/
SetAppIcon(Widget shell)823 void SetAppIcon(Widget shell)
824 {
825   Pixmap  iconPixmap;
826 
827   iconPixmap = XCreateBitmapFromData(XtDisplay(shell), XtScreen(shell)->root,
828 				     (char*)piano_bits, piano_width, piano_height);
829 
830   XtVaSetValues(shell, XmNiconPixmap, iconPixmap, NULL);
831 }
832 
833 
834 
835 /*--------------------------------------------------------------------*
836  |                            GetBell                                 |
837  *--------------------------------------------------------------------*/
GetBell(Display * dpy)838 void GetBell(Display *dpy)
839 {
840   XKeyboardState stateValues;
841 
842   XGetKeyboardControl(dpy, &stateValues);
843 
844   orig_percent  = stateValues.bell_percent;
845   orig_pitch    = stateValues.bell_pitch;
846   orig_duration = stateValues.bell_duration;
847 }
848 
849 
850 
851 /*--------------------------------------------------------------------*
852  |                             SetBell                                |
853  *--------------------------------------------------------------------*/
SetBell(Display * dpy,int pitch,int duration)854 void SetBell(Display *dpy, int pitch, int duration)
855 {
856   XKeyboardControl controlValues;
857   unsigned long    valueMask = KBBellPercent | KBBellPitch | KBBellDuration;
858 
859   controlValues.bell_percent  = orig_percent;
860   controlValues.bell_pitch    = pitch;
861   controlValues.bell_duration = duration;
862 
863   XChangeKeyboardControl(dpy, valueMask, &controlValues);
864 }
865 
866 
867 
868 /*--------------------------------------------------------------------*
869  |                            Pitch                                   |
870  *--------------------------------------------------------------------*/
Pitch(int note)871 int Pitch (int note)
872 {
873   double x, m, n, f;
874 
875   /* notes are calculated from the base frequency. */
876   /* This is the first note on the keyboard.       */
877   /* The frequency of a note = 2^(index / 12).     */
878 
879   x = (double)2.0;
880   m = (double)note;
881   n = (double)12.0;
882 
883   f = (double)appData->baseFrequency * pow(x, (m/n));
884 
885   return((int)f);
886 }
887 
888 
889 /*--------------------------------------------------------------------*
890  |                             PlayNote                               |
891  *--------------------------------------------------------------------*/
PlayNote(XtPointer clientData,XtIntervalId * id)892 void PlayNote (XtPointer clientData, XtIntervalId *id)
893 {
894   NoteRec *note = (NoteRec *)clientData;
895 
896   SetBell(note->display, Pitch(note->noteNumber), note->noteDuration);
897 
898   XBell(note->display, 100);
899 
900   SetBell(note->display, orig_pitch, orig_duration);
901 }
902 
903 
904 /*--------------------------------------------------------------------*
905  |                             SoundCB                                |
906  *--------------------------------------------------------------------*/
SoundCB(Widget w,XtPointer noteNumber,XtPointer callData)907 void SoundCB (Widget w, XtPointer noteNumber, XtPointer callData)
908 {
909   XmPushButtonCallbackStruct *cb = (XmPushButtonCallbackStruct *)callData;
910   NoteRec note;
911 
912   /* only play the note if this is a true arm event. */
913 
914   if (cb->event != NULL)
915     {
916       note.display      = XtDisplay(w);
917       note.noteType     = EIGHTH;
918       note.noteNumber   = (NoteType)noteNumber;
919       note.noteIndex    = 0;
920       note.ledgerLine   = 0;
921       note.noteDuration = appData->baseDuration;
922 
923       PlayNote(&note, 0);
924     }
925 }
926 
927 
928 #define MUG_SHOTS 11
929 /*--------------------------------------------------------------------*
930  |                             BuildKeys                              |
931  *--------------------------------------------------------------------*/
BuildKeys(Widget parent)932 void BuildKeys (Widget parent)
933 {
934   Pixmap   iconPixmaps[MUG_SHOTS+1];
935   int      i, j = 0, imageCount=MUG_SHOTS;
936   Boolean  pixmapsSet = FALSE, easterEgg = False;
937   int      noteCount = appData->wkeyCount;
938   static Boolean firstTime = True;
939 
940 
941   if (firstTime)
942     {
943       int          x, y, junk;
944       unsigned int bjunk;
945       Window       wjunk;
946 
947       firstTime = False;
948 
949       /* dev team's signature... :-) */
950       XQueryPointer(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)),
951 		    &wjunk, &wjunk, &x, &y, &junk, &junk, &bjunk);
952 
953       easterEgg = (x+y == 0);
954 
955       if (easterEgg)
956 	{
957 	  Display       *dsp = XtDisplay(parent);
958 	  Window         win = RootWindowOfScreen(XtScreen(parent));
959 	  Pixmap         shapemask;
960 	  XpmAttributes  attributes;
961 
962 	  attributes.valuemask = 0;
963 	  XmeXpmCreatePixmapFromData(dsp, win, none,     &(iconPixmaps[0]), &shapemask, &attributes);
964 	  XmeXpmCreatePixmapFromData(dsp, win, andrew,   &(iconPixmaps[1]), &shapemask, &attributes);
965 	  XmeXpmCreatePixmapFromData(dsp, win, dan,      &(iconPixmaps[2]), &shapemask, &attributes);
966 	  XmeXpmCreatePixmapFromData(dsp, win, dave,     &(iconPixmaps[3]), &shapemask, &attributes);
967 	  XmeXpmCreatePixmapFromData(dsp, win, doug,     &(iconPixmaps[4]), &shapemask, &attributes);
968 	  XmeXpmCreatePixmapFromData(dsp, win, ellis,    &(iconPixmaps[5]), &shapemask, &attributes);
969 	  XmeXpmCreatePixmapFromData(dsp, win, ingeborg, &(iconPixmaps[6]), &shapemask, &attributes);
970 	  XmeXpmCreatePixmapFromData(dsp, win, jim,      &(iconPixmaps[7]), &shapemask, &attributes);
971 	  XmeXpmCreatePixmapFromData(dsp, win, kamesh,   &(iconPixmaps[8]), &shapemask, &attributes);
972 	  XmeXpmCreatePixmapFromData(dsp, win, scott,    &(iconPixmaps[9]), &shapemask, &attributes);
973 	  XmeXpmCreatePixmapFromData(dsp, win, steve,    &(iconPixmaps[10]), &shapemask, &attributes);
974 	  XmeXpmCreatePixmapFromData(dsp, win, vania,    &(iconPixmaps[11]), &shapemask, &attributes);
975 
976 	  appData->wkeyCount = noteCount = imageCount;
977 	}
978     }
979 
980 
981   XtVaSetValues(parent, XmNfractionBase, noteCount * 10, NULL);
982 
983   j = 0;
984   for (i=0; i<noteCount; i++)
985     if ( !((i+1)%7) || !((i-2)%7) ) j++; /* handle 2 whitekeys in a row. ie: B-C, E-F. */
986     else
987       {
988 	j = j+2;
989 	key[j] = XtVaCreateManagedWidget("bKey", xmPushButtonWidgetClass, parent,
990 					 XmNwidth,            appData->keyWidth,
991 					 XmNleftAttachment,   XmATTACH_POSITION,
992 					 XmNleftPosition,     i*10 + 7,
993 					 XmNrightAttachment,  XmATTACH_POSITION,
994 					 XmNrightPosition,    i*10 + 13,
995 					 XmNtopAttachment,    XmATTACH_FORM,
996 					 XmNbottomAttachment, XmATTACH_POSITION,
997 					 XmNbottomPosition,   noteCount*6,
998 					 NULL);
999 	XtAddCallback(key[j], XmNarmCallback, SoundCB, (XtPointer)(long)j);
1000       }
1001 
1002   j = 0;
1003   for (i=0; i<noteCount; i++)
1004     {
1005       if ( !(i%7) || !((i-3)%7) ) j--;
1006       j = j+2;
1007       key[j] = XtVaCreateManagedWidget("wKey", xmPushButtonWidgetClass, parent,
1008 				       XmNheight,           appData->keyHeight,
1009 				       XmNleftAttachment,   XmATTACH_POSITION,
1010 				       XmNleftPosition,     i*10,
1011 				       XmNrightAttachment,  XmATTACH_POSITION,
1012 				       XmNrightPosition,    (i+1)*10,
1013 				       XmNtopAttachment,    XmATTACH_FORM,
1014 				       XmNbottomAttachment, XmATTACH_FORM,
1015 				       NULL);
1016       XtAddCallback(key[j], XmNarmCallback, SoundCB, (XtPointer)(long)j);
1017 
1018       if (easterEgg)
1019 	{
1020 	  XtVaSetValues(key[j],
1021 			XmNlabelType,   XmPIXMAP,
1022 			XmNlabelPixmap, iconPixmaps[0],
1023 			XmNarmPixmap,   iconPixmaps[(i%imageCount)+1],
1024 			XmNmarginLeft,  0,
1025 			XmNmarginRight, 0,
1026 			XmNmarginTop,   100,
1027 			NULL);
1028 	}
1029     }
1030 }
1031 
1032 
1033 /*--------------------------------------------------------------------*
1034  |                           CreateKeyboard                           |
1035  *--------------------------------------------------------------------*/
CreateKeyboard(Widget parent)1036 Widget CreateKeyboard(Widget parent)
1037 {
1038    int    i, j = 0;
1039    Widget keyBoard, wKey, bKey;
1040 
1041 
1042    keyBoard = XtVaCreateWidget("keyBoard", xmFormWidgetClass, parent, NULL);
1043    BuildKeys(keyBoard);
1044 
1045    XtManageChild(keyBoard);
1046    return(keyBoard);
1047 }
1048 
1049 
1050 
1051 /*--------------------------------------------------------------------*
1052  |                            SetIcon                                 |
1053  *--------------------------------------------------------------------*/
SetIcon(Widget w,Pixmap cursorPixmap)1054 void SetIcon (Widget w, Pixmap cursorPixmap)
1055 {
1056    Pixel     fgPix, bgPix;
1057    Cursor    cursor;
1058    XColor    xcolors[2];
1059    Display  *dsp = XtDisplay(w);
1060 
1061 
1062    xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(dsp));
1063    xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(dsp));
1064 
1065    XQueryColors(dsp, DefaultColormapOfScreen(DefaultScreenOfDisplay(dsp)), xcolors, 2);
1066 
1067    cursor = XCreatePixmapCursor(dsp, cursorPixmap, cursorPixmap,
1068 				&(xcolors[0]), &(xcolors[1]), note_x_hot, note_y_hot);
1069 
1070    XDefineCursor(dsp, XtWindow(w), cursor);
1071 }
1072 
1073 
1074 /*--------------------------------------------------------------------*
1075  |                           GetStaffData                             |
1076  | This scans the list of staffs for a match.  It returns the data    |
1077  | associated with the staff.                                         |
1078  *--------------------------------------------------------------------*/
GetStaffData(Widget staff)1079 StaffRec *GetStaffData (Widget staff)
1080 {
1081   StaffRec *sPtr;
1082 
1083   for (sPtr = appData->staffList; sPtr != NULL; sPtr = sPtr->next)
1084     {
1085       if (sPtr->staff == staff)
1086 	return (sPtr);
1087     }
1088 
1089   /* should never get here */
1090   return (NULL);
1091 }
1092 
1093 
1094 /*--------------------------------------------------------------------*
1095  |                             DrawNote                               |
1096  *--------------------------------------------------------------------*/
DrawNote(Widget staff,NoteRec * note,int x1,int x2)1097 void DrawNote (Widget staff, NoteRec *note, int x1, int x2)
1098 {
1099    Dimension  width, height;
1100    Pixmap     notePix, notePixMask;
1101    int        x, y;
1102 
1103 
1104    notePix = appData->noteTable[note->noteType].image;
1105    notePixMask = appData->noteTable[note->noteType].mask;
1106 
1107    XtVaGetValues(staff, XmNwidth, &width, XmNheight, &height, NULL);
1108 
1109    x = note->noteIndex * 15;
1110    y = (15 - note->ledgerLine) * (int)height / 16 - (note_height/2) - 4;
1111 
1112    /* if the position is off the right side of the staff, resize it. */
1113    if ((x + note_width) > (int)width)
1114      XtVaSetValues(staff, XmNwidth, x + 2*note_width, NULL);
1115 
1116    if ((x1 != x2) && (x < x1-note_width || x > x2+note_width))
1117      return;
1118 
1119    XSetClipMask  (XtDisplay(staff), appData->noteGC, notePixMask);
1120    XSetClipOrigin(XtDisplay(staff), appData->noteGC, x, y);
1121    XCopyArea(XtDisplay(staff), notePix, XtWindow(staff), appData->noteGC,
1122 	     0, 0, note_width, note_height,
1123 	     x, y);
1124 }
1125 
1126 
1127 /*--------------------------------------------------------------------*
1128  |                            SetActiveNote                           |
1129  *--------------------------------------------------------------------*/
SetActiveNote(Widget w,NoteType noteType)1130 void SetActiveNote (Widget w, NoteType noteType)
1131 {
1132    appData->activeNoteType = noteType;
1133 
1134    SetIcon(appData->score, appData->noteTable[noteType].mask);
1135 
1136    XSetClipMask(XtDisplay(w), appData->noteGC, appData->noteTable[noteType].mask);
1137 }
1138 
1139 
1140 
1141 /*--------------------------------------------------------------------*
1142  |                              SetNoteCB                             |
1143  | callback which sets the active note and modifies the cursor.       |
1144  *--------------------------------------------------------------------*/
SetNoteCB(Widget w,XtPointer clientData,XtPointer callData)1145 void SetNoteCB (Widget w, XtPointer clientData, XtPointer callData)
1146 {
1147    NoteType noteType = (NoteType)clientData;
1148 
1149    SetActiveNote(w, noteType);
1150 }
1151 
1152 
1153 
1154 /*--------------------------------------------------------------------*
1155  |                             NoteNumber                             |
1156  *--------------------------------------------------------------------*/
NoteNumber(int ledgerLine,Boolean isASharp)1157 int NoteNumber (int ledgerLine, Boolean isASharp)
1158 {
1159    int n = 0;
1160 
1161    switch (ledgerLine)
1162      {
1163      case 1:  n = 1;  break;
1164      case 2:  n = 3;  break;
1165      case 3:  n = 5;  break;
1166      case 4:  n = 6;  break;
1167      case 5:  n = 8;  break;
1168      case 6:  n = 10; break;
1169      case 7:  n = 12; break;
1170      case 8:  n = 13; break;
1171      case 9:  n = 15; break;
1172      case 10: n = 17; break;
1173      case 11: n = 18; break;
1174      case 12: n = 20; break;
1175      }
1176 
1177    if (isASharp)
1178      return (n+1);
1179    else
1180      return (n);
1181 }
1182 
1183 
1184 /*--------------------------------------------------------------------*
1185  |                         DeleteNoteAtPosn                           |
1186  *--------------------------------------------------------------------*/
DeleteNoteAtPosn(Widget staff,int x,int y)1187 void DeleteNoteAtPosn (Widget staff, int x, int y)
1188 {
1189    int        ledgerLine, noteIndex, i;
1190    Dimension  height;
1191    StaffRec  *staffData;
1192    NoteRec   *np, *npTemp;
1193 
1194 
1195    /* find the corresponding ledger line in the staff. */
1196    XtVaGetValues(staff, XmNheight, &height, NULL);
1197    ledgerLine = 15 - (16 * y / (int)height);
1198 
1199    noteIndex = x / 15;
1200 
1201    staffData = GetStaffData(staff);
1202    if ((staffData != NULL) && (staffData->notes != NULL))
1203      {
1204 	if (noteIndex == 1)
1205 	  {
1206 	     npTemp = staffData->notes;
1207 	     staffData->notes = staffData->notes->next;
1208 	  }
1209 	else
1210 	  {
1211 	     for (np = staffData->notes;
1212 		  ((noteIndex > 2) && (np->next != NULL));
1213 		  noteIndex--)
1214 	       np = np->next;
1215 
1216 	     if (np->next != NULL)
1217 	       {
1218 		 npTemp = np->next;
1219 		 np->next = np->next->next;
1220 	       }
1221 	     else
1222 	       npTemp = NULL;
1223 	  }
1224 	if (npTemp != NULL) XtFree((XtPointer)npTemp);
1225 
1226 	for (np = staffData->notes, i = 0; np != NULL; np = np->next)
1227 	  np->noteIndex = ++i;
1228 	XClearArea(XtDisplay(staff), XtWindow(staff), 0, 0, 0, 0, TRUE);
1229      }
1230 
1231 }
1232 
1233 
1234 
1235 /*--------------------------------------------------------------------*
1236  |                           AddNoteAtPosn                            |
1237  *--------------------------------------------------------------------*/
AddNoteAtPosn(Widget staff,int y,NoteType noteType)1238 void AddNoteAtPosn (Widget staff, int y, NoteType noteType)
1239 {
1240    int        ledgerLine, noteCount, noteDuration;
1241    Dimension  height;
1242    StaffRec  *staffData;
1243    NoteRec   *noteList, *currentNoteList, *np;
1244    Boolean    isASharp = FALSE;
1245 
1246 
1247    /* find the corresponding ledger line in the staff. */
1248    XtVaGetValues(staff, XmNheight, &height, NULL);
1249    ledgerLine = 15 - (16 * y / (int)height);
1250 
1251    /* round up to G and down to middle C. */
1252    if (ledgerLine <  1) ledgerLine = 1;
1253    else
1254      if (ledgerLine > 12) ledgerLine = 12;
1255 
1256    switch (noteType)
1257      {
1258      case EIGHTH:	   noteDuration = appData->baseDuration;				break;
1259      case EIGHTHDOT:	   noteDuration = appData->baseDuration*3/2;				break;
1260      case EIGHTHSHARP:	   noteDuration = appData->baseDuration;	isASharp = TRUE;	break;
1261      case EIGHTHDOTSHARP:  noteDuration = appData->baseDuration*3/2;	isASharp = TRUE;	break;
1262      case QUARTER:	   noteDuration = appData->baseDuration*2;				break;
1263      case QUARTERDOT:	   noteDuration = appData->baseDuration*3;				break;
1264      case QUARTERSHARP:	   noteDuration = appData->baseDuration*2;	isASharp = TRUE;	break;
1265      case QUARTERDOTSHARP: noteDuration = appData->baseDuration*3;	isASharp = TRUE;	break;
1266      case HALF:		   noteDuration = appData->baseDuration*4;				break;
1267      case HALFDOT:	   noteDuration = appData->baseDuration*6;				break;
1268      case HALFSHARP:	   noteDuration = appData->baseDuration*4;	isASharp = TRUE;	break;
1269      case HALFDOTSHARP:	   noteDuration = appData->baseDuration*6;	isASharp = TRUE;	break;
1270      case REST:		   noteDuration = appData->baseDuration;				break;
1271      case RESTDOT:	   noteDuration = appData->baseDuration*3/2;				break;
1272      default:              noteDuration = 0;
1273      }
1274 
1275    /* get the staff info - this tells the display to use. */
1276    staffData = GetStaffData(staff);
1277 
1278    noteList = (NoteRec *)XtMalloc(sizeof(NoteRec));
1279    noteList->display       = staffData->display;
1280    noteList->noteType      = noteType;
1281    noteList->noteDuration  = noteDuration;
1282    noteList->noteNumber    = NoteNumber(ledgerLine, isASharp);
1283    noteList->ledgerLine    = ledgerLine;
1284    noteList->next          = NULL;
1285 
1286    /* get the current list of notes. */
1287    currentNoteList = staffData->notes;
1288 
1289    /* find out how many there are. */
1290    for (noteCount=1, np=currentNoteList;
1291 	((np != NULL) && (np->next != NULL));
1292 	np=np->next, noteCount++)
1293      ;
1294 
1295    if (np == NULL)
1296      {
1297 	staffData->notes = noteList;
1298 	noteList->noteIndex = noteCount;
1299      }
1300    else
1301      {
1302 	np->next = noteList;
1303 	noteList->noteIndex = noteCount+1;
1304      }
1305 
1306    DrawNote(staff, noteList, 0, 0);
1307 }
1308 
1309 
1310 /*--------------------------------------------------------------------*
1311  |                          AddNoteToStaffCB                          |
1312  *--------------------------------------------------------------------*/
AddNoteToStaffCB(Widget staff,XtPointer clientData,XtPointer callData)1313 void AddNoteToStaffCB (Widget staff, XtPointer clientData, XtPointer callData)
1314 {
1315    XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct *)callData;
1316    XButtonEvent *btnEvent = (XButtonEvent *)cb->event;
1317    int           vposn, hposn;
1318 
1319 
1320    if ((btnEvent->button == Button1) && (btnEvent->type == ButtonPress))
1321      {
1322 	AddNoteAtPosn(staff, btnEvent->y, appData->activeNoteType);
1323      }
1324    else
1325 
1326      if ((btnEvent->button == Button2) && (btnEvent->type == ButtonPress))
1327        {
1328 	  DeleteNoteAtPosn(staff, btnEvent->x, btnEvent->y);
1329        }
1330 }
1331 
1332 
1333 /*--------------------------------------------------------------------*
1334  |                             AddNewStaff                            |
1335  | Creates data for a new staff and adds it to the global score.      |
1336  | A popup menu is attached to the staff.                             |
1337  *--------------------------------------------------------------------*/
AddNewStaff(Display * newDisplay,char * dspName)1338 void AddNewStaff (Display *newDisplay, char *dspName)
1339 {
1340    StaffRec *staffData;
1341 
1342 
1343    staffData = (StaffRec *) XtMalloc(sizeof(StaffRec));
1344    staffData->display       = newDisplay;
1345    staffData->notes         = NULL;
1346    staffData->next = staffData->prev = NULL;
1347 
1348    /* if this is not the first staff, the add a seperator. */
1349    if (appData->staffList != NULL)
1350      staffData->divider =
1351        XtVaCreateManagedWidget("divider", xmSeparatorWidgetClass, appData->score, NULL);
1352    else
1353      staffData->divider = NULL;
1354 
1355    staffData->staff =
1356      XtVaCreateManagedWidget("staff", xmDrawingAreaWidgetClass, appData->score,
1357 			     XmNresizePolicy, XmRESIZE_NONE,
1358 			     NULL);
1359    XtAddCallback(staffData->staff, XmNexposeCallback, DrawStaffCB,      staffData);
1360    XtAddCallback(staffData->staff, XmNinputCallback,  AddNoteToStaffCB, staffData);
1361 
1362    CreateStaffMenu(appData->score, staffData->staff, dspName);
1363 
1364    /*
1365     * add the staff to the staff list.
1366     */
1367    staffData->next = appData->staffList;
1368    if (appData->staffList != NULL)
1369      appData->staffList->prev = staffData;
1370    appData->staffList = staffData;
1371 }
1372 
1373 
1374 
1375 /*--------------------------------------------------------------------*
1376  |                            PostStaffMenu  		              |
1377  *--------------------------------------------------------------------*/
PostStaffMenu(Widget w,XtPointer clientData,XEvent * event,Boolean * dispatch)1378 void PostStaffMenu (Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch)
1379 {
1380    Widget        menu     = (Widget)clientData;
1381    XButtonEvent *btnEvent = (XButtonEvent *)event;
1382    int           button;
1383 
1384 
1385    XtVaGetValues(menu, XmNwhichButton, &button, NULL);
1386    if (btnEvent->button == button)
1387      {
1388 	XmMenuPosition(menu, btnEvent);
1389 	XtManageChild(menu);
1390      }
1391 }
1392 
1393 
1394 /*--------------------------------------------------------------------*
1395  |                           CreateStaffMenu                          |
1396  *--------------------------------------------------------------------*/
CreateStaffMenu(Widget score,Widget staff,char * dspName)1397 void CreateStaffMenu (Widget score, Widget staff, char *dspName)
1398 {
1399    Widget popupMenu, popupBtn[8];
1400 
1401 
1402    popupMenu = XmCreatePopupMenu(staff, "popupMenu", NULL, 0);
1403    XtAddEventHandler(staff, ButtonPressMask, False, PostStaffMenu, popupMenu);
1404 
1405    XtVaCreateManagedWidget(dspName, xmLabelWidgetClass,     popupMenu, NULL);
1406    XtVaCreateManagedWidget("line",  xmSeparatorWidgetClass, popupMenu, NULL);
1407    popupBtn[1] = XtVaCreateManagedWidget("popupBtn1", xmPushButtonWidgetClass, popupMenu, NULL);
1408    popupBtn[2] = XtVaCreateManagedWidget("popupBtn2", xmPushButtonWidgetClass, popupMenu, NULL);
1409    popupBtn[3] = XtVaCreateManagedWidget("popupBtn3", xmPushButtonWidgetClass, popupMenu, NULL);
1410    popupBtn[4] = XtVaCreateManagedWidget("popupBtn4", xmPushButtonWidgetClass, popupMenu, NULL);
1411    popupBtn[5] = XtVaCreateManagedWidget("popupBtn5", xmPushButtonWidgetClass, popupMenu, NULL);
1412    popupBtn[6] = XtVaCreateManagedWidget("popupBtn6", xmPushButtonWidgetClass, popupMenu, NULL);
1413    popupBtn[7] = XtVaCreateManagedWidget("popupBtn7", xmPushButtonWidgetClass, popupMenu, NULL);
1414 
1415    /* if this is the first one, then don't allow it to be removed. */
1416    if (appData->staffList == NULL) XtVaSetValues(popupBtn[2], XmNsensitive, False, NULL);
1417 
1418    XtAddCallback(popupBtn[1], XmNactivateCallback, AddVoiceCB,    NULL);
1419    XtAddCallback(popupBtn[2], XmNactivateCallback, RemoveVoiceCB, staff);
1420    XtAddCallback(popupBtn[3], XmNactivateCallback, ClearVoiceCB,  staff);
1421    XtAddCallback(popupBtn[4], XmNactivateCallback, PlayVoiceCB,   staff);
1422    XtAddCallback(popupBtn[5], XmNactivateCallback, PlayAllCB,     NULL);
1423    XtAddCallback(popupBtn[6], XmNactivateCallback, SaveVoiceCB,   staff);
1424    XtAddCallback(popupBtn[7], XmNactivateCallback, LoadVoiceCB,   staff);
1425 
1426 }
1427 
1428 
1429 
1430 /*--------------------------------------------------------------------*
1431  |                             CreateScore                            |
1432  | Creates the rowcolumn to holds the staffs.  Also creates an option |
1433  | menu for manipulating the staffs. The staff is inserted into the   |
1434  | global appData list of scores.                                     |
1435  *--------------------------------------------------------------------*/
CreateScore(Widget parent)1436 void CreateScore(Widget parent)
1437 {
1438    Widget scoreWin;
1439 
1440    scoreWin = XtVaCreateManagedWidget("scoreWin", xmScrolledWindowWidgetClass, parent,
1441 				       XmNscrollingPolicy, XmAUTOMATIC, NULL);
1442    appData->score =
1443      XtVaCreateManagedWidget("score", xmRowColumnWidgetClass, scoreWin,
1444 			     XmNadjustLast,   FALSE,
1445 			     XmNnumColumns,   1,
1446 			     XmNorientation,  XmVERTICAL,
1447 			     XmNpacking,      XmPACK_TIGHT,
1448 			     NULL);
1449    AddNewStaff(XtDisplay(appData->score), LOCAL_NAME);
1450 }
1451 
1452 
1453 /*--------------------------------------------------------------------*
1454  |                           CreateNotebook                           |
1455  *--------------------------------------------------------------------*/
CreateNotebook(Widget parent)1456 Widget CreateNotebook(Widget parent)
1457 {
1458    NoteType noteType;
1459    Widget   notebook, noteButton[LAST_NOTE];
1460    Pixel    fg, bg;
1461    Display *dsp = XtDisplay(parent);
1462    Window   win = RootWindowOfScreen(XtScreen(parent));
1463    int      d   = DefaultDepthOfScreen(XtScreen(parent));
1464 
1465 
1466    notebook  = XtVaCreateManagedWidget("notebook", xmRowColumnWidgetClass, parent, NULL);
1467 
1468    /*
1469     * create a pushbutton for each note type and setup its callback.
1470     */
1471    for (noteType = (NoteType)0;  noteType < LAST_NOTE;  noteType++)
1472      {
1473 	noteButton[noteType] =
1474 	  XtVaCreateManagedWidget(noteName[noteType],
1475 				  xmPushButtonWidgetClass, notebook,
1476 				  XmNlabelType,   XmPIXMAP,
1477 				  XmNlabelPixmap, appData->noteTable[noteType].image,
1478 				  NULL);
1479 	XtAddCallback(noteButton[noteType], XmNactivateCallback, SetNoteCB,
1480 		      (XtPointer)noteType);
1481      }
1482 
1483    return notebook;
1484 }
1485 
1486 
1487 /*-------------------------------------------------------------*
1488  |	   Resource Converter:    CvtStrToFloat                |
1489  *-------------------------------------------------------------*/
CvtStrToFloat(XrmValue * args,Cardinal * nargs,XrmValue * fromVal,XrmValue * toVal)1490 void CvtStrToFloat (XrmValue *args, Cardinal *nargs, XrmValue *fromVal, XrmValue *toVal)
1491 {
1492   static float result;
1493 
1494   if (sscanf((char *)fromVal->addr, "%f", &result) == 1)
1495     {
1496       toVal->size = sizeof(float);
1497       toVal->addr = (XtPointer) &result;
1498     }
1499   else
1500     XtStringConversionWarning((char *)fromVal->addr, "Float");
1501 }
1502 
1503 /*----------------------------------------------------------------*
1504  |			  GetAppResources	 	          |
1505  | The following resources are supported in piano:                |
1506  | .baseDuration:  (int)                                          |
1507  |     -- frequencey in Hz for middle C.                          |
1508  | .baseFrequency: (float)                                        |
1509  |     -- duration in ms of a quarter note.                       |
1510  | .wkeyCount:     (int)                                          |
1511  |     -- specifies the number of white keys.                     |
1512  | .keyHeight:     (int)                                          |
1513  |     -- white key pixel height. black keys are calculated.      |
1514  | .keyWidth:      (int)                                          |
1515  |     -- white key pixel width. black keys are calculated.       |
1516  *----------------------------------------------------------------*/
GetAppResources(Widget w)1517 AppData *GetAppResources (Widget w)
1518 {
1519    AppData *appData;
1520 
1521    if ((appData = (AppData *) XtCalloc(1, sizeof(AppData))) == NULL)
1522      {
1523 	printf(EMSG1);
1524 	exit(0);
1525      }
1526 
1527    XtGetApplicationResources(w, (XtPointer)appData,
1528 			     appRes, XtNumber(appRes), NULL, 0);
1529 
1530    return (appData);
1531 }
1532 
1533 
1534 /*--------------------------------------------------------------------*
1535  |                         GetNoteImagePixmap                         |
1536  *--------------------------------------------------------------------*/
GetNoteImagePixmap(Widget w,NoteType note)1537 Pixmap GetNoteImagePixmap(Widget w, NoteType note)
1538 {
1539    Pixel    fg, bg;
1540    Display *dsp = XtDisplay(w);
1541    Window   win = RootWindowOfScreen(XtScreen(w));
1542    int      d   = DefaultDepthOfScreen(XtScreen(w));
1543    unsigned char    *data = NULL;
1544 
1545 
1546    XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL);
1547 
1548    switch (note)
1549      {
1550      case EIGHTH:          data = eighth_bits;		 break;
1551      case EIGHTHDOT:       data = eighth_dot_bits;	 break;
1552      case EIGHTHSHARP:     data = eighth_sharp_bits;	 break;
1553      case EIGHTHDOTSHARP:  data = eighth_dot_sharp_bits; break;
1554      case QUARTER:         data = quarter_bits;		 break;
1555      case QUARTERDOT:      data = quarter_dot_bits;	 break;
1556      case QUARTERSHARP:    data = quarter_sharp_bits;	 break;
1557      case QUARTERDOTSHARP: data = quarter_dot_sharp_bits;break;
1558      case HALF:            data = half_bits;		 break;
1559      case HALFDOT:         data = half_dot_bits;	 break;
1560      case HALFSHARP:       data = half_sharp_bits;	 break;
1561      case HALFDOTSHARP:    data = half_dot_sharp_bits;	 break;
1562      case REST:            data = rest_bits;		 break;
1563      case RESTDOT:         data = rest_dot_bits;	 break;
1564      default: ;
1565      }
1566 
1567    return(XCreatePixmapFromBitmapData(dsp, win, (char*)data,
1568 				      note_width, note_height, fg, bg, d));
1569 }
1570 
1571 
1572 
1573 /*--------------------------------------------------------------------*
1574  |                         GetNoteMaskPixmap                          |
1575  *--------------------------------------------------------------------*/
GetNoteMaskPixmap(Widget w,NoteType note)1576 Pixmap GetNoteMaskPixmap(Widget w, NoteType note)
1577 {
1578    Pixel    fg, bg;
1579    Display *dsp = XtDisplay(w);
1580    Window   win = RootWindowOfScreen(XtScreen(w));
1581    int      d   = DefaultDepthOfScreen(XtScreen(w));
1582    unsigned char    *data = NULL;
1583 
1584 
1585    XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL);
1586 
1587    switch (note)
1588      {
1589      case EIGHTH:          data = eighth_bits;		 break;
1590      case EIGHTHDOT:       data = eighth_dot_bits;	 break;
1591      case EIGHTHSHARP:     data = eighth_sharp_bits;	 break;
1592      case EIGHTHDOTSHARP:  data = eighth_dot_sharp_bits; break;
1593      case QUARTER:         data = quarter_bits;		 break;
1594      case QUARTERDOT:      data = quarter_dot_bits;	 break;
1595      case QUARTERSHARP:    data = quarter_sharp_bits;	 break;
1596      case QUARTERDOTSHARP: data = quarter_dot_sharp_bits;break;
1597      case HALF:            data = half_bits;		 break;
1598      case HALFDOT:         data = half_dot_bits;	 break;
1599      case HALFSHARP:       data = half_sharp_bits;	 break;
1600      case HALFDOTSHARP:    data = half_dot_sharp_bits;	 break;
1601      case REST:            data = rest_bits;		 break;
1602      case RESTDOT:         data = rest_dot_bits;	 break;
1603      default: ;
1604      }
1605 
1606    return (XCreatePixmapFromBitmapData(dsp, win, (char*)data,
1607 				       note_width, note_height, 1, 0, 1));
1608 }
1609 
1610 /*--------------------------------------------------------------------*
1611  |                           BuildNoteTable                           |
1612  *--------------------------------------------------------------------*/
BuildNoteTable(Widget w)1613 void BuildNoteTable (Widget w)
1614 {
1615    NoteType i;
1616 
1617    for (i = (NoteType)0; i<LAST_NOTE; i++)
1618      {
1619 	appData->noteTable[i].image     = GetNoteImagePixmap(w, i);
1620 	appData->noteTable[i].mask      = GetNoteMaskPixmap(w, i);
1621      }
1622 }
1623 
1624 
1625 /*--------------------------------------------------------------------*
1626  |                          CreateMenuBar                             |
1627  *--------------------------------------------------------------------*/
CreateMenuBar(Widget parent)1628 void CreateMenuBar (Widget parent)
1629 {
1630   Cardinal n;
1631   Arg      args[10];
1632   Widget   menuBar;
1633   Widget   cascade1, cascade2;
1634   Widget   menuPane1, menuPane2;
1635   Widget   b1;
1636 
1637   menuBar   = XmCreateMenuBar(parent, "menuBar", NULL, 0);
1638 
1639   menuPane1 = XmCreatePulldownMenu(menuBar, "menuPane1", NULL, 0);
1640   menuPane2 = XmCreatePulldownMenu(menuBar, "menuPane2", NULL, 0);
1641 
1642   b1 = XtCreateManagedWidget("b1", xmPushButtonWidgetClass, menuPane1, NULL,0);
1643 
1644   n = 0;
1645   XtSetArg(args[n], XmNsubMenuId, menuPane1);  n++;
1646   cascade1 = XmCreateCascadeButton(menuBar, "cascade1", args, n);
1647   XtManageChild(cascade1);
1648 
1649   n = 0;
1650   cascade2 = XmCreateCascadeButton(menuBar, "cascade2", args, n);
1651   XtManageChild(cascade2);
1652 
1653   n = 0;
1654   XtSetArg(args[n], XmNmenuHelpWidget, cascade2);  n++;
1655   XtSetValues(menuBar, args, n);
1656 
1657   XtAddCallback(b1, XmNactivateCallback, MenuCB, (XtPointer)MENU_QUIT);
1658   XtAddCallback(cascade2, XmNactivateCallback, MenuCB, (XtPointer)MENU_HELP);
1659 
1660   XtManageChild(menuBar);
1661 }
1662 
1663 
1664 
1665 /*--------------------------------------------------------------------*
1666  |                              Main                                  |
1667  *--------------------------------------------------------------------*/
1668 int
main(int argc,char ** argv)1669 main(int argc, char **argv)
1670 {
1671    Widget           shell, mainWin, panedWin;
1672    Widget           keyboard, notebook;
1673    int              fn;
1674    Pixel            fg, bg;
1675    XGCValues        values;
1676 
1677 
1678    shell = XtVaAppInitialize(&context, APP_CLASS, NULL, 0, &argc, argv, fallback, NULL);
1679 
1680    XSetErrorHandler(MyErrorHandler);
1681 
1682    XtAddConverter(XtRString, XtRFloat, CvtStrToFloat, NULL, 0);
1683    appData   = GetAppResources(shell);
1684 
1685    mainWin   = XmCreateMainWindow(shell, "mainWin", NULL, 0);
1686    XtManageChild(mainWin);
1687    CreateMenuBar(mainWin);
1688 
1689    panedWin = XtVaCreateManagedWidget("panedWin",
1690 				      xmPanedWindowWidgetClass, mainWin, NULL);
1691 
1692    keyboard = CreateKeyboard(panedWin);
1693 
1694    CreateScore(panedWin);
1695    BuildNoteTable(panedWin);
1696 
1697    notebook = CreateNotebook(panedWin);
1698 
1699    SetAppIcon(shell);
1700 
1701    XtRealizeWidget(shell);
1702 
1703    /* get the note GC */
1704    XtVaGetValues(appData->score, XmNforeground, &fg, XmNbackground, &bg, NULL);
1705    values.foreground = fg;
1706    values.background = bg;
1707    appData->noteGC = XtGetGC(appData->score, GCForeground | GCBackground, &values);
1708 
1709    SetActiveNote(appData->score, QUARTER);
1710 
1711    /* save the old bell values so that they can be restored. */
1712    GetBell(XtDisplay(shell));
1713 
1714    XtAppMainLoop(context);
1715 
1716    return 0;    /* make compiler happy */
1717 }
1718 
1719