InterfaceImplC.mesa
Copyright (C) 1981, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
last modified: December 18, 1981 8: 55 AM
Edited by Doug Wyatt, June 14, 1984 1:07:39 pm PDT
DIRECTORY
CursorDefs,
Beam USING [SetStems],
Chord USING [Draw],
Graphics USING [SetPaint, SetTexture],
InlineDefs USING [LowHalf],
Interface USING [count, Object, Wait],
MusicDefs,
Note USING [Draw, Duration, FindChord, GetBackTie, SetAccidental],
Piece USING [AddSync, CleanUpSyncs, NearestSync],
Score USING [GetKey, NormalAcc, PriorNote, Redraw, ShowPitch],
Selection, -- USING everything
Sheet USING [FindSection, Height, NearestStaff, NearestTime],
Sync USING [AddNote, Adjust],
Utility; -- USING everything
InterfaceImplC:
CEDAR
PROGRAM
IMPORTS Beam, Chord, Graphics, InlineDefs, Interface, MusicDefs, Note, Piece, Score, Selection, Sheet, Sync, Utility
EXPORTS Interface =
BEGIN
OPEN Graphics, MusicDefs;
Error: SIGNAL = CODE;
******************************************************************
deleting objects
******************************************************************
defaultSync: SyncRec ← [];
InsertNote:
PUBLIC
PROC[object: Interface.Object] = {
time: Time;
height, key: INTEGER;
n: NotePTR ← Utility.NewNote[];
n.value ← IF object=rest THEN quarter ELSE unknown;
n.rest ← (object=rest);
n.voice ← (IF voice THEN selectedVoice ELSE 0);
FOR i:
CARDINAL
IN [0..selectionLength)
DO
IF selection[i]=NIL THEN LOOP;
n.value ← selection[i].value;
n.dotted ← selection[i].dotted;
n.grace ← selection[i].grace;
EXIT; ENDLOOP;
n.sync ← @defaultSync;
n.duration ← InlineDefs.LowHalf[7*(Note.Duration[n, 128]/8)];
[time, height] ← Sheet.NearestTime[];
n.show ← FALSE;
n.sync.time ← time;
key ← Score.GetKey[time];
n.staff ← Sheet.NearestStaff[time, height];
IF n.rest
THEN [n.pitch, n.spelled] ←RestPosition[height, key, n.staff, time]
ELSE [n.pitch, n.spelled] ← DefaultPitch[height, key, n.staff, time];
n.stemUp ← StemDirection[n.pitch, n.staff, time];
n.voice ← IF voice THEN selectedVoice ELSE 0;
IF voice THEN maxVoice ← MAX[maxVoice, selectedVoice];
SetBrush[black, invert];
Note.Draw[n];
MoveNote[n];
Selection.Clear[];
Selection.AddNote[n];
command ← TRUE;
};
DefaultPitch:
PROC[height, key:
INTEGER, staff:
CARDINAL, time: Time]
RETURNS[pitch:
INTEGER, spelled: Accidental] = {
prior: NotePTR;
normal: Accidental;
index: CARDINAL = SyncIndex[time];
pitch ← WhiteKey[time, height, staff];
pitch ← Score.ShowPitch[pitch, natural, key];
height ← Sheet.Height[time, pitch, staff];
prior ← Score.PriorNote[pitch, height, index];
IF show.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]};
SyncIndex:
PROC[time: Time]
RETURNS[s:
CARDINAL] =
INLINE {
s ← Piece.NearestSync[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[n: NotePTR] = {
OPEN CursorDefs;
c: ChordPTR;
i: CARDINAL;
oldTime, time: Time;
scale, offset: INTEGER;
spell, oldSpell: INTEGER;
pitch, oldPitch: INTEGER;
note: NotePTR; s: SyncPTR;
tempCursor: Cursor ← cursor^;
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[oldTime], n.pitch]
ELSE n.spelled];
oldPitch ← 1000; -- forces a move
offset ← oldSpell*4*scale+2*scale;
pitch ← Score.ShowPitch[n.pitch, n.spelled, key];
oldHeight ← Sheet.Height[n.sync.time, pitch, n.staff];
y ← MouseY^;
oldSpell ← 6};
NonAccMode:
PROC = {
scale ← 1;
offset ← scale*10;
oldPitch ← 1000; -- forces a move
pitch ← Score.ShowPitch[n.pitch, n.spelled, key];
oldHeight ← Sheet.Height[n.sync.time, pitch, n.staff];
y ← MouseY^};
IF n=NIL THEN RETURN;
cursor^ ← ALL[0];
c ← Note.FindChord[n];
SetBrush[black, invert];
oldTime ← n.sync.time;
min ← max ← 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[n];
forwardEqual ← (forwardtie.pitch=n.pitch)};
defaultSync.event ← ALL[NIL];
FOR i
IN [0..chordLength)
DO
IF c=NIL AND i>0 THEN EXIT;
IF c=NIL THEN note ← n ELSE note ← c.note[i];
IF note=NIL THEN EXIT;
Selection.RemoveNote[note];
IF note.beam#
NIL
THEN {
min ← MIN[min, note.beam.sync1.time];
max ← MAX[max, note.beam.sync2.time]};
IF note.sync#
NIL
THEN {
min ← MIN[min, note.sync.time];
max ← MAX[max, note.sync.time]};
Sync.AddNote[@defaultSync, note];
ENDLOOP;
Piece.CleanUpSyncs[score];
defaultSync.time ← oldTime;
x ← MouseX^; y ← MouseY^;
key ← Score.GetKey[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+(MouseX^-x);
[height, spell] ← Scale[oldHeight,(y+offset-MouseY^), scale];
key ← Score.GetKey[time];
pitch ← WhiteKey[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[c] ELSE Note.Draw[n];
n.sync.time ← time;
IF pitch#oldPitch
AND ~n.rest
AND yellow=up
THEN {
n.show ← FALSE;
[n.pitch, n.spelled] ← DefaultPitch[height, key, n.staff, time]};
IF spell#oldSpell
AND ~n.rest
THEN {
flash ← FALSE;
n.show ← TRUE;
SetAccidental[n, pitch, spell]};
oldPitch ← pitch;
oldSpell ← spell;
Sync.Adjust[n.sync];
IF n.rest THEN [n.pitch, n.spelled]← RestPosition[height, key, n.staff, time];
IF n.beam=NIL THEN n.stemUp ← StemDirection[pitch, n.staff, n.sync.time];
IF c#NIL THEN Chord.Draw[c] ELSE Note.Draw[n];
ENDLOOP;
ALL MOVEMENTS FINISHED
IF show.display=physical
AND c#
NIL
THEN
FOR i
IN [0..chordLength)
DO
IF c.note[i]=NIL THEN EXIT;
c.note[i].toc ← c.note[i].sync.time*TF;
ENDLOOP
ELSE n.toc ← n.sync.time*TF;
IF c#NIL THEN Chord.Draw[c] ELSE Note.Draw[n];
s ← score[Piece.NearestSync[p: score, t: time, notesOnly: TRUE]];
IF s=
NIL
OR
ABS[s.time-time]>2
THEN
{
s ← Utility.NewSync[];
s.time ← time;
Piece.AddSync[score, s];
};
IF c#
NIL
THEN
FOR i
IN [0..chordLength)
DO
IF c.note[i]=NIL THEN LOOP;
Sync.AddNote[s, c.note[i]];
ENDLOOP;
IF c=NIL THEN Sync.AddNote[s, n];
IF n.beam#NIL THEN Beam.SetStems[n.beam];
Selection.AddNote[NIL];
cursor^ ← tempCursor;
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 ← backtie;
backtie ← backtie.tie;
IF backtie#NIL THEN backEqual← (backtie.pitch=note.pitch);
note.pitch ← pitch;
note.spelled ← n.spelled;
min ← MIN[min, note.sync.time];
max ← MAX[max, note.sync.time];
ENDLOOP;
WHILE forwardtie#
NIL
AND forwardEqual
DO
note ← forwardtie;
forwardtie ← Note.GetBackTie[forwardtie];
IF forwardtie#NIL THEN forwardEqual ← (forwardtie.pitch=note.pitch);
note.pitch ← pitch;
note.spelled ← n.spelled;
min ← MIN[min, note.sync.time];
max ← MAX[max, note.sync.time];
ENDLOOP;
SetDirty[s.time, s.time];
Score.Redraw[min, max];
Piece.CleanUpSyncs[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[t: Time, height:
INTEGER, staff:
CARDINAL]
RETURNS[
INTEGER]= {
delta: INTEGER;
staffHeight: INTEGER = Sheet.Height[t,, staff];
pitch: INTEGER ← sheet[Sheet.FindSection[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[n: NotePTR, pitch, spell:
INTEGER] = {
key: INTEGER;
normal: Accidental;
IF n.rest THEN RETURN;
SELECT spell
FROM
0=>{n.pitch ← pitch-2; Note.SetAccidental[n, doubleFlat]};
1=>{n.pitch ← pitch-1; Note.SetAccidental[n, flat]};
2=>{n.pitch ← pitch; Note.SetAccidental[n, natural]};
3=>{n.pitch ← pitch+1; Note.SetAccidental[n, sharp]};
4=>{n.pitch ← pitch+2;Note.SetAccidental[n, doubleSharp]};
ENDCASE => ERROR;
IF flash THEN Error;
IF n.spelled NOT IN [sharp..flat] THEN RETURN;
key ← Score.GetKey[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[height, key:
INTEGER, staff:
CARDINAL, time: Time]
RETURNS[pitch:
INTEGER, spelled: Accidental] = {
w: INTEGER = 8;
staffHeight: INTEGER = Sheet.Height[time,, staff];
height ← 8*ModDiv[(height-staffHeight), w] +w-4+staffHeight;
pitch ← WhiteKey[time, height, staff];
pitch ← Score.ShowPitch[pitch, natural, key];
spelled ← natural;
};
StemDirection:
PROC[pitch, staff:
INTEGER, t: Time]
RETURNS[
BOOL] = {
RETURN[Sheet.Height[t, pitch, staff]<Sheet.Height[t,, staff]+16];
};
END.