InterfaceImplC.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 1:05 pm
Edited by Doug Wyatt, June 15, 1984 10:43:07 am PDT
DIRECTORY
CursorDefs,
Beam USING [SetStems],
Chord USING [Draw],
Event USING [AddNote, Adjust, Sync],
Inline USING [LowHalf],
Interface USING [count, Object, Wait],
MusicDefs,
Note USING [Draw, Duration, FindChord, GetBackTie, SetAccidental],
Piece USING [AddEvent, CleanUpEvents, NearestEvent, Overflow],
Score USING [GetKey, NormalAcc, PriorNote, Redraw, ShowPitch],
Selection, -- USING everything
Sheet USING [FindSection, Height, NearestStaff, NearestTime],
UserTerminal USING [mouse, GetCursorPattern, SetCursorPattern];
InterfaceImplC: CEDAR PROGRAM
IMPORTS
Beam, Chord, Inline, Interface, MusicDefs, Note,
Piece, Score, Selection, Sheet, Event, UserTerminal
EXPORTS Interface =
BEGIN
OPEN MusicDefs, UserTerminal;
Error: SIGNAL = CODE;
******************************************************************
deleting objects
******************************************************************
defaultSync: SyncPTR ← zone.NEW[EventRec.sync[3]];
InsertNote: PUBLIC PROC[score: ScorePTR, object: Interface.Object] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
time: Time;
height, key: INTEGER;
n: NotePTR ← zone.NEW[NoteRec];
n.value ← IF object = rest THEN quarter ELSE unknown;
n.rest ← (object = rest);
n.voice ← (IF score.sheet.voice # noVoice THEN score.sheet.voice ELSE 0);
FOR i: CARDINAL IN [0..Selection.selection.length) DO
IF Selection.selection[i] = NIL THEN LOOP;
n.value ← Selection.selection[i].value;
n.dotted ← Selection.selection[i].dotted;
n.grace ← Selection.selection[i].grace;
EXIT; ENDLOOP;
n.sync ← defaultSync;
n.duration ← Inline.LowHalf[7*(Note.Duration[n, 128]/8)];
[time, height] ← Sheet.NearestTime[score.sheet, ];
n.show ← FALSE;
n.sync.time ← time;
key ← Score.GetKey[score, time];
n.staff ← Sheet.NearestStaff[score.sheet, time, height];
IF n.rest
THEN [n.pitch, n.spelled] ← RestPosition[score, height, key, n.staff, time]
ELSE [n.pitch, n.spelled] ← DefaultPitch[score, height, key, n.staff, time];
n.stemUp ← StemDirection[score, n.pitch, n.staff, time];
n.voice ← IF score.sheet.voice # noVoice THEN score.sheet.voice ELSE 0;
IF score.sheet.voice # noVoice THEN score.maxVoice ← MAX[score.maxVoice, score.sheet.voice];
SetBrush[score, black, invert];
Note.Draw[score, n];
MoveNote[score, n];
Selection.Clear[];
Selection.AddNote[score, n];
score.command ← TRUE;
};
DefaultPitch: PROC[score: ScorePTR, height, key: INTEGER, staff: CARDINAL, time: Time] RETURNS[pitch: INTEGER, spelled: Accidental] = {
prior: NotePTR;
normal: Accidental;
index: CARDINAL = EventIndex[score, time];
pitch ← WhiteKey[score, time, height, staff];
pitch ← Score.ShowPitch[score.sheet, pitch, natural, key];
height ← Sheet.Height[score.sheet, time, pitch, staff];
prior ← Score.PriorNote[score, pitch, height, index];
IF score.sheet.noCarry OR prior = NIL THEN
IF Score.NormalAcc[key, pitch] = natural
THEN IF key < 0
THEN RETURN[pitch-1, inKey]
ELSE RETURN[pitch+1, inKey]
ELSE RETURN[pitch, inKey];
pitch ← prior.pitch;
spelled ← prior.spelled;
IF spelled = inKey THEN RETURN;
IF spelled = doubleSharp THEN RETURN;
IF spelled = doubleFlat THEN RETURN;
can we substitute inKey for spelled?
normal ← Score.NormalAcc[key, pitch];
IF normal # inKey THEN RETURN;
normal ← Score.NormalAcc[0, pitch];
IF spelled = natural AND normal = inKey THEN spelled ← inKey;
IF spelled = flat AND key < 0 AND normal = sharp THEN spelled ← inKey;
IF spelled = sharp AND key >= 0 AND normal = sharp THEN spelled ← inKey;
};
Spell: PROC[a: Accidental] RETURNS[CARDINAL] = INLINE
{RETURN[SELECT a FROM doubleSharp => 4, sharp => 3,
natural, inKey => 2, flat => 1, ENDCASE => 0]};
EventIndex: PROC[score: ScorePTR, time: Time] RETURNS[s: CARDINAL] = INLINE {
s ← Piece.NearestEvent[score, time];
IF score[s] # NIL AND score[s].time >= time THEN RETURN[s];
FOR i: CARDINAL DECREASING IN [0..s+10) DO
IF score[i] = NIL THEN LOOP;
IF score[i].time > time THEN LOOP;
RETURN[i+1];
ENDLOOP;
};
******************************************************************
moving things
******************************************************************
MoveNote: PUBLIC PROC[score: ScorePTR, n: NotePTR] = {
OPEN CursorDefs;
s: SyncPTR;
c: ChordPTR;
oldTime, time: Time;
scale, offset: INTEGER;
spell, oldSpell: INTEGER;
pitch, oldPitch: INTEGER;
tempCursor: Cursor ← GetCursorPattern[];
backtie, forwardtie: NotePTR ← NIL;
oldHeight, height, x, y, key: INTEGER;
backEqual, forwardEqual: BOOL;
yellow: {up, down1, clicked, down2} ← up;
AccMode: PROC = {
scale ← 5;
oldSpell ← spell ← Spell[IF n.spelled = inKey
THEN DefaultAcc[Score.GetKey[score, oldTime], n.pitch]
ELSE n.spelled];
oldPitch ← 1000; -- forces a move
offset ← oldSpell*4*scale+2*scale;
pitch ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key];
oldHeight ← Sheet.Height[score.sheet, n.sync.time, pitch, n.staff];
y ← mouse.y;
oldSpell ← 6};
NonAccMode: PROC = {
scale ← 1;
offset ← scale*10;
oldPitch ← 1000; -- forces a move
pitch ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key];
oldHeight ← Sheet.Height[score.sheet, n.sync.time, pitch, n.staff];
y ← mouse.y};
IF n = NIL THEN RETURN;
SetCursorPattern[ALL[0]];
c ← Note.FindChord[n];
SetBrush[score, black, invert];
oldTime ← n.sync.time;
score.sheet.dirty1 ← score.sheet.dirty2 ← time ← oldTime;
IF n.tie # NIL THEN {
backEqual ← (n.tie.pitch = n.pitch);
backtie ← n.tie;
n.tie ← NIL};
IF n.tied THEN {
forwardtie ← Note.GetBackTie[score, n];
forwardEqual ← (forwardtie.pitch = n.pitch)};
FOR i: CARDINAL IN [0..defaultSync.length) DO defaultSync.note[i] ← NIL; ENDLOOP;
FOR i: CARDINAL IN [0..IF c = NIL THEN 1 ELSE c.length) DO
note: NotePTR ← IF c = NIL THEN n ELSE c.note[i];
Selection.RemoveNote[note];
IF note.beam # NIL THEN SetDirty[score, note.beam.sync1.time, note.beam.sync2.time];
IF note.sync # NIL THEN SetDirty[score, note.sync.time, note.sync.time];
defaultSync ← Event.AddNote[score, defaultSync, note];
ENDLOOP;
Piece.CleanUpEvents[score];
defaultSync.time ← oldTime;
x ← mouse.x; y ← mouse.y;
key ← Score.GetKey[score, oldTime];
NonAccMode[];
oldPitch ← pitch;
WHILE BlueBug[] DO Interface.Wait[1];
SELECT yellow FROM
down1 => IF ~YellowBug[] THEN yellow ← clicked;
clicked => IF YellowBug[] THEN yellow ← down2;
down2 => IF ~YellowBug[] THEN {yellow ← up; NonAccMode[]};
up => IF YellowBug[] THEN {yellow ← down1; AccMode[]};
ENDCASE;
time ← oldTime+(mouse.x-x);
[height, spell] ← Scale[oldHeight, (y+offset-mouse.y), scale];
key ← Score.GetKey[score, time];
pitch ← WhiteKey[score, time, height, n.staff];
IF yellow = up THEN oldSpell ← spell;
IF pitch = oldPitch AND spell = oldSpell AND time = n.sync.time THEN LOOP;
WE HAVE A MOVEMENT
IF c # NIL THEN Chord.Draw[score, c] ELSE Note.Draw[score, n];
n.sync.time ← time;
IF pitch # oldPitch AND ~n.rest AND yellow = up THEN {
n.show ← FALSE;
[n.pitch, n.spelled] ← DefaultPitch[score, height, key, n.staff, time]};
IF spell # oldSpell AND ~n.rest THEN {
score.flash ← FALSE;
n.show ← TRUE;
SetAccidental[score, n, pitch, spell]};
oldPitch ← pitch;
oldSpell ← spell;
Event.Adjust[score, n.sync];
IF n.rest THEN [n.pitch, n.spelled] ← RestPosition[score, height, key, n.staff, time];
IF n.beam = NIL THEN n.stemUp ← StemDirection[score, pitch, n.staff, n.sync.time];
IF c # NIL THEN Chord.Draw[score, c] ELSE Note.Draw[score, n];
ENDLOOP;
ALL MOVEMENTS FINISHED
IF score.sheet.display = physical AND c # NIL
THEN FOR i: CARDINAL IN [0..c.length) DO
c.note[i].toc ← c.note[i].sync.time*score.sheet.density;
ENDLOOP
ELSE n.toc ← n.sync.time*score.sheet.density;
IF c # NIL THEN Chord.Draw[score, c] ELSE Note.Draw[score, n];
s ← Event.Sync[score[Piece.NearestEvent[score: score, t: time, syncsOnly: TRUE]]];
IF s = NIL OR ABS[s.time-time] > 2 THEN
{
s ← zone.NEW[EventRec.sync[5]];
s.time ← time;
score ← Piece.AddEvent[score, s];
};
FOR i: CARDINAL IN [0..(IF c = NIL THEN 1 ELSE c.length)) DO
temp: NotePTR ← IF c = NIL THEN n ELSE c.note[i];
s ← Event.AddNote[score, s, temp];
ENDLOOP;
IF n.beam # NIL THEN Beam.SetStems[score.sheet, n.beam];
Selection.AddNote[score, NIL];
SetCursorPattern[tempCursor];
score.flash ← FALSE;
WHILE YellowBug[] DO NULL; ENDLOOP;
n.tie ← backtie;
change the pitches of the ties if they were equal before
WHILE backtie # NIL AND backEqual DO
note: NotePTR ← backtie;
backtie ← backtie.tie;
IF backtie # NIL THEN backEqual ← (backtie.pitch = note.pitch);
note.pitch ← pitch;
note.spelled ← n.spelled;
SetDirty[score, note.sync.time, note.sync.time];
ENDLOOP;
WHILE forwardtie # NIL AND forwardEqual DO
note: NotePTR ← forwardtie;
forwardtie ← Note.GetBackTie[score, forwardtie];
IF forwardtie # NIL THEN forwardEqual ← (forwardtie.pitch = note.pitch);
note.pitch ← pitch;
note.spelled ← n.spelled;
SetDirty[score, note.sync.time, note.sync.time];
ENDLOOP;
SetDirty[score, s.time, s.time];
Score.Redraw[score, score.sheet.dirty1, score.sheet.dirty2];
Piece.CleanUpEvents[score];
Interface.count ← TRUE;
};
tryX: INTEGER ← 3;
Scale: PROC[oldHeight, delta, scale: INTEGER] RETURNS[height, spell: INTEGER] = {
height ← oldHeight+ModDiv[delta, (5*scale)];
spell ← Mod[delta, 20*scale]/(4*scale);
};
WhiteKey: PROC[score: ScorePTR, t: Time, height: INTEGER, staff: CARDINAL] RETURNS[INTEGER] = {
delta: INTEGER;
staffHeight: INTEGER = Sheet.Height[score.sheet, t, , staff];
pitch: INTEGER ← score.sheet[Sheet.FindSection[score.sheet, t]].staves.staff[staff].pitch;
delta ← ModDiv[(height-staffHeight), 4];
IF delta >= 0
THEN FOR i: CARDINAL IN [0..ABS[delta]) DO
SELECT Mod[pitch, 12] FROM
0, 7 => pitch ← pitch+1;
ENDCASE => pitch ← pitch + 2;
ENDLOOP
ELSE FOR i: CARDINAL IN [0..ABS[delta]) DO
SELECT Mod[pitch, 12] FROM
1, 8 => pitch ← pitch-1;
ENDCASE => pitch ← pitch - 2;
ENDLOOP;
RETURN[pitch];
};
ModDiv: PROC[top, bottom: INTEGER] RETURNS[d: INTEGER] = INLINE
{d ← top/bottom;
IF top < 0 AND Mod[top, bottom] # 0 THEN d ← d-1};
DefaultAcc: PROC[key, pitch: INTEGER] RETURNS[Accidental] = {
SELECT Score.NormalAcc[0, pitch] FROM
inKey => RETURN[natural];
sharp => RETURN[IF key < 0 THEN flat ELSE sharp];
ENDCASE => ERROR;
};
SetAccidental: PROC[score: ScorePTR, n: NotePTR, pitch, spell: INTEGER] = {
key: INTEGER;
normal: Accidental;
IF n.rest THEN RETURN;
SELECT spell FROM
0 => {n.pitch ← pitch-2; Note.SetAccidental[score, n, doubleFlat]}; 
1 => {n.pitch ← pitch-1; Note.SetAccidental[score, n, flat]};
2 => {n.pitch ← pitch; Note.SetAccidental[score, n, natural]};
3 => {n.pitch ← pitch+1; Note.SetAccidental[score, n, sharp]};
4 => {n.pitch ← pitch+2;Note.SetAccidental[score, n, doubleSharp]};
ENDCASE => ERROR;
IF score.flash THEN Error;
IF n.spelled NOT IN [sharp..flat] THEN RETURN;
key ← Score.GetKey[score, n.sync.time];
normal ← Score.NormalAcc[key, n.pitch];
IF normal # inKey THEN RETURN;
normal ← Score.NormalAcc[0, n.pitch];
IF n.spelled = natural AND normal = inKey THEN n.spelled ← inKey;
IF n.spelled = flat AND key < 0 AND normal = sharp THEN n.spelled ← inKey;
IF n.spelled = sharp AND key >= 0 AND normal = sharp THEN n.spelled ← inKey;
};
RestPosition: PROC[score: ScorePTR, height, key: INTEGER, staff: CARDINAL, time: Time] RETURNS[pitch: INTEGER, spelled: Accidental] = {
w: INTEGER = 8;
staffHeight: INTEGER = Sheet.Height[score.sheet, time, , staff];
height ← 8*ModDiv[(height-staffHeight), w] +w-4+staffHeight;
pitch ← WhiteKey[score, time, height, staff];
pitch ← Score.ShowPitch[score.sheet, pitch, natural, key];
spelled ← natural;
};
StemDirection: PROC[score: ScorePTR, pitch, staff: INTEGER, t: Time] RETURNS[BOOL] = {
RETURN[Sheet.Height[score.sheet, t, pitch, staff] < Sheet.Height[score.sheet, t, , staff]+16];
};
END.