InterfaceImplB.mesa
Copyright (C) 1981, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: December 15, 1981 4: 06 PM
Edited by Doug Wyatt, June 14, 1984 3:42:04 pm PDT
DIRECTORY
CursorDefs,
Beam USING [Draw, SetStems],
Graphics USING [DrawScreenArea, SetPaint, SetTexture],
Interface USING [InsertNote, Object],
MusicDefs,
Note USING [Draw, DrawTie, SetAccidental, SetEmbellishment],
Piece USING [AddSync, DeleteSync, NearestNote, NearestObject, RemoveSync],
ProcessDefs USING [SetTimeout],
Real USING [FixI],
Score USING [GetKey, Redraw],
Selection, -- USING everything
Sheet USING [AlternateTime, DrawClef, DrawOctava, FindLine, FindSection, GetStyle, Height, NearestStaff, NearestTime, NextLine, NextStaff, NormalPitch, OctavaHeight, PriorStaff, Reset, ScreenPoint, SetClef, SetOctava],
Sync USING [Draw, GetScoreIndex, GetStaff, Octava],
Utility; -- USING everything
InterfaceImplB: CEDAR MONITOR
IMPORTS Beam, Graphics, Interface, MusicDefs, Note, Piece, ProcessDefs, Real, Score, Selection, Sheet, Sync, Utility
EXPORTS Interface =
BEGIN
OPEN Graphics, MusicDefs;
timeout: CONDITION;
Error: SIGNAL = CODE;
******************************************************************
deleting objects
******************************************************************
count: PUBLIC BOOL;
DeleteGraphical: PUBLIC PROC = {
type: EventType;
obj: ObjectType;
s: SyncPTR ← NIL;
time, carry, next: Time;
p: UnspecifiedPTR ← NIL;
value, oldClef, oldStyle: INTEGER ← 0;
SetBrush[black, invert];
WHILE YellowBug[] DO
[obj, p] ← Piece.NearestObject[];
IF p#NIL THEN SELECT obj FROM
note => Note.Draw[p];
measure => Sync.Draw[p];
leftBeam => [] ← Beam.Draw[p];
rightBeam=> [] ← Beam.Draw[p];
ENDCASE;
Wait[1];
IF p#NIL THEN SELECT obj FROM
note => Note.Draw[p];
measure => Sync.Draw[p];
leftBeam => [] ← Beam.Draw[p];
rightBeam=> [] ← Beam.Draw[p];
ENDCASE;
ENDLOOP;
IF p#NIL AND obj=note THEN Selection.RemoveNote[p];
IF obj#measure OR p=NIL THEN RETURN;
s ← LOOPHOLE[p, SyncPTR];
IF voice AND s.type IN [clef..octava2] THEN RETURN;
type ← s.type; time ← s.time; value ← s.value;
IF type=clef THEN oldClef ← Clef[value, time];
IF type=staves THEN y ← sheet[Sheet.FindLine[time]].y;
IF type=staves THEN {
next ← sheet[Sheet.NextLine[Sheet.FindLine[time]]].time;
oldStyle ← Sheet.GetStyle[next]};
carry ← GetCarry[s];
Piece.DeleteSync[s];
SetDirty[time, time];
IF type=keySignature THEN SetDirty[time, carry];
IF type=keySignature OR type=clef THEN ResetSheet[time, time];
IF type=clef AND oldClef#Clef[value, time] THEN SetDirty[time, carry];
IF type=staves THEN {
Sheet.Reset[];
IF Sheet.GetStyle[time]#value THEN SetDirty[begin, endTime];
IF Sheet.GetStyle[next]#oldStyle THEN SetDirty[begin, endTime]};
IF sheet[Sheet.FindLine[time]].y#y THEN SetDirty[begin, endTime];
IF type IN [octava1..octava2] THEN Sheet.Reset[];
Score.Redraw[min, max];
count ← TRUE;
};
ChangeSpelling: PROC[object: Interface.Object] = {
n: NotePTR;
min �
max ← -1;
SetBrush[black, invert];
WHILE BlueBug[] DO
IF (n ← Piece.NearestNote[])=NIL THEN LOOP;
Note.Draw[n];
Note.Draw[n];
ENDLOOP;
SetBrush[black, paint];
IF n=NIL OR n.rest THEN RETURN;
IF voice AND n.voice#selectedVoice THEN RETURN;
SELECT object FROM
doubleFlat => Note.SetAccidental[n, doubleFlat];
flat => Note.SetAccidental[n, flat];
natural => Note.SetAccidental[n, natural];
inKey => Note.SetAccidental[n, inKey];
sharp => Note.SetAccidental[n, sharp];
doubleSharp=> Note.SetAccidental[n, doubleSharp];
ENDCASE;
IF flash THEN {Flash[]; RETURN};
IF n.spelled#inKey THEN n.show ← TRUE ELSE n.show ← FALSE;
Score.Redraw[min, max];
};
Embellish: PROC[object: Interface.Object] = {
x, y: INTEGER;
n: NotePTR ← NIL;
min �
max ← -1;
SetBrush[black, invert];
WHILE BlueBug[] DO
[x, y] ← Sheet.ScreenPoint[];
IF (n ← Piece.NearestNote[x, y])=NIL THEN LOOP;
Note.Draw[n];
Note.Draw[n];
ENDLOOP;
SetBrush[black, paint];
IF n=NIL OR n.rest THEN RETURN;
SELECT object FROM
trill  => Note.SetEmbellishment[n, trill];
mordent1 => Note.SetEmbellishment[n, mordent1];
mordent2 => Note.SetEmbellishment[n, mordent2];
ENDCASE;
Score.Redraw[min, max];
};
******************************************************************
inserting objects
******************************************************************
Insert: PROC[object: Interface.Object] = {
OPEN CursorDefs;
tempCursor: Cursor ← cursor^;
cursor^ ← ALL[0];
SELECT object FROM
staves, measure, repeat1, repeat2, doubleMeasure, endMeasure => InsertMeasure[object];
bass, treble => InsertClefChange[object];
octava => {cursor^ ← tempCursor; InsertOctava[object]};
doubleFlat, flat, natural, inKey, sharp, doubleSharp => ChangeSpelling[object];
trill, mordent1, mordent2 => Embellish[object];
note, rest => Interface.InsertNote[object];
ENDCASE => ERROR;
cursor^ ← tempCursor;
count ← TRUE;
};
InsertMeasure: PROC[object: Interface.Object] = {
x, y: INTEGER;
s: SyncPTR ← Utility.NewSync[];
SELECT object FROM
measure => s.type ← measure;
staves => s.type ← staves;
repeat1 => s.type ← repeat1;
repeat2 => s.type ← repeat2;
endMeasure => s.type ← endMeasure;
doubleMeasure => s.type ← doubleMeasure;
ENDCASE => ERROR;
[x, y] ← Sheet.ScreenPoint[];
[s.time,] ← Sheet.NearestTime[x, y];
SetBrush[black, invert];
WHILE BlueBug[] DO
Sync.Draw[s];
Sync.Draw[s];
[x, y] ← Sheet.ScreenPoint[];
[s.time,] ← Sheet.NearestTime[];
ENDLOOP;
IF YellowBug[] THEN RETURN;
IF object=staves THEN {
s.value ← Sheet.GetStyle[s.time];
LOOPHOLE[s.event, Staves] ← style[s.value]};
Piece.AddSync[score, s];
SetDirty[s.time, s.time];
Score.Redraw[min, max];
};
InsertClefChange: PROC[object: Interface.Object] = {
old, new: Section;
sync: SyncPTR ← NIL;
pitch, height: INTEGER;
oldTime, newTime: Time ← 0;
change: BOOLFALSE;
oldStaff, newStaff, oldClef: INTEGER ← 10;
SELECT object FROM
bass => pitch ← 27;
treble => pitch ← 48;
ENDCASE => Error;
SetBrush[black, invert];
[newTime, height] ← Sheet.NearestTime[];
newStaff ← Sheet.NearestStaff[newTime, height];
WHILE BlueBug[] DO
[newTime, height] ← Sheet.NearestTime[];
newStaff ← Sheet.NearestStaff[newTime, height];
IF newTime=oldTime AND newStaff=oldStaff THEN LOOP;
IF oldStaff#10 THEN Sheet.DrawClef[pitch, oldStaff, oldTime];
Sheet.DrawClef[pitch, newStaff, newTime];
oldTime ← newTime;
oldStaff ← newStaff;
ENDLOOP;
Sheet.DrawClef[pitch, newStaff, newTime];
IF YellowBug[] THEN RETURN;
old ← sheet[Sheet.FindLine[newTime]];
oldClef ← Clef[newStaff, newTime];
Sheet.SetClef[pitch, newStaff, newTime];
new ← sheet[Sheet.FindLine[newTime]];
IF flash THEN {Flash[]; RETURN};
IF old#new THEN SetDirty[new.time, new.time];
how much of the score does this affect?
SetDirty[newTime, newTime];
IF pitch=oldClef THEN {Score.Redraw[min, max]; RETURN};
FOR i: CARDINAL IN [0..scoreLength) DO
IF score[i].time#newTime THEN LOOP;
IF score[i].type#clef THEN LOOP;
IF score[i].value#newStaff THEN LOOP;
sync ← score[i];
EXIT; ENDLOOP;
IF sync#NIL THEN SetDirty[sync.time, GetCarry[sync]];
Score.Redraw[min, max];
};
InsertOctava: PROC[object: Interface.Object] = {
begin, end, oldEnd: Time← -1;
staff, oldStaff, alternate: INTEGER ← 1;
pitch, height, oldHeight, octHeight, limit: INTEGER ← 0;
SetBrush[black, invert];
[begin, oldHeight] ← Sheet.NearestTime[];
staff ← Sheet.NearestStaff[begin, oldHeight];
WHILE BlueBug[] DO
[end, height, staff] ← Slide[oldEnd, oldHeight, oldStaff];
IF end=oldEnd AND height=oldHeight THEN LOOP;
WE HAVE A MOVEMENT
IF oldEnd#-1 THEN Sheet.DrawOctava[pitch, oldStaff, octHeight, begin, oldEnd];
oldEnd ← end;
oldStaff ← staff;
oldHeight ← height;
octHeight ← height - Sheet.Height[end,, staff];
pitch ← (IF Sheet.NormalPitch[staff]=27 THEN 15 ELSE 60);
Sheet.DrawOctava[pitch, staff, octHeight, begin, end];
ENDLOOP;
Sheet.DrawOctava[pitch, staff, octHeight, begin, end];
IF YellowBug[] THEN RETURN;
IF begin>=end THEN RETURN;
Sheet.SetOctava[pitch, staff, octHeight, begin, end];
IF flash THEN {Flash[]; RETURN};
Score.Redraw[begin, end];
};
Slide: PROC[oldTime: Time, oldHeight: INTEGER, oldStaff: CARDINAL] RETURNS[time: Time, height: INTEGER, staff: CARDINAL] = {
limit: INTEGER;
staves: StavesPTR;
staff ← oldStaff;
[time, height] ← Sheet.NearestTime[];
IF oldTime#-1 AND ABS[time-oldTime]>200 AND time>oldTime THEN
[time, height] ← Sheet.AlternateTime[time, height,-1];
IF oldTime#-1 AND ABS[time-oldTime]>200 AND time<oldTime THEN
[time, height] ← Sheet.AlternateTime[time, height, 1];
IF time=oldTime AND height=oldHeight THEN RETURN;
should we move down to the next staff/line?
staves ← sheet[Sheet.FindSection[time]].staves;
staff ← Sheet.NextStaff[oldStaff, time];
limit ← staves.staff[staff].y+32;
IF staff=oldStaff THEN limit ← limit-32-staves.offset;
IF oldHeight <= limit THEN { -- we went below a lower limit
IF staff=oldStaff THEN { -- must be next line
[time, height] ← Sheet.AlternateTime[time, height, 1];
staff ← 0};
RETURN};
should we move up to the next staff/line?
staff ← Sheet.PriorStaff[oldStaff, time];
limit ← staves.staff[staff].y;
IF staff=oldStaff THEN limit ← limit+32+staves.offset;
IF height >= limit THEN { -- we went above an upper limit
IF staff=oldStaff THEN { -- must be last line
[time, height] ← Sheet.AlternateTime[time, height,-1];
staff ← staves.sl};
RETURN};
staff ← oldStaff;
};
******************************************************************
moving things
******************************************************************
MoveGraphical: PUBLIC PROC[object: Interface.Object] = {
s: SyncPTR;
p: UnspecifiedPTR ← NIL;
obj: ObjectType;
IF object#none THEN { Insert[object]; RETURN; };
SetBrush[black, invert];
WHILE BlueBug[] DO
[obj, p] ← Piece.NearestObject[];
IF p=NIL THEN LOOP;
s ← LOOPHOLE[p];
IF voice AND s.type IN [clef..octava2] THEN LOOP;
SELECT obj FROM
measure => {
IF s.type IN [octava1..octava2]
THEN MoveOctava[s]
ELSE MoveMeasure[s]};
leftBeam => MoveBeam[p];
rightBeam=> ChangeTilt[p];
tie=> MoveTie[p];
ENDCASE;
ENDLOOP;
IF p#NIL THEN count ← TRUE;
};
MoveMeasure: PROC[s: SyncPTR] = {
old: Time=s.time;
SetBrush[black, invert];
SetDirty[s.time, s.time];
WHILE BlueBug[] DO
Sync.Draw[s];
s.time ← Sheet.NearestTime[].time;
Sync.Draw[s];
ENDLOOP;
Sync.Draw[s];
SetDirty[s.time, s.time];
SELECT s.type FROM
clef => MoveClef[s, old, s.time];
staves => MoveStaves[s, old, s.time];
keySignature => MoveKey[s, old, s.time];
ENDCASE => {Piece.RemoveSync[score, s]; Piece.AddSync[score, s]};
Score.Redraw[min, max];
};
MoveClef: PROC[s: SyncPTR, old, new: Time] = {
oldCarry: Time;
time: Time=s.time;
newClef, clef: INTEGER;
newClef ← Clef[s.value, s.time];
s.time ← old;
oldCarry ← GetCarry[s];
s.time ← new;
Piece.RemoveSync[score, s];
Piece.AddSync[score, s];
ResetSheet[min, max];
clef ← Sync.GetStaff[s, s.value].pitch;
IF clef#newClef THEN SetDirty[new, GetCarry[s]];
IF clef#Clef[s.value, old] THEN SetDirty[old, oldCarry];
};
MoveStaves: PROC[s: SyncPTR, old, new: Time] = {
next: Time;
nextStyle, oldStyle, newStyle: INTEGER;
next ← sheet[Sheet.NextLine[Sheet.FindLine[MAX[old, new]]]].time;
nextStyle ← Sheet.GetStyle[next];
y ← sheet[Sheet.FindLine[old]].y;
oldStyle ← Sheet.GetStyle[old];
newStyle ← Sheet.GetStyle[new];
s.time ← new;
Piece.RemoveSync[score, s];
Piece.AddSync[score, s];
Sheet.Reset[];
IF nextStyle#Sheet.GetStyle[next] THEN SetDirty[begin, endTime];
IF oldStyle#Sheet.GetStyle[old] THEN SetDirty[begin, endTime];
IF newStyle#Sheet.GetStyle[new] THEN SetDirty[begin, endTime];
};
MoveKey: PROC[s: SyncPTR, old, new: Time] = {
oldCarry: Time;
newKey: INTEGER;
s.time ← old;
oldCarry ← GetCarry[s];
newKey ← Score.GetKey[new];
s.time ← new;
Piece.RemoveSync[score, s];
Piece.AddSync[score, s];
ResetSheet[min, max];
IF new>old AND newKey#s.value THEN SetDirty[new, GetCarry[s]];
IF new<old AND Score.GetKey[old]#s.value THEN SetDirty[old, oldCarry];
};
MoveOctava: PROC[s: SyncPTR] = {
staff: CARDINAL;
staves: StavesPTR;
pitch, height: INTEGER;
octava1, octava2: SyncPTR;
octava1 ← (IF s.type=octava1 THEN s ELSE Sync.Octava[s]);
octava2 ← (IF s.type=octava2 THEN s ELSE Sync.Octava[s]);
IF octava1=NIL THEN {Flash[]; RETURN};
staves ← LOOPHOLE[@octava1.event];
SetDirty[s.time, s.time];
SetBrush[black, invert];
staff ← octava1.value;
pitch ← staves.staff[octava1.value].pitch;
WHILE BlueBug[] DO
Sync.Draw[octava1];
[s.time, height, staff] ← Slide[s.time, height, staff];
staves.height ← height - Sheet.Height[octava1.time,, octava1.value];
staves.height ← Sheet.OctavaHeight[pitch, staves.height];
Sync.Draw[octava1];
ENDLOOP;
SetDirty[s.time, s.time];
IF octava1=NIL OR octava2=NIL OR
octava1.time>=octava2.time THEN {
Piece.DeleteSync[octava1]; -- deletes octava2 as well
Score.Redraw[min, max];
RETURN};
Piece.RemoveSync[score, s];
Piece.AddSync[score, s]; -- Remove/Add keeps the score sorted
Sheet.Reset[];
Score.Redraw[min, max];
};
MoveBeam: PROC[b: BeamPTR] = {
height, y: INTEGER ← 0;
SetBrush[black, invert];
y ← MouseY^;
WHILE BlueBug[] DO
height ← b.height+(y-MouseY^);
IF height=b.height THEN LOOP;
[] ← Beam.Draw[b];
b.height ← height; y ← MouseY^;
Beam.SetStems[b];
[] ← Beam.Draw[b];
ENDLOOP;
Score.Redraw[b.sync1.time, b.sync2.time];
};
ChangeTilt: PROC[b: BeamPTR] = {
height, oldHeight, y: INTEGER ← 0;
l: REAL ← 1;
SetBrush[black, invert];
oldHeight ← b.height + Real.FixI[b.tilt*(b.sync2.time-b.sync1.time)];
y ← MouseY^;
WHILE BlueBug[] DO
height ← oldHeight+(y-MouseY^);
IF height=oldHeight THEN LOOP;
[] ← Beam.Draw[b];
b.tilt ← l*(height-b.height)/(b.sync2.time-b.sync1.time);
oldHeight ← height; y ← MouseY^;
Beam.SetStems[b];
[] ← Beam.Draw[b];
ENDLOOP;
Score.Redraw[b.sync1.time, b.sync2.time];
};
MoveTie: PROC[n: NotePTR] = {
y: INTEGER;
time: Time;
time ← (n.sync.time+n.tie.sync.time)/2;
y ← MouseY^;
SetBrush[black, invert];
WHILE BlueBug[] DO
IF y=MouseY^ THEN LOOP;
Note.DrawTie[n];
n.tieHeight ← n.tieHeight+(y-MouseY^);
y ← MouseY^;
Note.DrawTie[n];
ENDLOOP;
Score.Redraw[n.tie.sync.time, n.sync.time];
};
******************************************************************
utility procedures
******************************************************************
ResetSheet: PROC[t1, t2: Time] = {
line: CARDINAL;
old1, old2: Section;
new1, new2: Section;
get state before reset
line ← Sheet.FindLine[MIN[t1, t2]];
old1 ← sheet[line];
line ← Sheet.NextLine[line];
old2 ← sheet[line];
Sheet.Reset[];
get state after reset
line ← Sheet.FindLine[MIN[t1, t2]];
new1 ← sheet[line];
line ← Sheet.NextLine[line];
new2 ← sheet[line];
compare before and after
IF old1#new1 THEN SetDirty[new1.time, new1.time+15];
IF old2#new2 THEN SetDirty[new2.time, new2.time+15];
IF old1.key#new1.key THEN SetDirty[begin, endTime];
IF old2.key#new2.key THEN SetDirty[begin, endTime];
};
GetCarry: PROC[s: SyncPTR] RETURNS[Time] = {
index: CARDINAL;
IF s.type#clef AND s.type#keySignature THEN RETURN[s.time];
is this insertion/deletion a NOOP?
IF s.type=keySignature AND Score.GetKey[s.time-1]=s.value THEN RETURN[s.time];
index ← Sync.GetScoreIndex[s];
IF s.type=clef THEN
FOR i: CARDINAL IN (index..scoreLength] DO
IF i=scoreLength THEN RETURN[endTime];
IF score[i].type NOT IN SheetSwitch THEN LOOP;
IF score[i].type = staves AND ~Similar[s, score[i]] THEN RETURN[score[i].time];
IF score[i].type = staves THEN LOOP;
IF Sync.GetStaff[score[i], score[i].value].y#Sync.GetStaff[s, s.value].y THEN LOOP;
RETURN[score[i].time];
ENDLOOP;
IF s.type=keySignature THEN
FOR i: CARDINAL IN (index..scoreLength] DO
IF i=scoreLength THEN RETURN[endTime];
IF score[i].type # keySignature THEN LOOP;
RETURN[score[i].time];
ENDLOOP;
RETURN[s.time];
};
Similar: PROC[s1, s2: SyncPTR] RETURNS[BOOL] = {
n, o: BOOL;
oldS, newS: StavesPTR;
oldS ← LOOPHOLE[@s1.event];
newS ← LOOPHOLE[@s2.event];
IF oldS.sl#newS.sl THEN RETURN[FALSE];
FOR i: CARDINAL IN [1..newS.sl] DO
n ← newS.staff[i].y=newS.staff[i-1].y;
o ← oldS.staff[i].y=oldS.staff[i-1].y;
IF n#o THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
Clef: PROC[staff: CARDINAL, time: Time] RETURNS[INTEGER] = {
staves: StavesPTR = sheet[Sheet.FindSection[time]].staves;
RETURN[staves.staff[staff].pitch];
};
Flash: PUBLIC PROC = {
SetBrush[black, invert];
Graphics.DrawScreenArea[context];
Wait[1];
Graphics.DrawScreenArea[context];
flash ← FALSE;
};
Wait: PUBLIC ENTRY PROC[ticks: CARDINAL] = {
FOR i: CARDINAL IN [0..ticks) DO
WAIT timeout;
ENDLOOP;
};
ProcessDefs.SetTimeout[@timeout, 1];
END.