/* $TOG: piano.c /main/11 1997/05/14 13:42:25 bill $ */
/*
* Motif
*
* Copyright (c) 1987-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*
*/
/*
* HISTORY
*/
/****************************************************************************
****************************************************************************
**
** File: piano.c
**
** Version: 2.0
**
** By: Andrew deBlois
**
** This application won't be able to play tunes on the pmax and sun
** since you can't change the tones generated by XBell.
**
****************************************************************************
****************************************************************************/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <Xm/XmP.h>
#include <Xm/Xm.h>
#include <X11/Shell.h>
#include <Xm/BulletinB.h>
#include <Xm/CascadeB.h>
#include <Xm/DrawingAP.h>
#include <Xm/DrawingA.h>
#include <Xm/FileSB.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/MainW.h>
#include <Xm/MenuShell.h>
#include <Xm/MessageB.h>
#include <Xm/PanedW.h>
#include <Xm/PushBP.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/Scale.h>
#include <Xm/ScrolledW.h>
#include <Xm/SelectioB.h>
#include <Xm/Separator.h>
#include <Xm/XpmP.h>
#include "piano.images"
/* note that anything after REST must be some type of rest. */
typedef enum {EIGHTH, EIGHTHDOT, EIGHTHSHARP, EIGHTHDOTSHARP,
QUARTER, QUARTERDOT, QUARTERSHARP, QUARTERDOTSHARP,
HALF, HALFDOT, HALFSHARP, HALFDOTSHARP,
REST, RESTDOT, LAST_NOTE} NoteType;
char *noteName[] = {"eighth", "eighthdot", "eighthsharp", "eighthdotsharp",
"quarter", "quarterdot", "quartersharp", "quarterdotsharp",
"half", "halfdot", "halfsharp", "halfdotsharp",
"rest", "restdot"};
typedef enum { MENU_QUIT, MENU_HELP } MenuFunction;
/*-------------------------------------------------------------*
| Types |
*-------------------------------------------------------------*/
/*****
* NoteDescription: Structure to hold the description of all note types supported.
*/
typedef struct _NoteDescription
{
Pixmap image;
Pixmap mask;
} NoteDescription;
/*****
* NoteRec: Data stored in each note after it is added to the staff.
*/
typedef struct _NoteRec
{
Display *display;
NoteType noteType; /* type of note or rest. */
int noteDuration;
int noteNumber; /* number of note to play */
int noteIndex; /* index used for positioning. */
int ledgerLine;
struct _NoteRec *next;
} NoteRec;
/*****
* StaffRec: Data used to manipulate the staff containing a list of notes.
*/
typedef struct _StaffRec
{
Display *display;
Widget staff;
NoteRec *notes;
Widget divider; /* seperator above this staff. */
struct _StaffRec *prev, *next;
} StaffRec;
/*****
* AppData: Data used throughout the app to hold all necessary data.
*/
typedef struct _AppData
{
/* resources */
int baseDuration; /* equal to one quarter note */
float baseFrequency; /* frequency assigned to new staffs. */
Boolean useKeyboard; /* if true, keyboard played with voice(s). */
int wkeyCount; /* number of white keys on the keyboard. */
int keyHeight; /* white key keight - also sets black keys */
int keyWidth; /* width of each black key. */
/* data */
GC noteGC;
NoteType activeNoteType; /* index into the noteTable */
NoteDescription noteTable[LAST_NOTE]; /* definition info for each note type. */
StaffRec *staffList; /* holds list of all staffs (voices). */
Widget score; /* the rowcolumn holding the staffs. */
} AppData;
/*-------------------------------------------------------------*
| defines |
*-------------------------------------------------------------*/
#define APP_NAME "piano"
#define APP_CLASS "Piano"
/*
* default resource settings.
*/
#define DEFAULT_BASE_FREQUENCY "246.9413"
#define DEFAULT_BASE_DURATION 200
#define DEFAULT_WKEY_COUNT 28 /* number of white keys */
#define DEFAULT_KEY_HEIGHT 160
#define DEFAULT_KEY_WIDTH 20
#define LOCAL_NAME "local" /* just used for a label */
#define EMSG1 "Fatal Error -- Cannot allocate memory for resources.\n"
/*-------------------------------------------------------------*
| Resources |
*-------------------------------------------------------------*/
XtResource appRes[] =
{
{"baseDuration", "BaseDuration", XtRInt, sizeof(int),
XtOffsetOf(AppData, baseDuration), XtRImmediate,
(XtPointer)DEFAULT_BASE_DURATION},
{"baseFrequency", "BaseFrequency", XtRFloat, sizeof(float),
XtOffsetOf(AppData, baseFrequency), XmRString,
DEFAULT_BASE_FREQUENCY},
{"useKeyboard", "UseKeyboard", XtRBoolean, sizeof(Boolean),
XtOffsetOf(AppData, useKeyboard), XmRImmediate,
(XtPointer)TRUE},
{"wkeyCount", "WkeyCount", XtRInt, sizeof(int),
XtOffsetOf(AppData, wkeyCount), XtRImmediate,
(XtPointer)DEFAULT_WKEY_COUNT},
{"keyHeight", "KeyHeight", XtRInt, sizeof(int),
XtOffsetOf(AppData, keyHeight), XtRImmediate,
(XtPointer)DEFAULT_KEY_HEIGHT},
{"keyWidth", "KeyWidth", XtRInt, sizeof(int),
XtOffsetOf(AppData, keyWidth), XtRImmediate,
(XtPointer)DEFAULT_KEY_WIDTH},
};
/*-------------------------------------------------------------*
| Function Declarations |
*-------------------------------------------------------------*/
/* KEYBOARD */
void BuildKeys (Widget);
Widget CreateKeyboard (Widget);
/* SCORE */
StaffRec *GetStaffData (Widget);
void DrawNotes (Widget, int, int);
void DrawStaffCB (Widget, XtPointer, XtPointer);
void SetIcon (Widget, Pixmap);
void DrawNote (Widget, NoteRec *, int, int);
void SetActiveNote (Widget, NoteType);
void AddNoteAtPosn (Widget, int, NoteType);
void AddNoteToStaffCB (Widget, XtPointer, XtPointer);
void AddNewStaff (Display *, char *);
void PostStaffMenu (Widget, XtPointer, XEvent *, Boolean *);
void CreateStaffMenu (Widget, Widget, char *);
/* OTHER */
void AddVoiceCB (Widget, XtPointer, XtPointer);
void RemoveVoiceCB (Widget, XtPointer, XtPointer);
void ClearVoiceCB (Widget, XtPointer, XtPointer);
void PlayVoiceCB (Widget, XtPointer, XtPointer);
void PlayAllCB (Widget, XtPointer, XtPointer);
void SetAppIcon (Widget);
void GetBell (Display *);
void SetBell (Display *, int, int);
int Pitch (int);
void PlayNote (XtPointer, XtIntervalId *);
void SoundCB (Widget, XtPointer, XtPointer);
void SetNoteCB (Widget, XtPointer, XtPointer);
void CreateScore (Widget);
Widget CreateNotebook (Widget);
void CvtStrToFloat (XrmValue *, Cardinal *, XrmValue *, XrmValue *);
/* Globals */
AppData *appData;
XtAppContext context;
int orig_percent, orig_pitch, orig_duration;
Widget key[1000];
String fallback[] = {
"Piano*highlightThickness: 0",
"Piano*borderWidth: 0",
"Piano*iconImage: Piano.bmp",
"Piano*borderWidth: 0",
"Piano*margin: 0",
"Piano*wKey.background: white",
"Piano*bKey.background: black",
"Piano*wKey.foreground: black",
"Piano*bKey.foreground: white",
"Piano*wKey.shadowThickness: 2",
"Piano*bKey.shadowThickness: 4",
"Piano*wKey.armColor: grey85",
"Piano*bKey.armColor: grey0",
"Piano*wKey.topShadowColor: white",
"Piano*bKey.bottomShadowColor: black",
"Piano*wKey.topShadowColor: grey60",
"Piano*bKey.bottomShadowColor: grey20",
"Piano*bKey.labelString: ",
"Piano*wKey.labelString: ",
"Piano*keyboard.marginWidth: 10",
"Piano*keyboard.marginHeight: 10",
"Piano*scoreWin.height: 111",
"Piano*staff.width: 920",
"Piano*staff.height: 100",
"Piano*popupBtn1.labelString: Add Voice",
"Piano*popupBtn2.labelString: Remove Voice",
"Piano*popupBtn3.labelString: Clear Voice",
"Piano*popupBtn4.labelString: Play Voice",
"Piano*popupBtn5.labelString: Play All",
"Piano*popupBtn6.labelString: Save Voice",
"Piano*popupBtn7.labelString: Load Voice",
"Piano*cascade1.labelString: File",
"Piano*cascade2.labelString: Help",
"Piano*b1.labelString: Quit",
"Piano*notebook.orientation: horizontal",
"Piano*notebook.adjustLast: false",
"Piano*notebook*paneMaximum: 40",
"Piano*dspPromptDlog.labelString: Enter name of display to connect to:",
"Piano*warnDlog.messageString: Error in connecting to display",
"Piano*helpDlog*messageString:\
Piano Demo\\n\
----------\\n\
Press Btn3 on a staff to post an associated menu\\n\
containing the following items:\\n\
Add Voice - Add a new staff and voice. Each voice may\\n\
connect to a different display.\\n\
Remove Voice - Removes a staff and voice from the score.\\n\
Clear Voice - Removes all notes in a staff.\\n\
Play Voice - Plays the notes in the selected staff.\\n\
Play All - Plays all voices in the score together.\\n\
Save Voice - Saves the selected voice to a file.\\n\
Load Voice - Loads a voice from a file. This will\\n\
append to any existing notes in the voice.\\n\\n\
To delete a note, press Btn2 over the desired note in a staff.\\n\\n\
Settable resources are:\\n\
baseDuration - sets the duration of a quarter note. (msec)\\n\
baseFrequency - sets the frequency of bottom note. (Hz)\\n\
useKeyboard - specifies if keyboard should play along.\\n\
wkeyCount - number of white keys on the keyboard.\\n\
keyHeight - initial height in pixels of the white keys.\\n\
keyWidth - initial width in pixels of the white keys.",
NULL
};
/***********************************************************************/
/*----------------------------------------------------------------*
| MyErrorHandler |
*----------------------------------------------------------------*/
int MyErrorHandler (Display *display, XErrorEvent *errorEvent)
{
/* this is most likely invoked when the frequency selected is out of range. */
printf("X Error!\n");
return 0; /* Ignored by X. */
}
/*----------------------------------------------------------------*
| DoQuit |
*----------------------------------------------------------------*/
void DoQuit ()
{
exit(0);
}
/*----------------------------------------------------------------*
| DoHelp |
*----------------------------------------------------------------*/
void DoHelp ()
{
static Widget dlog = NULL;
Arg args[3];
int n;
if (dlog == NULL)
{
dlog = XmCreateInformationDialog(appData->score, "helpDlog", NULL, 0);
XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_HELP_BUTTON) );
XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_CANCEL_BUTTON) );
}
XtManageChild(dlog);
}
/*----------------------------------------------------------------*
| MenuCB |
*----------------------------------------------------------------*/
void MenuCB (Widget w, XtPointer clientData, XtPointer callData)
{
switch ((long)clientData)
{
case MENU_QUIT: DoQuit(); break;
case MENU_HELP: DoHelp(); break;
}
}
/*--------------------------------------------------------------------*
| DoAddVoiceCB |
*--------------------------------------------------------------------*/
void DoAddVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
XmSelectionBoxCallbackStruct *cb = (XmSelectionBoxCallbackStruct *)callData;
String dspName;
Display *newDisplay;
String appName, appClass;
static Widget dlog = NULL;
int n, argc = 0;
Arg args[5];
XtGetApplicationNameAndClass(XtDisplay(w), &appName, &appClass);
XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &dspName);
newDisplay = XtOpenDisplay(context, dspName, appName, appClass, NULL, 0, &argc, NULL);
if (newDisplay != NULL)
AddNewStaff(newDisplay, dspName);
else
{
if (dlog == NULL)
{
n = 0;
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
XtSetArg(args[n], XmNdialogType, XmDIALOG_ERROR); n++;
dlog = XmCreateWarningDialog(appData->score, "warnDlog", args, n);
}
XtManageChild(dlog);
}
if (dspName) XtFree(dspName);
}
/*--------------------------------------------------------------------*
| AddVoiceCB |
*--------------------------------------------------------------------*/
void AddVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
Cardinal n;
Arg args[5];
static Widget dlog = NULL;
if (dlog == NULL)
{
n = 0;
XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
XtSetArg(args[n], XmNdialogType, XmDIALOG_PROMPT); n++;
dlog = XmCreatePromptDialog(appData->score, "dspPromptDlog", args, n);
XtAddCallback(dlog, XmNokCallback, DoAddVoiceCB, NULL);
}
XtManageChild(dlog);
}
/*--------------------------------------------------------------------*
| RemoveVoiceCB |
*--------------------------------------------------------------------*/
void RemoveVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
Widget staff = (Widget)clientData;
StaffRec *staffData;
staffData = GetStaffData(staff);
if (staffData->divider != NULL)
XtDestroyWidget(staffData->divider);
XtDestroyWidget(staff);
if (staffData->next != NULL)
staffData->next->prev = staffData->prev;
if (staffData->prev != NULL)
staffData->prev->next = staffData->next;
else
appData->staffList = staffData->next;
XtFree((char *)staffData);
}
/*--------------------------------------------------------------------*
| ClearVoiceCB |
*--------------------------------------------------------------------*/
void ClearVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
Widget staff = (Widget)clientData;
StaffRec *staffData;
NoteRec *notes, *np;
staffData = GetStaffData(staff);
if (staffData != NULL)
{
while (staffData->notes != NULL)
{
np = staffData->notes;
staffData->notes = staffData->notes->next;
XtFree((char *)np);
}
XClearArea(XtDisplay(staff), XtWindow(staff), 0, 0, 0, 0, TRUE);
}
}
/*--------------------------------------------------------------------*
| ArmKey |
*--------------------------------------------------------------------*/
void ArmKey (XtPointer clientData, XtIntervalId *id)
{
Widget key = (Widget) clientData;
XEvent event;
XtCallbackList cbList;
XtVaGetValues(key, XmNarmCallback, &cbList, NULL);
XtVaSetValues(key, XmNarmCallback, NULL, NULL);
XtCallActionProc(key, "Arm", &event, NULL, 0);
XtVaSetValues(key, XmNarmCallback, cbList, NULL);
}
/*--------------------------------------------------------------------*
| DisarmKey |
*--------------------------------------------------------------------*/
void DisarmKey (XtPointer clientData, XtIntervalId *id)
{
Widget key = (Widget) clientData;
XEvent event;
XtCallActionProc(key, "Disarm", &event, NULL, 0);
}
/*--------------------------------------------------------------------*
| PlayNotes |
| Note that bell duration and interval timing are defined in X as |
| based on milliseconds. Add a timeout for each note to play, then |
| let things go. |
*--------------------------------------------------------------------*/
void PlayNotes (XtPointer clientData, XtIntervalId *id)
{
NoteRec *note = (NoteRec *)clientData;
XEvent event;
XtCallbackList tempCallbackList;
int dt = 0;
while (note != NULL)
{
if (note->noteType < REST)
{
XtAppAddTimeOut(context, dt, PlayNote, note);
/* now to press the keys. */
if (appData->useKeyboard)
{
XtAppAddTimeOut(context, dt, ArmKey, key[note->noteNumber]);
XtAppAddTimeOut(context, dt+note->noteDuration, DisarmKey,
key[note->noteNumber]);
}
}
dt += note->noteDuration;
note = note->next;
}
}
/*--------------------------------------------------------------------*
| PlayVoiceCB |
*--------------------------------------------------------------------*/
void PlayVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
Widget staff = (Widget)clientData;
StaffRec *staffData;
NoteRec *notes;
staffData = GetStaffData(staff);
if (staffData != NULL)
XtAppAddTimeOut(context, 1, PlayNotes, staffData->notes);
}
/*--------------------------------------------------------------------*
| PlayAllCB |
*--------------------------------------------------------------------*/
void PlayAllCB (Widget staff, XtPointer clientData, XtPointer callData)
{
StaffRec *sPtr;
for (sPtr = appData->staffList; sPtr != NULL; sPtr = sPtr->next)
{
XtAppAddTimeOut(context, 1, PlayNotes, sPtr->notes);
}
}
/*--------------------------------------------------------------------*
| DoSaveVoiceCB |
*--------------------------------------------------------------------*/
void DoSaveVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
XmFileSelectionBoxCallbackStruct *fdata =
(XmFileSelectionBoxCallbackStruct *)callData;
Widget staff = (Widget)clientData;
StaffRec *staffData;
NoteRec *note;
FILE *fp;
char *fileName;
static Widget errDlog = NULL;
if (fdata->length > 0)
{
XmStringGetLtoR(fdata->value, XmSTRING_DEFAULT_CHARSET, &fileName);
staffData = GetStaffData(staff);
if (staffData != NULL)
{
fp = fopen(fileName, "w");
for (note = staffData->notes; note != NULL; note = note->next)
{
fprintf(fp, "%d %d %d %d %d\n",
note->noteType,
note->noteDuration,
note->noteNumber,
note->noteIndex,
note->ledgerLine);
}
fclose(fp);
}
if (fileName) XtFree(fileName);
}
}
/*--------------------------------------------------------------------*
| SaveVoiceCB |
*--------------------------------------------------------------------*/
void SaveVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
Widget staff = (Widget)clientData;
static Widget fsdlog = NULL;
static Widget oldStaff;
if (fsdlog == NULL)
{
fsdlog = XmCreateFileSelectionDialog(staff, "saveDlog", NULL, 0);
XtVaSetValues(fsdlog,
XmNautoUnmanage, True,
XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
NULL);
}
else
{
XtRemoveCallback(fsdlog, XmNokCallback, DoSaveVoiceCB, oldStaff);
}
XtAddCallback(fsdlog, XmNokCallback, DoSaveVoiceCB, staff);
oldStaff = staff;
XtManageChild(fsdlog);
}
/*--------------------------------------------------------------------*
| DoLoadVoiceCB |
*--------------------------------------------------------------------*/
void DoLoadVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
XmFileSelectionBoxCallbackStruct *fdata =
(XmFileSelectionBoxCallbackStruct *)callData;
Widget staff = (Widget)clientData;
StaffRec *staffData;
NoteRec *note, *tail;
FILE *fp;
Boolean done = FALSE;
int noteOffset;
char *fileName;
if (fdata->length > 0)
{
XmStringGetLtoR(fdata->value, XmSTRING_DEFAULT_CHARSET, &fileName);
fp = fopen(fileName, "r");
if (fileName) XtFree(fileName);
if (fp != NULL)
{
staffData = GetStaffData(staff);
if (staffData->notes == NULL)
{
tail = NULL;
noteOffset = 0;
}
else
for (tail=staffData->notes, noteOffset = 1;
tail->next != NULL;
tail = tail->next, noteOffset++);
while (!done)
{
note = (NoteRec *) XtMalloc(sizeof(NoteRec));
if (fscanf(fp, "%d %d %d %d %d\n",
¬e->noteType,
¬e->noteDuration,
¬e->noteNumber,
¬e->noteIndex,
¬e->ledgerLine)
> 0)
{
note->noteIndex += noteOffset;
note->display = staffData->display;
note->next = NULL;
if (tail == NULL)
{
staffData->notes = note;
tail = note;
}
else
{
tail->next = note;
tail = tail->next;
}
}
else
{
XtFree((XtPointer)note);
done = TRUE;
}
}
fclose(fp);
}
XClearArea(XtDisplay(w), XtWindow(staff), 0, 0, 0, 0, TRUE);
}
}
/*--------------------------------------------------------------------*
| LoadVoiceCB |
*--------------------------------------------------------------------*/
void LoadVoiceCB (Widget w, XtPointer clientData, XtPointer callData)
{
Widget staff = (Widget)clientData;
static Widget fsdlog = NULL;
static Widget oldStaff;
if (fsdlog == NULL)
{
fsdlog = XmCreateFileSelectionDialog(staff, "loadDlog", NULL, 0);
XtVaSetValues(fsdlog,
XmNautoUnmanage, True,
XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
NULL);
}
else
{
XtRemoveCallback(fsdlog, XmNokCallback, DoLoadVoiceCB, oldStaff);
}
XtAddCallback(fsdlog, XmNokCallback, DoLoadVoiceCB, staff);
oldStaff = staff;
XtManageChild(fsdlog);
}
/*--------------------------------------------------------------------*
| DrawNotes |
| x1,x2 specify the clipping width. If they are the same value, no |
| clipping is performed. |
*--------------------------------------------------------------------*/
void DrawNotes (Widget staff, int x1, int x2)
{
StaffRec *staffData;
NoteRec *notes, *np;
staffData = GetStaffData(staff);
if (staffData != NULL)
for (np = staffData->notes; np != NULL; np = np->next)
DrawNote(staff, np, x1, x2);
}
/*--------------------------------------------------------------------*
| DrawStaffCB |
*--------------------------------------------------------------------*/
void DrawStaffCB (Widget staff, XtPointer clientData, XtPointer callData)
{
XExposeEvent *expEvt =
(XExposeEvent *)((XmDrawingAreaCallbackStruct*)callData)->event;
int i, y;
Dimension width, height;
if (expEvt->count > 1)
return;
XtVaGetValues(XtParent(staff), XmNwidth, &width, NULL);
XtVaGetValues(staff, XmNwidth, &width, XmNheight, &height, NULL);
for (i=4; i<=12; i+=2)
{
y = i*(int)height / 16;
XDrawLine(XtDisplay(staff), XtWindow(staff),
DefaultGCOfScreen(XtScreen(staff)),
0, y, width, y);
}
DrawNotes(staff, expEvt->x, expEvt->x + expEvt->width);
}
/*-------------------------------------------------------------*
| SetAppIcon() |
*-------------------------------------------------------------*/
void SetAppIcon(Widget shell)
{
Pixmap iconPixmap;
iconPixmap = XCreateBitmapFromData(XtDisplay(shell), XtScreen(shell)->root,
(char*)piano_bits, piano_width, piano_height);
XtVaSetValues(shell, XmNiconPixmap, iconPixmap, NULL);
}
/*--------------------------------------------------------------------*
| GetBell |
*--------------------------------------------------------------------*/
void GetBell(Display *dpy)
{
XKeyboardState stateValues;
XGetKeyboardControl(dpy, &stateValues);
orig_percent = stateValues.bell_percent;
orig_pitch = stateValues.bell_pitch;
orig_duration = stateValues.bell_duration;
}
/*--------------------------------------------------------------------*
| SetBell |
*--------------------------------------------------------------------*/
void SetBell(Display *dpy, int pitch, int duration)
{
XKeyboardControl controlValues;
unsigned long valueMask = KBBellPercent | KBBellPitch | KBBellDuration;
controlValues.bell_percent = orig_percent;
controlValues.bell_pitch = pitch;
controlValues.bell_duration = duration;
XChangeKeyboardControl(dpy, valueMask, &controlValues);
}
/*--------------------------------------------------------------------*
| Pitch |
*--------------------------------------------------------------------*/
int Pitch (int note)
{
double x, m, n, f;
/* notes are calculated from the base frequency. */
/* This is the first note on the keyboard. */
/* The frequency of a note = 2^(index / 12). */
x = (double)2.0;
m = (double)note;
n = (double)12.0;
f = (double)appData->baseFrequency * pow(x, (m/n));
return((int)f);
}
/*--------------------------------------------------------------------*
| PlayNote |
*--------------------------------------------------------------------*/
void PlayNote (XtPointer clientData, XtIntervalId *id)
{
NoteRec *note = (NoteRec *)clientData;
SetBell(note->display, Pitch(note->noteNumber), note->noteDuration);
XBell(note->display, 100);
SetBell(note->display, orig_pitch, orig_duration);
}
/*--------------------------------------------------------------------*
| SoundCB |
*--------------------------------------------------------------------*/
void SoundCB (Widget w, XtPointer noteNumber, XtPointer callData)
{
XmPushButtonCallbackStruct *cb = (XmPushButtonCallbackStruct *)callData;
NoteRec note;
/* only play the note if this is a true arm event. */
if (cb->event != NULL)
{
note.display = XtDisplay(w);
note.noteType = EIGHTH;
note.noteNumber = (NoteType)noteNumber;
note.noteIndex = 0;
note.ledgerLine = 0;
note.noteDuration = appData->baseDuration;
PlayNote(¬e, 0);
}
}
#define MUG_SHOTS 11
/*--------------------------------------------------------------------*
| BuildKeys |
*--------------------------------------------------------------------*/
void BuildKeys (Widget parent)
{
Pixmap iconPixmaps[MUG_SHOTS+1];
int i, j = 0, imageCount=MUG_SHOTS;
Boolean pixmapsSet = FALSE, easterEgg = False;
int noteCount = appData->wkeyCount;
static Boolean firstTime = True;
if (firstTime)
{
int x, y, junk;
unsigned int bjunk;
Window wjunk;
firstTime = False;
/* dev team's signature... :-) */
XQueryPointer(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)),
&wjunk, &wjunk, &x, &y, &junk, &junk, &bjunk);
easterEgg = (x+y == 0);
if (easterEgg)
{
Display *dsp = XtDisplay(parent);
Window win = RootWindowOfScreen(XtScreen(parent));
Pixmap shapemask;
XpmAttributes attributes;
attributes.valuemask = 0;
XmeXpmCreatePixmapFromData(dsp, win, none, &(iconPixmaps[0]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, andrew, &(iconPixmaps[1]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, dan, &(iconPixmaps[2]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, dave, &(iconPixmaps[3]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, doug, &(iconPixmaps[4]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, ellis, &(iconPixmaps[5]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, ingeborg, &(iconPixmaps[6]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, jim, &(iconPixmaps[7]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, kamesh, &(iconPixmaps[8]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, scott, &(iconPixmaps[9]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, steve, &(iconPixmaps[10]), &shapemask, &attributes);
XmeXpmCreatePixmapFromData(dsp, win, vania, &(iconPixmaps[11]), &shapemask, &attributes);
appData->wkeyCount = noteCount = imageCount;
}
}
XtVaSetValues(parent, XmNfractionBase, noteCount * 10, NULL);
j = 0;
for (i=0; i<noteCount; i++)
if ( !((i+1)%7) || !((i-2)%7) ) j++; /* handle 2 whitekeys in a row. ie: B-C, E-F. */
else
{
j = j+2;
key[j] = XtVaCreateManagedWidget("bKey", xmPushButtonWidgetClass, parent,
XmNwidth, appData->keyWidth,
XmNleftAttachment, XmATTACH_POSITION,
XmNleftPosition, i*10 + 7,
XmNrightAttachment, XmATTACH_POSITION,
XmNrightPosition, i*10 + 13,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_POSITION,
XmNbottomPosition, noteCount*6,
NULL);
XtAddCallback(key[j], XmNarmCallback, SoundCB, (XtPointer)(long)j);
}
j = 0;
for (i=0; i<noteCount; i++)
{
if ( !(i%7) || !((i-3)%7) ) j--;
j = j+2;
key[j] = XtVaCreateManagedWidget("wKey", xmPushButtonWidgetClass, parent,
XmNheight, appData->keyHeight,
XmNleftAttachment, XmATTACH_POSITION,
XmNleftPosition, i*10,
XmNrightAttachment, XmATTACH_POSITION,
XmNrightPosition, (i+1)*10,
XmNtopAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
NULL);
XtAddCallback(key[j], XmNarmCallback, SoundCB, (XtPointer)(long)j);
if (easterEgg)
{
XtVaSetValues(key[j],
XmNlabelType, XmPIXMAP,
XmNlabelPixmap, iconPixmaps[0],
XmNarmPixmap, iconPixmaps[(i%imageCount)+1],
XmNmarginLeft, 0,
XmNmarginRight, 0,
XmNmarginTop, 100,
NULL);
}
}
}
/*--------------------------------------------------------------------*
| CreateKeyboard |
*--------------------------------------------------------------------*/
Widget CreateKeyboard(Widget parent)
{
int i, j = 0;
Widget keyBoard, wKey, bKey;
keyBoard = XtVaCreateWidget("keyBoard", xmFormWidgetClass, parent, NULL);
BuildKeys(keyBoard);
XtManageChild(keyBoard);
return(keyBoard);
}
/*--------------------------------------------------------------------*
| SetIcon |
*--------------------------------------------------------------------*/
void SetIcon (Widget w, Pixmap cursorPixmap)
{
Pixel fgPix, bgPix;
Cursor cursor;
XColor xcolors[2];
Display *dsp = XtDisplay(w);
xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(dsp));
xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(dsp));
XQueryColors(dsp, DefaultColormapOfScreen(DefaultScreenOfDisplay(dsp)), xcolors, 2);
cursor = XCreatePixmapCursor(dsp, cursorPixmap, cursorPixmap,
&(xcolors[0]), &(xcolors[1]), note_x_hot, note_y_hot);
XDefineCursor(dsp, XtWindow(w), cursor);
}
/*--------------------------------------------------------------------*
| GetStaffData |
| This scans the list of staffs for a match. It returns the data |
| associated with the staff. |
*--------------------------------------------------------------------*/
StaffRec *GetStaffData (Widget staff)
{
StaffRec *sPtr;
for (sPtr = appData->staffList; sPtr != NULL; sPtr = sPtr->next)
{
if (sPtr->staff == staff)
return (sPtr);
}
/* should never get here */
return (NULL);
}
/*--------------------------------------------------------------------*
| DrawNote |
*--------------------------------------------------------------------*/
void DrawNote (Widget staff, NoteRec *note, int x1, int x2)
{
Dimension width, height;
Pixmap notePix, notePixMask;
int x, y;
notePix = appData->noteTable[note->noteType].image;
notePixMask = appData->noteTable[note->noteType].mask;
XtVaGetValues(staff, XmNwidth, &width, XmNheight, &height, NULL);
x = note->noteIndex * 15;
y = (15 - note->ledgerLine) * (int)height / 16 - (note_height/2) - 4;
/* if the position is off the right side of the staff, resize it. */
if ((x + note_width) > (int)width)
XtVaSetValues(staff, XmNwidth, x + 2*note_width, NULL);
if ((x1 != x2) && (x < x1-note_width || x > x2+note_width))
return;
XSetClipMask (XtDisplay(staff), appData->noteGC, notePixMask);
XSetClipOrigin(XtDisplay(staff), appData->noteGC, x, y);
XCopyArea(XtDisplay(staff), notePix, XtWindow(staff), appData->noteGC,
0, 0, note_width, note_height,
x, y);
}
/*--------------------------------------------------------------------*
| SetActiveNote |
*--------------------------------------------------------------------*/
void SetActiveNote (Widget w, NoteType noteType)
{
appData->activeNoteType = noteType;
SetIcon(appData->score, appData->noteTable[noteType].mask);
XSetClipMask(XtDisplay(w), appData->noteGC, appData->noteTable[noteType].mask);
}
/*--------------------------------------------------------------------*
| SetNoteCB |
| callback which sets the active note and modifies the cursor. |
*--------------------------------------------------------------------*/
void SetNoteCB (Widget w, XtPointer clientData, XtPointer callData)
{
NoteType noteType = (NoteType)clientData;
SetActiveNote(w, noteType);
}
/*--------------------------------------------------------------------*
| NoteNumber |
*--------------------------------------------------------------------*/
int NoteNumber (int ledgerLine, Boolean isASharp)
{
int n = 0;
switch (ledgerLine)
{
case 1: n = 1; break;
case 2: n = 3; break;
case 3: n = 5; break;
case 4: n = 6; break;
case 5: n = 8; break;
case 6: n = 10; break;
case 7: n = 12; break;
case 8: n = 13; break;
case 9: n = 15; break;
case 10: n = 17; break;
case 11: n = 18; break;
case 12: n = 20; break;
}
if (isASharp)
return (n+1);
else
return (n);
}
/*--------------------------------------------------------------------*
| DeleteNoteAtPosn |
*--------------------------------------------------------------------*/
void DeleteNoteAtPosn (Widget staff, int x, int y)
{
int ledgerLine, noteIndex, i;
Dimension height;
StaffRec *staffData;
NoteRec *np, *npTemp;
/* find the corresponding ledger line in the staff. */
XtVaGetValues(staff, XmNheight, &height, NULL);
ledgerLine = 15 - (16 * y / (int)height);
noteIndex = x / 15;
staffData = GetStaffData(staff);
if ((staffData != NULL) && (staffData->notes != NULL))
{
if (noteIndex == 1)
{
npTemp = staffData->notes;
staffData->notes = staffData->notes->next;
}
else
{
for (np = staffData->notes;
((noteIndex > 2) && (np->next != NULL));
noteIndex--)
np = np->next;
if (np->next != NULL)
{
npTemp = np->next;
np->next = np->next->next;
}
else
npTemp = NULL;
}
if (npTemp != NULL) XtFree((XtPointer)npTemp);
for (np = staffData->notes, i = 0; np != NULL; np = np->next)
np->noteIndex = ++i;
XClearArea(XtDisplay(staff), XtWindow(staff), 0, 0, 0, 0, TRUE);
}
}
/*--------------------------------------------------------------------*
| AddNoteAtPosn |
*--------------------------------------------------------------------*/
void AddNoteAtPosn (Widget staff, int y, NoteType noteType)
{
int ledgerLine, noteCount, noteDuration;
Dimension height;
StaffRec *staffData;
NoteRec *noteList, *currentNoteList, *np;
Boolean isASharp = FALSE;
/* find the corresponding ledger line in the staff. */
XtVaGetValues(staff, XmNheight, &height, NULL);
ledgerLine = 15 - (16 * y / (int)height);
/* round up to G and down to middle C. */
if (ledgerLine < 1) ledgerLine = 1;
else
if (ledgerLine > 12) ledgerLine = 12;
switch (noteType)
{
case EIGHTH: noteDuration = appData->baseDuration; break;
case EIGHTHDOT: noteDuration = appData->baseDuration*3/2; break;
case EIGHTHSHARP: noteDuration = appData->baseDuration; isASharp = TRUE; break;
case EIGHTHDOTSHARP: noteDuration = appData->baseDuration*3/2; isASharp = TRUE; break;
case QUARTER: noteDuration = appData->baseDuration*2; break;
case QUARTERDOT: noteDuration = appData->baseDuration*3; break;
case QUARTERSHARP: noteDuration = appData->baseDuration*2; isASharp = TRUE; break;
case QUARTERDOTSHARP: noteDuration = appData->baseDuration*3; isASharp = TRUE; break;
case HALF: noteDuration = appData->baseDuration*4; break;
case HALFDOT: noteDuration = appData->baseDuration*6; break;
case HALFSHARP: noteDuration = appData->baseDuration*4; isASharp = TRUE; break;
case HALFDOTSHARP: noteDuration = appData->baseDuration*6; isASharp = TRUE; break;
case REST: noteDuration = appData->baseDuration; break;
case RESTDOT: noteDuration = appData->baseDuration*3/2; break;
default: noteDuration = 0;
}
/* get the staff info - this tells the display to use. */
staffData = GetStaffData(staff);
noteList = (NoteRec *)XtMalloc(sizeof(NoteRec));
noteList->display = staffData->display;
noteList->noteType = noteType;
noteList->noteDuration = noteDuration;
noteList->noteNumber = NoteNumber(ledgerLine, isASharp);
noteList->ledgerLine = ledgerLine;
noteList->next = NULL;
/* get the current list of notes. */
currentNoteList = staffData->notes;
/* find out how many there are. */
for (noteCount=1, np=currentNoteList;
((np != NULL) && (np->next != NULL));
np=np->next, noteCount++)
;
if (np == NULL)
{
staffData->notes = noteList;
noteList->noteIndex = noteCount;
}
else
{
np->next = noteList;
noteList->noteIndex = noteCount+1;
}
DrawNote(staff, noteList, 0, 0);
}
/*--------------------------------------------------------------------*
| AddNoteToStaffCB |
*--------------------------------------------------------------------*/
void AddNoteToStaffCB (Widget staff, XtPointer clientData, XtPointer callData)
{
XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct *)callData;
XButtonEvent *btnEvent = (XButtonEvent *)cb->event;
int vposn, hposn;
if ((btnEvent->button == Button1) && (btnEvent->type == ButtonPress))
{
AddNoteAtPosn(staff, btnEvent->y, appData->activeNoteType);
}
else
if ((btnEvent->button == Button2) && (btnEvent->type == ButtonPress))
{
DeleteNoteAtPosn(staff, btnEvent->x, btnEvent->y);
}
}
/*--------------------------------------------------------------------*
| AddNewStaff |
| Creates data for a new staff and adds it to the global score. |
| A popup menu is attached to the staff. |
*--------------------------------------------------------------------*/
void AddNewStaff (Display *newDisplay, char *dspName)
{
StaffRec *staffData;
staffData = (StaffRec *) XtMalloc(sizeof(StaffRec));
staffData->display = newDisplay;
staffData->notes = NULL;
staffData->next = staffData->prev = NULL;
/* if this is not the first staff, the add a seperator. */
if (appData->staffList != NULL)
staffData->divider =
XtVaCreateManagedWidget("divider", xmSeparatorWidgetClass, appData->score, NULL);
else
staffData->divider = NULL;
staffData->staff =
XtVaCreateManagedWidget("staff", xmDrawingAreaWidgetClass, appData->score,
XmNresizePolicy, XmRESIZE_NONE,
NULL);
XtAddCallback(staffData->staff, XmNexposeCallback, DrawStaffCB, staffData);
XtAddCallback(staffData->staff, XmNinputCallback, AddNoteToStaffCB, staffData);
CreateStaffMenu(appData->score, staffData->staff, dspName);
/*
* add the staff to the staff list.
*/
staffData->next = appData->staffList;
if (appData->staffList != NULL)
appData->staffList->prev = staffData;
appData->staffList = staffData;
}
/*--------------------------------------------------------------------*
| PostStaffMenu |
*--------------------------------------------------------------------*/
void PostStaffMenu (Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch)
{
Widget menu = (Widget)clientData;
XButtonEvent *btnEvent = (XButtonEvent *)event;
int button;
XtVaGetValues(menu, XmNwhichButton, &button, NULL);
if (btnEvent->button == button)
{
XmMenuPosition(menu, btnEvent);
XtManageChild(menu);
}
}
/*--------------------------------------------------------------------*
| CreateStaffMenu |
*--------------------------------------------------------------------*/
void CreateStaffMenu (Widget score, Widget staff, char *dspName)
{
Widget popupMenu, popupBtn[8];
popupMenu = XmCreatePopupMenu(staff, "popupMenu", NULL, 0);
XtAddEventHandler(staff, ButtonPressMask, False, PostStaffMenu, popupMenu);
XtVaCreateManagedWidget(dspName, xmLabelWidgetClass, popupMenu, NULL);
XtVaCreateManagedWidget("line", xmSeparatorWidgetClass, popupMenu, NULL);
popupBtn[1] = XtVaCreateManagedWidget("popupBtn1", xmPushButtonWidgetClass, popupMenu, NULL);
popupBtn[2] = XtVaCreateManagedWidget("popupBtn2", xmPushButtonWidgetClass, popupMenu, NULL);
popupBtn[3] = XtVaCreateManagedWidget("popupBtn3", xmPushButtonWidgetClass, popupMenu, NULL);
popupBtn[4] = XtVaCreateManagedWidget("popupBtn4", xmPushButtonWidgetClass, popupMenu, NULL);
popupBtn[5] = XtVaCreateManagedWidget("popupBtn5", xmPushButtonWidgetClass, popupMenu, NULL);
popupBtn[6] = XtVaCreateManagedWidget("popupBtn6", xmPushButtonWidgetClass, popupMenu, NULL);
popupBtn[7] = XtVaCreateManagedWidget("popupBtn7", xmPushButtonWidgetClass, popupMenu, NULL);
/* if this is the first one, then don't allow it to be removed. */
if (appData->staffList == NULL) XtVaSetValues(popupBtn[2], XmNsensitive, False, NULL);
XtAddCallback(popupBtn[1], XmNactivateCallback, AddVoiceCB, NULL);
XtAddCallback(popupBtn[2], XmNactivateCallback, RemoveVoiceCB, staff);
XtAddCallback(popupBtn[3], XmNactivateCallback, ClearVoiceCB, staff);
XtAddCallback(popupBtn[4], XmNactivateCallback, PlayVoiceCB, staff);
XtAddCallback(popupBtn[5], XmNactivateCallback, PlayAllCB, NULL);
XtAddCallback(popupBtn[6], XmNactivateCallback, SaveVoiceCB, staff);
XtAddCallback(popupBtn[7], XmNactivateCallback, LoadVoiceCB, staff);
}
/*--------------------------------------------------------------------*
| CreateScore |
| Creates the rowcolumn to holds the staffs. Also creates an option |
| menu for manipulating the staffs. The staff is inserted into the |
| global appData list of scores. |
*--------------------------------------------------------------------*/
void CreateScore(Widget parent)
{
Widget scoreWin;
scoreWin = XtVaCreateManagedWidget("scoreWin", xmScrolledWindowWidgetClass, parent,
XmNscrollingPolicy, XmAUTOMATIC, NULL);
appData->score =
XtVaCreateManagedWidget("score", xmRowColumnWidgetClass, scoreWin,
XmNadjustLast, FALSE,
XmNnumColumns, 1,
XmNorientation, XmVERTICAL,
XmNpacking, XmPACK_TIGHT,
NULL);
AddNewStaff(XtDisplay(appData->score), LOCAL_NAME);
}
/*--------------------------------------------------------------------*
| CreateNotebook |
*--------------------------------------------------------------------*/
Widget CreateNotebook(Widget parent)
{
NoteType noteType;
Widget notebook, noteButton[LAST_NOTE];
Pixel fg, bg;
Display *dsp = XtDisplay(parent);
Window win = RootWindowOfScreen(XtScreen(parent));
int d = DefaultDepthOfScreen(XtScreen(parent));
notebook = XtVaCreateManagedWidget("notebook", xmRowColumnWidgetClass, parent, NULL);
/*
* create a pushbutton for each note type and setup its callback.
*/
for (noteType = (NoteType)0; noteType < LAST_NOTE; noteType++)
{
noteButton[noteType] =
XtVaCreateManagedWidget(noteName[noteType],
xmPushButtonWidgetClass, notebook,
XmNlabelType, XmPIXMAP,
XmNlabelPixmap, appData->noteTable[noteType].image,
NULL);
XtAddCallback(noteButton[noteType], XmNactivateCallback, SetNoteCB,
(XtPointer)noteType);
}
return notebook;
}
/*-------------------------------------------------------------*
| Resource Converter: CvtStrToFloat |
*-------------------------------------------------------------*/
void CvtStrToFloat (XrmValue *args, Cardinal *nargs, XrmValue *fromVal, XrmValue *toVal)
{
static float result;
if (sscanf((char *)fromVal->addr, "%f", &result) == 1)
{
toVal->size = sizeof(float);
toVal->addr = (XtPointer) &result;
}
else
XtStringConversionWarning((char *)fromVal->addr, "Float");
}
/*----------------------------------------------------------------*
| GetAppResources |
| The following resources are supported in piano: |
| .baseDuration: (int) |
| -- frequencey in Hz for middle C. |
| .baseFrequency: (float) |
| -- duration in ms of a quarter note. |
| .wkeyCount: (int) |
| -- specifies the number of white keys. |
| .keyHeight: (int) |
| -- white key pixel height. black keys are calculated. |
| .keyWidth: (int) |
| -- white key pixel width. black keys are calculated. |
*----------------------------------------------------------------*/
AppData *GetAppResources (Widget w)
{
AppData *appData;
if ((appData = (AppData *) XtCalloc(1, sizeof(AppData))) == NULL)
{
printf(EMSG1);
exit(0);
}
XtGetApplicationResources(w, (XtPointer)appData,
appRes, XtNumber(appRes), NULL, 0);
return (appData);
}
/*--------------------------------------------------------------------*
| GetNoteImagePixmap |
*--------------------------------------------------------------------*/
Pixmap GetNoteImagePixmap(Widget w, NoteType note)
{
Pixel fg, bg;
Display *dsp = XtDisplay(w);
Window win = RootWindowOfScreen(XtScreen(w));
int d = DefaultDepthOfScreen(XtScreen(w));
unsigned char *data = NULL;
XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL);
switch (note)
{
case EIGHTH: data = eighth_bits; break;
case EIGHTHDOT: data = eighth_dot_bits; break;
case EIGHTHSHARP: data = eighth_sharp_bits; break;
case EIGHTHDOTSHARP: data = eighth_dot_sharp_bits; break;
case QUARTER: data = quarter_bits; break;
case QUARTERDOT: data = quarter_dot_bits; break;
case QUARTERSHARP: data = quarter_sharp_bits; break;
case QUARTERDOTSHARP: data = quarter_dot_sharp_bits;break;
case HALF: data = half_bits; break;
case HALFDOT: data = half_dot_bits; break;
case HALFSHARP: data = half_sharp_bits; break;
case HALFDOTSHARP: data = half_dot_sharp_bits; break;
case REST: data = rest_bits; break;
case RESTDOT: data = rest_dot_bits; break;
default: ;
}
return(XCreatePixmapFromBitmapData(dsp, win, (char*)data,
note_width, note_height, fg, bg, d));
}
/*--------------------------------------------------------------------*
| GetNoteMaskPixmap |
*--------------------------------------------------------------------*/
Pixmap GetNoteMaskPixmap(Widget w, NoteType note)
{
Pixel fg, bg;
Display *dsp = XtDisplay(w);
Window win = RootWindowOfScreen(XtScreen(w));
int d = DefaultDepthOfScreen(XtScreen(w));
unsigned char *data = NULL;
XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL);
switch (note)
{
case EIGHTH: data = eighth_bits; break;
case EIGHTHDOT: data = eighth_dot_bits; break;
case EIGHTHSHARP: data = eighth_sharp_bits; break;
case EIGHTHDOTSHARP: data = eighth_dot_sharp_bits; break;
case QUARTER: data = quarter_bits; break;
case QUARTERDOT: data = quarter_dot_bits; break;
case QUARTERSHARP: data = quarter_sharp_bits; break;
case QUARTERDOTSHARP: data = quarter_dot_sharp_bits;break;
case HALF: data = half_bits; break;
case HALFDOT: data = half_dot_bits; break;
case HALFSHARP: data = half_sharp_bits; break;
case HALFDOTSHARP: data = half_dot_sharp_bits; break;
case REST: data = rest_bits; break;
case RESTDOT: data = rest_dot_bits; break;
default: ;
}
return (XCreatePixmapFromBitmapData(dsp, win, (char*)data,
note_width, note_height, 1, 0, 1));
}
/*--------------------------------------------------------------------*
| BuildNoteTable |
*--------------------------------------------------------------------*/
void BuildNoteTable (Widget w)
{
NoteType i;
for (i = (NoteType)0; i<LAST_NOTE; i++)
{
appData->noteTable[i].image = GetNoteImagePixmap(w, i);
appData->noteTable[i].mask = GetNoteMaskPixmap(w, i);
}
}
/*--------------------------------------------------------------------*
| CreateMenuBar |
*--------------------------------------------------------------------*/
void CreateMenuBar (Widget parent)
{
Cardinal n;
Arg args[10];
Widget menuBar;
Widget cascade1, cascade2;
Widget menuPane1, menuPane2;
Widget b1;
menuBar = XmCreateMenuBar(parent, "menuBar", NULL, 0);
menuPane1 = XmCreatePulldownMenu(menuBar, "menuPane1", NULL, 0);
menuPane2 = XmCreatePulldownMenu(menuBar, "menuPane2", NULL, 0);
b1 = XtCreateManagedWidget("b1", xmPushButtonWidgetClass, menuPane1, NULL,0);
n = 0;
XtSetArg(args[n], XmNsubMenuId, menuPane1); n++;
cascade1 = XmCreateCascadeButton(menuBar, "cascade1", args, n);
XtManageChild(cascade1);
n = 0;
cascade2 = XmCreateCascadeButton(menuBar, "cascade2", args, n);
XtManageChild(cascade2);
n = 0;
XtSetArg(args[n], XmNmenuHelpWidget, cascade2); n++;
XtSetValues(menuBar, args, n);
XtAddCallback(b1, XmNactivateCallback, MenuCB, (XtPointer)MENU_QUIT);
XtAddCallback(cascade2, XmNactivateCallback, MenuCB, (XtPointer)MENU_HELP);
XtManageChild(menuBar);
}
/*--------------------------------------------------------------------*
| Main |
*--------------------------------------------------------------------*/
int
main(int argc, char **argv)
{
Widget shell, mainWin, panedWin;
Widget keyboard, notebook;
int fn;
Pixel fg, bg;
XGCValues values;
shell = XtVaAppInitialize(&context, APP_CLASS, NULL, 0, &argc, argv, fallback, NULL);
XSetErrorHandler(MyErrorHandler);
XtAddConverter(XtRString, XtRFloat, CvtStrToFloat, NULL, 0);
appData = GetAppResources(shell);
mainWin = XmCreateMainWindow(shell, "mainWin", NULL, 0);
XtManageChild(mainWin);
CreateMenuBar(mainWin);
panedWin = XtVaCreateManagedWidget("panedWin",
xmPanedWindowWidgetClass, mainWin, NULL);
keyboard = CreateKeyboard(panedWin);
CreateScore(panedWin);
BuildNoteTable(panedWin);
notebook = CreateNotebook(panedWin);
SetAppIcon(shell);
XtRealizeWidget(shell);
/* get the note GC */
XtVaGetValues(appData->score, XmNforeground, &fg, XmNbackground, &bg, NULL);
values.foreground = fg;
values.background = bg;
appData->noteGC = XtGetGC(appData->score, GCForeground | GCBackground, &values);
SetActiveNote(appData->score, QUARTER);
/* save the old bell values so that they can be restored. */
GetBell(XtDisplay(shell));
XtAppMainLoop(context);
return 0; /* make compiler happy */
}