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 *)¬e->noteType,
704 ¬e->noteDuration,
705 ¬e->noteNumber,
706 ¬e->noteIndex,
707 ¬e->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(¬e, 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