--Author: John Maxwell
--last modified: December 14, 1981 10:34 AM

DIRECTORY
Beam USING [Drawn],
Device USING [Handle],
Graphics USING [DisplayContext, DrawScreenArea, MoveTo, SetTexture],
MusicDefs,
Note USING [DrawTie],
Piece USING [AddSync,NearestSync,RemoveSync],
PressDefs USING [PressFileDescriptor,WritePage],
PressDeviceImpl USING [DataRef],
Score USING [Draw,GetKey,Justify,LogicalToPhysical,maxCacheLength,ScalePhysical,ShowPitch],
Selection USING [Draw],
Sheet USING [Draw, FindLine, FindSection, Height, HiLite, Map, MapHeight, MapNote, NearestTime, Reset, Scale, ScreenPoint, SetBegin, SetStyle],
StringDefs USING [AppendDecimal],
Sync USING [Adjust,Draw,GetScoreIndex],
Utility;

ScoreImpl:PROGRAM
IMPORTS Beam, Graphics, MusicDefs, Note, Piece, PressDefs, PressDeviceImpl, Score, Selection, Sheet, StringDefs, Sync, Utility
EXPORTS MusicDefs, Score
SHARES PressDeviceImpl =
BEGIN
OPEN Graphics, MusicDefs, Score, Utility;

score:PUBLIC PiecePTR←NIL;
scoreLength:PUBLIC CARDINAL ← 0;
Error:PUBLIC SIGNAL[s:STRING] = CODE;

show:PUBLIC DocumentProfile;
TF:PUBLIC CARDINAL ← 256;
hardcopy:PUBLIC BOOLEAN ← FALSE;

--****************************************************************************
--
cache
--****************************************************************************

cache:PUBLIC ARRAY [0..maxCacheLength) OF SyncPTR;
cacheLength:PUBLIC CARDINAL ← 0;

BuildCache:PUBLIC PROCEDURE =
BEGIN
i:CARDINAL;
cache ← ALL[NIL];
cacheLength ← 0;
FOR i IN [0..scoreLength) DO
IF score[i].type IN [measure..m5] THEN LOOP;
IF score[i].type=notes THEN LOOP;
IF cacheLength = maxCacheLength THEN ERROR;
cache[cacheLength] ← score[i];
cacheLength ← cacheLength+1;
ENDLOOP;
END;

--****************************************************************************
--
drawing the score
--****************************************************************************

Draw:PUBLIC PROCEDURE[erase:BOOLEAN] =
BEGIN
i,j:CARDINAL←0;
Selection.Draw[];
IF erase THEN {
SetBrush[white,replace];
Graphics.DrawScreenArea[context];
SetBrush[black,paint];
Sheet.Draw[]};
DrawInterval[begin,endTime];
Selection.Draw[];
END;

Redraw:PUBLIC PROCEDURE[t1,t2:Time] =
BEGIN
i:CARDINAL;
x,y:INTEGER;
select:BOOLEAN;
SetBrush[black,paint];
SELECT scale FROM
1 => IF t2-t1>700 THEN {Score.Draw[]; RETURN};
2 => IF t2-t1>2000 THEN {Score.Draw[]; RETURN};
4 => IF t2-t1>8000 THEN {Score.Draw[]; RETURN};
ENDCASE;
select ← lineSelect AND select2>t1-40 AND select1<t2+50;
IF select THEN Selection.Draw[];
Sheet.HiLite[white,t1-20,t2+30];
DrawInterval[t1-30,t2+40];
IF select THEN Selection.Draw[];
IF lineSelect THEN RETURN;
SetBrush[black,invert];
FOR i IN [0..selectionLength) DO
IF selection[i]=NIL THEN LOOP;
IF selection[i].sync.time>t2+40 OR selection[i].sync.time<t1-30 THEN LOOP;
IF voice AND selection[i].voice#selectedVoice THEN LOOP;
[x,y] ← Sheet.MapNote[selection[i]];
Graphics.MoveTo[context,[x,y]];
DrawChar[context,170C];
ENDLOOP;
END;

DrawInterval:PROCEDURE[t1,t2:Time] =
BEGIN
n:NotePTR;
staves:StavesPTR;
i,j:CARDINAL←0;
[] ← Beam.Drawn[NIL]; --
clears the cache
SetBrush[black,paint];
--
draw octava markings first
staves ← sheet[Sheet.FindSection[t1]].staves;
FOR i IN [0..staves.sl] DO
IF staves.staff[i].pitch#15 AND staves.staff[i].pitch#60 THEN LOOP;
FOR j DECREASING IN [0..scoreLength) DO
IF score[j].time>=t1 THEN LOOP;
IF score[j].type NOT IN [octava1..octava2] THEN LOOP;
IF score[j].value#LOOPHOLE[i] THEN LOOP;
IF score[j].type=octava2 THEN EXIT;
Sync.Draw[score[j]];
EXIT; ENDLOOP;
ENDLOOP;
--
pre-Adjust the first 10 syncs (beams may draw notes in syncs downstream)
j←0;
IF show.display=graphical THEN
FOR i IN [0..scoreLength) DO
IF score[i].time< t1 THEN LOOP;
IF score[i].time> t2 THEN EXIT;
IF j=10 THEN EXIT ELSE j←j+1;
Sync.Adjust[score[i]];
ENDLOOP;
--
draw the syncs
j←0;
FOR i IN [0..scoreLength) DO
IF score[i].time < t1 THEN LOOP;
IF score[i].time > t2 THEN {j←i; EXIT};
IF show.display=graphical THEN Sync.Adjust[score[MIN[scoreLength-1,i+10]]];
Sync.Draw[score[i]];
IF NOT print AND AnyBug[] THEN EXIT;
ENDLOOP;
--
draw ties that go off the end of the section
IF j#0 THEN FOR i:CARDINAL IN [j..scoreLength) DO
IF score[i].time>t2+200 THEN EXIT;
IF score[i].type#notes THEN LOOP;
FOR j:CARDINAL IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF n.tie=NIL THEN LOOP;
IF n.tie.sync.time>t2 THEN LOOP;
IF (voice AND n.voice#selectedVoice)
THEN Graphics.SetTexture[context,light]
ELSE Graphics.SetTexture[context,black];
Note.DrawTie[n];
ENDLOOP;
ENDLOOP;
[] ← Beam.Drawn[NIL];
END;

--****************************************************************************
--
printing the score
--****************************************************************************

Print:PUBLIC PROCEDURE[splines:BOOLEAN] =
BEGIN
printAll:BOOLEAN;
oldBegin:Time ← begin;
ph:POINTER TO PressDefs.PressFileDescriptor;
device:Device.Handle ← Utility.OpenPressDevice[splines];
ph←LOOPHOLE[device.data,PressDeviceImpl.DataRef].pressHandle;
printAll ← ~splines AND scale=2;
IF printAll THEN Sheet.SetBegin[0];
FOR i:CARDINAL IN [0..scoreLength) DO
Sync.Adjust[score[i]];
ENDLOOP;
WHILE TRUE DO
Sheet.Draw[];
DrawInterval[begin,endTime];
IF endTime>EndOfScore[]-40 THEN EXIT;
IF printAll THEN {Sheet.SetBegin[endTime]; PressDefs.WritePage[ph]} ELSE EXIT;
ENDLOOP;
Utility.ClosePressDevice[@device];
Sheet.SetBegin[oldBegin];
END;

--****************************************************************************
--
changing the view
--****************************************************************************

Look:PUBLIC PROCEDURE[look:LookCommand,switch:BOOLEAN,n:INTEGER] =
BEGIN
draw:BOOLEAN ← TRUE;
SELECT look FROM
accidental => show.accidental ← switch;
hardcopy => {Sheet.Scale[IF switch THEN 2 ELSE 1]; hardcopy←switch};
justified => {TF←n; Score.Justify[select1,select2]};
logical => Score.LogicalToPhysical[select1,select2];
noCarry => show.noCarry ← switch;
notehead => show.structure ← show.notehead ← switch;
overview => Overview[switch];
physical => Score.ScalePhysical[512/n];
sheet => Sheet.SetStyle[n,0,EndOfScore[]];
sync => show.sync ← switch;
voice => {voice←switch; selectedVoice←n};
ENDCASE;
END;

Overview:PROCEDURE[switch:BOOLEAN] =
BEGIN
time:Time;
x,y:INTEGER;
SELECT TRUE FROM
scale<4 AND ~switch => {flash←TRUE; RETURN};
scale>2 AND switch => {flash←TRUE; RETURN};
switch => {Sheet.Scale[4]; Sheet.SetBegin[0]};
YellowBug[] => {
[x,y] ← Sheet.ScreenPoint[];
time ← Sheet.NearestTime[x,y].time;
Sheet.Scale[IF hardcopy THEN 2 ELSE 1];
Sheet.SetBegin[time]};
ENDCASE => {Sheet.Scale[IF hardcopy THEN 2 ELSE 1]; Sheet.SetBegin[0]};
END;

--****************************************************************************
--
procedures that handle keys
--****************************************************************************

phi:ARRAY [0..12) OF INTEGER = [6,1,-4,3,-2,5,0,7,2,-3,4,-1];
flatHeight:ARRAY[0..12) OF INTEGER = [0,4,8,8,12,12,16,16,20,24,24,28];
sharpHeight:ARRAY[0..12) OF INTEGER = [0,4,4,8, 8,12,12,16,20,20,24,24];

SetKey:PUBLIC PROCEDURE[time1,time2:Time,k:INTEGER] =
BEGIN
oldKey:INTEGER;
i,j:CARDINAL←0;
sync:SyncPTR←NIL;
IF time1>time2 THEN RETURN;
IF k NOT IN [-7..7] THEN RETURN;
min ← 0;
max ← EndOfScore[]+2000;
show.accidental ← TRUE;
oldKey ← Score.GetKey[time2];
FOR i DECREASING IN [0..cacheLength) DO
IF cache[i].time>time2 THEN LOOP;
IF cache[i].time< time1 THEN EXIT;
IF cache[i].type#keySignature THEN LOOP;
Piece.RemoveSync[score,cache[i]];
Utility.FreeSync[@cache[i]];
ENDLOOP;
--
put in the new key signature
IF k # Score.GetKey[time1] THEN {
sync ← Utility.NewSync[];
sync.time ← time1;
sync.type ← keySignature;
sync.value ← k;
Piece.AddSync[score,sync]};
--
put back the old signature
IF oldKey # k AND time2<EndOfScore[] THEN {
sync ← Utility.NewSync[];
sync.time ← time2;
sync.type ← keySignature;
sync.value ← oldKey;
Piece.AddSync[score,sync]};
Sheet.Reset[]; --
rebuilds the cache
END;

GetKey:PUBLIC PROCEDURE[t:Time] RETURNS[key:INTEGER←0] =
BEGIN
i:CARDINAL;
sync:SyncPTR←NIL;
FOR i IN [0..cacheLength) DO
IF cache[i]=NIL THEN EXIT;
IF cache[i].type#keySignature THEN LOOP;
IF cache[i].time>t THEN EXIT;
sync ← cache[i];
ENDLOOP;
IF sync#NIL THEN key ← sync.value;
END;

GetAccidental:PUBLIC PROCEDURE[n:NotePTR] RETURNS[Accidental]=
BEGIN
a:Accidental;
prior:NotePTR;
index:CARDINAL;
pitch,key:INTEGER;
normal:Accidental;
noteHeight:INTEGER;
IF NOT show.accidental THEN RETURN[inKey];
IF n.tie#NIL AND n.tie.pitch=n.pitch THEN RETURN[inKey];
IF n.show AND n.spelled=inKey THEN Error["show inKey?"];
IF n.show THEN RETURN[n.spelled];
IF show.noCarry THEN {
IF n.spelled#inKey THEN RETURN[n.spelled];
a←DefaultAcc[0,n.pitch];
IF a=natural THEN a←inKey;
RETURN[a]};
key ← Score.GetKey[n.sync.time];
pitch ← Score.ShowPitch[n.pitch,n.spelled,key];
noteHeight ← Sheet.Height[n.sync.time,pitch,n.staff];
index ← Sync.GetScoreIndex[n.sync];
IF index=scoreLength THEN index ← SyncIndex[n.sync.time];
prior ← PriorNote[pitch,noteHeight,index];
normal ← NormalAcc[key,pitch];
-- no prior note
IF prior=NIL THEN SELECT TRUE FROM
normal=inKey => RETURN[n.spelled];
n.spelled=inKey => RETURN[normal];
ENDCASE => RETURN[n.spelled];
--
n.spelled=normal => RETURN[inKey];
--
n.spelled#normal => RETURN[n.spelled];
--
ENDCASE;
-- prior note
IF prior.pitch=n.pitch THEN RETURN[inKey];
-- an accidental MUST be asserted
IF n.spelled#inKey THEN RETURN[n.spelled];
IF normal#inKey THEN RETURN[normal];
IF Mod[n.pitch,12]=7 AND key<-5 THEN RETURN[flat];
IF Mod[n.pitch,12]=0 AND key<-6 THEN RETURN[flat];
IF Mod[n.pitch,12]=1 AND key>5 THEN RETURN[sharp];
IF Mod[n.pitch,12]=8 AND key>6 THEN RETURN[sharp];
normal ← NormalAcc[0,n.pitch];
IF normal=inKey THEN normal←natural;
IF key<0 AND normal=sharp THEN normal←flat;
RETURN[normal];
END;

SyncIndex:PROCEDURE[time:Time] RETURNS[s:CARDINAL] = INLINE
BEGIN
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;
END;

PriorNote:PUBLIC PROCEDURE[pitch,height:INTEGER,index:CARDINAL] RETURNS[NotePTR] =
BEGIN
n:NotePTR;
newPitch,key:INTEGER;
FOR i:CARDINAL DECREASING IN [0..index) DO
IF Measure[score[i].type] THEN RETURN[NIL];
IF score[i].type#notes THEN LOOP;
key ← Score.GetKey[score[i].time];
FOR j:CARDINAL IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF n.rest THEN LOOP;
newPitch ← Score.ShowPitch[n.pitch,n.spelled,key];
IF NOT newPitch IN [pitch-2..pitch+2] THEN LOOP;
IF Sheet.Height[score[i].time,newPitch,n.staff]#height THEN LOOP;
RETURN[n];
ENDLOOP;
ENDLOOP;
RETURN[NIL];
END;

assert:Accidental = LOOPHOLE[LOOPHOLE[LAST[Accidental],CARDINAL]+1];

NormalAcc:PUBLIC PROCEDURE[key,pitch:INTEGER] RETURNS[Accidental] =
BEGIN
strangeness:INTEGER ← phi[Mod[pitch,12]];
--
i bet you don’t really believe this works.
SELECT TRUE FROM
Mod[(key-strangeness),12] > 4 => RETURN[inKey];
strangeness> 0 => RETURN[natural];
key<0 => RETURN[flat];
ENDCASE => RETURN[sharp];
END;

DefaultAcc:PROCEDURE[key,pitch:INTEGER] RETURNS[Accidental] =
BEGIN
SELECT NormalAcc[0,pitch] FROM
inKey => RETURN[natural];
sharp => RETURN[IF key<0 THEN flat ELSE sharp];
ENDCASE => ERROR;
END;

AddToPitch
:PUBLIC PROCEDURE[key,pitch,delta:INTEGER] RETURNS[INTEGER] =
BEGIN
i:INTEGER;
IF delta>0 THEN FOR i IN (pitch..90] DO
IF NormalAcc[key,i]=inKey THEN delta←delta-1;
IF delta = 0 THEN RETURN[i];
ENDLOOP
ELSE FOR i DECREASING IN [0..pitch) DO
IF NormalAcc[key,i]=inKey THEN delta←delta+1;
IF delta = 0 THEN RETURN[i];
ENDLOOP;
ERROR;
END;

KeyHeight:PUBLIC PROCEDURE[key,pitch:INTEGER] RETURNS[INTEGER]=
BEGIN
i:CARDINAL ← Mod[pitch,12];
SELECT TRUE FROM
i=7 AND key<-5 => RETURN[flatHeight[8]];
i=0 AND key<-6 => RETURN[flatHeight[1]];
key< 0 => RETURN[flatHeight[i]];
i=1 AND key>5 => RETURN[sharpHeight[0]];
i=8 AND key>6 => RETURN[sharpHeight[7]];
ENDCASE => RETURN[sharpHeight[i]];
END;


DrawKey:PUBLIC PROCEDURE[key:INTEGER,time:Time] =
BEGIN
n:INTEGER = 8;
oldStaff:Staff←[0,0];
x,y,offset:INTEGER;
l:CARDINAL←Sheet.FindLine[time];
staves:StavesPTR ← sheet[l].staves;
IF NOT show.accidental THEN RETURN;
IF time IN [sheet[l].time-10..sheet[l].time) THEN RETURN;
IF time IN (sheet[l].time..sheet[l].time+15] THEN time←sheet[l].time;
x ← Sheet.MapHeight[time,0].x;
FOR i:CARDINAL IN [0..staves.sl] DO
IF staves.staff[i]=oldStaff THEN LOOP;
oldStaff ← staves.staff[i];
offset ← Sheet.Height[time,oldStaff.pitch,i];
IF Mod[staves.staff[i].pitch,12] = 3 THEN offset ← offset - 8;
[x,y] ← Sheet.MapHeight[time,offset];
IF sheet[l].time=time THEN x←3;
IF key>0 THEN SlapDown[x ,32+y,sharp];
IF key>1 THEN SlapDown[x+ n,20+y,sharp];
IF key>2 THEN SlapDown[x+2*n,36+y,sharp];
IF key>3 THEN SlapDown[x+3*n,24+y,sharp];
IF key>4 THEN SlapDown[x+4*n,12+y,sharp];
IF key>5 THEN SlapDown[x+5*n,28+y,sharp];
IF key>6 THEN SlapDown[x+6*n,16+y,sharp];
IF key<0 THEN SlapDown[x ,16+y, flat];
IF key<-1 THEN SlapDown[x+ n,28+y, flat];
IF key<-2 THEN SlapDown[x+2*n,12+y, flat];
IF key<-3 THEN SlapDown[x+3*n,24+y, flat];
IF key<-4 THEN SlapDown[x+4*n, 8+y, flat];
IF key<-5 THEN SlapDown[x+5*n,20+y, flat];
IF key<-6 THEN SlapDown[x+6*n, 4+y, flat];
ENDLOOP;
END;

SlapDown:PROCEDURE[x,y:INTEGER,a:Accidental] =
BEGIN
Graphics.MoveTo[context,[x,y]];
SELECT a FROM
flat => DrawChar[context,111C];
natural => DrawChar[context,112C];
sharp => DrawChar[context,113C];
ENDCASE;
END;

--****************************************************************************
--
score attributes: metrenome
--****************************************************************************

SetMetrenome:PUBLIC PROCEDURE[time1,time2:Time,m:INTEGER] =
BEGIN
i,j:CARDINAL←0;
sync:SyncPTR←NIL;
IF time1>time2 THEN RETURN;
min ← time1;
max ← time1+50;
--remove old metrenomes
FOR i DECREASING IN [0..cacheLength) DO
IF cache[i].time>time2 THEN LOOP;
IF cache[i].time< time1 THEN EXIT;
IF cache[i].type#metrenome THEN LOOP;
max ← MAX[max,cache[i].time+50];
Piece.RemoveSync[score,cache[i]];
Utility.FreeSync[@cache[i]];
ENDLOOP;
--insert new one
sync ← Utility.NewSync[];
sync.time ← time1;
sync.type ← metrenome;
sync.value ← m;
Piece.AddSync[score,sync];
BuildCache[];
END;

GetMetrenome:PUBLIC PROCEDURE[t:Time] RETURNS[INTEGER] =
BEGIN
i:CARDINAL;
sync:SyncPTR←NIL;
metrenome:INTEGER←0;
FOR i IN [0..cacheLength) DO
IF cache[i]=NIL THEN EXIT;
IF cache[i].type#metrenome THEN LOOP;
IF cache[i].time>t THEN EXIT;
sync ← cache[i];
ENDLOOP;
IF sync=NIL THEN metrenome←128 ELSE metrenome ← sync.value;
RETURN[metrenome];
END;

DrawMetrenome:PUBLIC PROCEDURE[metrenome:INTEGER,time:Time] =
BEGIN
x,y:INTEGER;
string:STRING ← [10];
IF print THEN RETURN;
[x,y] ← Sheet.Map[time,,0];
Graphics.MoveTo[context,[x,y+40]];
DrawChar[context,’t];
StringDefs.AppendDecimal[string,metrenome];
Utility.SetFont[context,text,12];
DrawChar[context,’=];
DrawString[context,string];
Utility.SetFont[context,music,8];
END;

--****************************************************************************
--
time signature
--****************************************************************************

SetTimeSignature:PUBLIC PROCEDURE[time1,time2:Time,ts:TimeSignature] =
BEGIN
i,j:CARDINAL←0;
sync:SyncPTR←NIL;
oldTS:TimeSignature←[0,0];
IF time1>time2 THEN RETURN;
min ← time1;
max ← time1+50;
--remove old time signature
FOR i IN [0..cacheLength) DO
IF cache[i].time>time2 THEN EXIT;
IF cache[i].type#timeSignature THEN LOOP;
IF cache[i].time< time1 THEN {oldTS ← cache[i].ts; LOOP};
sync ← cache[i];
max ← MAX[max, sync.time+50];
Piece.RemoveSync[score,sync];
Utility.FreeSync[@sync];
ENDLOOP;
--insert new time signature
IF ts.top#0 AND ts.bottom#0 THEN {
sync ← Utility.NewSync[];
sync.time ← time1+10;
sync.type ← timeSignature;
sync.ts ← ts;
Piece.AddSync[score,sync]};
IF oldTS.top#0 AND time2<EndOfScore[] THEN {
sync ← Utility.NewSync[];
sync.time ← time2+10;
sync.type ← timeSignature;
sync.ts ← oldTS;
Piece.AddSync[score,sync];
SetDirty[sync.time,sync.time]};
BuildCache[];
END;

GetTimeSignature:PUBLIC PROCEDURE[t:Time] RETURNS[TimeSignature] =
BEGIN
i:CARDINAL;
sync:SyncPTR←NIL;
ts:TimeSignature←[4,4];
FOR i IN [0..cacheLength) DO
IF cache[i]=NIL THEN EXIT;
IF cache[i].type#timeSignature THEN LOOP;
IF cache[i].time>t THEN EXIT;
sync ← cache[i];
ENDLOOP;
IF sync#NIL THEN ts ← sync.ts;
RETURN[ts];
END;

DrawTimeSignature:PUBLIC PROCEDURE[ts:TimeSignature,time:Time] =
BEGIN
i:CARDINAL;
x,y:INTEGER;
oldStaff:Staff;
staves:StavesPTR=sheet[Sheet.FindSection[time]].staves;
FOR i IN [0..staves.sl] DO
IF staves.staff[i] = oldStaff THEN LOOP;
oldStaff ← staves.staff[i];
[x,y] ← Sheet.Map[time,,i];
x←x-5;
Graphics.MoveTo[context,[x,y+8]];
SELECT ts.bottom FROM
2 => DrawChar[context,062C];
4 => DrawChar[context,064C];
8 => DrawChar[context,070C];
ENDCASE;
Graphics.MoveTo[context,[x,y+24]];
SELECT ts.top FROM
1 => DrawChar[context,061C];
2 => DrawChar[context,062C];
3 => DrawChar[context,063C];
4 => DrawChar[context,064C];
5 => DrawChar[context,065C];
6 => DrawChar[context,066C];
7 => DrawChar[context,067C];
8 => DrawChar[context,070C];
9 => DrawChar[context,071C];
12 => {
Graphics.MoveTo[context,[x-5,y+24]];
DrawString[context,"12"]};
ENDCASE;
ENDLOOP;
END;

END..


GetAccidental:PUBLIC PROCEDURE[note:NotePTR] RETURNS[Accidental]=
BEGIN
a:Accidental;
pitch,newPitch,key:INTEGER;
noteHeight:INTEGER;
i,j:CARDINAL;
t:Time;
n:NotePTR;
IF NOT show.accidental THEN RETURN[inKey];
IF note.tie#NIL THEN RETURN[inKey];
t ← note.sync.time;
--leftEdge ← sheet[Sheet.FindLine[t]].time;
key ← Score.GetKey[t];
IF note.spelled=inKey THEN a ← NormalAccidental[key,note.pitch]
ELSE a ← note.spelled;
IF show.noCarry THEN RETURN[a];
pitch ← Score.ShowPitch[note.pitch,note.spelled,key];
noteHeight ← Sheet.Height[t,pitch,note.staff];
FOR i DECREASING IN [0..Sync.GetScoreIndex[note.sync]) DO
IF score[i].time>=note.sync.time THEN LOOP;
IF score[i].type IN Measure THEN RETURN[a];
IF score[i].type#notes THEN LOOP;
--
IF score[i].time<leftEdge THEN RETURN[a];
FOR j IN [0..syncLength) DO
IF (n←score[i].event[j])=NIL THEN EXIT;
IF n.rest THEN LOOP;
newPitch ← Score.ShowPitch[n.pitch,n.spelled,key];
IF NOT newPitch IN [pitch-2..pitch+2] THEN LOOP;
IF Sheet.Height[t,newPitch,n.staff]#noteHeight THEN LOOP;
--there’s a note prior to this on the same section
IF n.pitch=note.pitch THEN RETURN[inKey]; --don’t assert accidental
IF note.spelled#inKey THEN RETURN[note.spelled];
a ← NormalAccidental[0,pitch];
IF a=inKey THEN RETURN[natural]
ELSE RETURN[IF key>=0 THEN sharp ELSE flat];
ENDLOOP;
ENDLOOP;
RETURN[a];
END;

AddToPitch:PUBLIC PROCEDURE[key,pitch,delta:INTEGER] RETURNS[INTEGER] =
BEGIN
i:INTEGER;
IF delta>0 THEN FOR i IN (pitch..90] DO
IF NormalAccidental[key,i]=inKey THEN delta←delta-1;
IF delta = 0 THEN RETURN[i];
ENDLOOP
ELSE FOR i DECREASING IN [0..pitch) DO
IF NormalAccidental[key,i]=inKey THEN delta←delta+1;
IF delta = 0 THEN RETURN[i];
ENDLOOP;
ERROR;
END;

NormalAccidental:PUBLIC PROCEDURE[key,pitch:INTEGER] RETURNS[Accidental] =
BEGIN
strangeness:INTEGER ← phi[Mod[pitch,12]];
--
i bet you don’t really believe this works.
SELECT TRUE FROM
Mod[(key-strangeness),12] > 4 => RETURN[inKey];
strangeness> 0 => RETURN[natural];
key<0 => RETURN[flat];
ENDCASE => RETURN[sharp];
END;