EventImpl.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 12:02 pm
Last Edited by: Doug Wyatt, June 13, 1984 4:10:29 pm PDT
DIRECTORY
Beam USING [Draw, Drawn, SetSyncs],
Chord USING [Draw],
Event USING [Free, GetScoreIndex, GetStaff, KeySignature, Metronome, Sync, TimeSignature],
Graphics USING [SetStipple],
MusicDefs USING [BeamPTR, black, ChordPTR, EventPTR, EventRec, KeySignaturePTR, light, MeasureType, NotePTR, ScorePTR, Staff, StavesPTR, SyncPTR, Time],
Note USING [Draw, InVoice],
Sheet USING [MapNote, Reset],
Utility USING [DrawLine];
EventImpl: CEDAR PROGRAM
IMPORTS Beam, Chord, Event, Graphics, MusicDefs, Note, Sheet, Utility
EXPORTS Event
= BEGIN OPEN MusicDefs;
GetScoreIndex:
PUBLIC
PROC[score: ScorePTR, event: EventPTR]
RETURNS[index: CARDINAL ← 0] = {
binary: CARDINAL ← 8192;
IF event = NIL THEN RETURN[score.length];
FOR i:
CARDINAL
IN [0..13)
DO
binary ← binary/2;
IF index + binary >= score.length THEN LOOP;
IF score[index+binary].time <= event.time THEN index ← index + binary;
IF score[index].time # event.time THEN LOOP;
IF score[index] = event THEN RETURN[index];
must be a nearby sync
FOR j:
CARDINAL
IN (index..score.length)
WHILE score[j].time = event.time
DO
IF score[j] = event THEN RETURN[j]; ENDLOOP;
FOR j:
CARDINAL
DECREASING
IN [0..index)
WHILE score[j].time = event.time
DO
IF score[j] = event THEN RETURN[j]; ENDLOOP;
EXIT; ENDLOOP;
FAILURE: score isn't sorted. Try again.
FOR i: CARDINAL IN [0..score.length) DO IF score[i] = event THEN RETURN[i]; ENDLOOP;
RETURN[score.length]; -- not in score
};
GetStaff:
PUBLIC
PROC[staves: StavesPTR, staff:
CARDINAL]
RETURNS[Staff] ~ {
RETURN[staves.staff[staff]];
};
Invisible:
PUBLIC
PROC[score: ScorePTR, index:
CARDINAL, leftEdge: Time]
RETURNS[
BOOL] = {
event: EventPTR ~ score[index];
WITH event
SELECT
FROM
ev: StavesPTR => IF ev.staves # clef THEN RETURN[FALSE];
ev: KeySignaturePTR => NULL;
ENDCASE => RETURN[FALSE];
RETURN[event.time-leftEdge <= 15];
};
******************************************************************
procedures for handling staves
******************************************************************
SetStave:
PUBLIC
PROC[score: ScorePTR, oldS: StavesPTR, newS: StavesPTR] = {
n, o: BOOL;
pitch, height: INTEGER;
IF newS = NIL THEN RETURN;
height ← newS.height;
IF newS.staves # style
THEN {
pitch ← newS.staff[newS.value].pitch;
newS^ ← oldS^;
newS.height ← height;
FOR j:
CARDINAL
IN [0..newS.length)
DO
IF newS.staff[j].y # newS.staff[newS.value].y THEN LOOP;
newS.staff[j].pitch ← pitch;
ENDLOOP;
RETURN};
newS.staves = staves
newS^ ← score.style[newS.value]^;
newS.height ← height;
clef switches carry over if the staff form is the same
IF oldS = NIL THEN RETURN;
IF oldS.length # newS.length THEN RETURN;
FOR i:
CARDINAL
IN [1..newS.length)
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;
ENDLOOP;
FOR i:
CARDINAL
IN [0..newS.length)
DO
newS.staff[i].pitch ← oldS.staff[i].pitch;
ENDLOOP;
};
GetOctava:
PUBLIC
PROC[score: ScorePTR, octava: StavesPTR]
RETURNS[StavesPTR] = {
OPEN Event; -- USING [GetScoreIndex, GetStaff];
IF octava = NIL THEN RETURN[NIL];
IF octava.staves = octava1
THEN {
FOR i:
CARDINAL
IN (GetScoreIndex[score, octava]..score.length)
DO
staves: StavesPTR;
IF score[i].type # staves THEN LOOP;
staves ← NARROW[score[i]];
IF staves.staves # octava2 THEN LOOP;
IF octava.value = staves.value THEN RETURN[staves];
IF GetStaff[octava, octava.value].y = GetStaff[staves, staves.value].y THEN RETURN[staves];
ENDLOOP};
IF octava.staves = octava2
THEN {
FOR i:
CARDINAL
DECREASING
IN [0..GetScoreIndex[score, octava])
DO
staves: StavesPTR;
IF score[i].type # staves THEN LOOP;
staves ← NARROW[score[i]];
IF staves.staves # octava1 THEN LOOP;
IF octava.value = staves.value THEN RETURN[staves];
IF GetStaff[octava, octava.value].y = GetStaff[staves, staves.value].y THEN RETURN[staves];
ENDLOOP};
RETURN[NIL];
};
Draw:
PUBLIC
PROC[score: ScorePTR, event: EventPTR] = {
IF event = NIL THEN RETURN;
Graphics.SetStipple[score.sheet.context, black];
SELECT event.type FROM
sync => DrawSync[score, Event.Sync[event]];
staves => {
staves: StavesPTR ← Event.Staves[event];
SELECT staves.staves FROM
style => DrawMeasure[score, measure, event.time, 10];
clef => Sheet.DrawClef[score.sheet, staves.staff[staves.value].pitch,
staves.value, staves.time];
octava1 => {
octava2: StavesPTR = Event.GetOctava[score, staves];
IF octava2 = NIL THEN ERROR;
Sheet.DrawOctava[score.sheet, staves.staff[staves.value].pitch,
staves.value, staves.height, staves.time, octava2.time]};
octava2 => IF Event.GetOctava[score, Event.GetOctava[score, staves]] # staves
THEN DrawMeasure[score, doubleMeasure, staves.time, 20];
ELSE already drawn
ENDCASE};
keySignature => Sheet.DrawKey[score.sheet, Event.KeySignature[event].key, Score.GetKey[score, event.time - 1], event.time];
timeSignature => Score.DrawTimeSignature[score, Event.TimeSignature[event].ts, event.time];
metronome => Score.DrawMetronome[score, Event.Metronome[event].metronome, event.time];
measure => DrawMeasure[score, Event.Measure[event].measure, event.time, 0];
ENDCASE;
};
DrawMeasure:
PROC[score: ScorePTR, type: MeasureType, time: Time, dy:
INTEGER] = {
staves: StavesPTR;
x1, y1, top, delta: INTEGER;
IF time = 0 THEN RETURN;
IF score.sheet.printing THEN dy ← 0;
Graphics.SetStipple[score.sheet.context, black];
staves ← Sheet.FindStaves[score.sheet, time];
[x1, y1] ← Sheet.Map[score.sheet, time, , staves.length-1];
y1 ← y1 - dy;
top ← -staves.staff[staves.length-1].y+2*dy;
SELECT type FROM
measure => DrawLine[score.sheet.context, x1, y1, x1, y1+top];
doubleMeasure => {DrawLine[score.sheet.context, x1, y1, x1, y1+top];
IF score.sheet.printing OR score.sheet.scale = 1 THEN delta ← 2 ELSE delta ← 3;
DrawLine[score.sheet.context, x1-delta, y1, x1-delta, y1+top]};
repeat1 => { DrawBox[score.sheet.context, [x1, y1, x1+3, y1+top]];
DrawLine[score.sheet.context, x1+5, y1, x1+5, y1+top]; };
repeat2, endMeasure => { DrawBox[score.sheet.context, [x1+1, y1, x1-2, y1+top]];
DrawLine[score.sheet.context, x1-5, y1, x1-5, y1+top]; };
ENDCASE;
IF type # repeat1 AND type # repeat2 THEN RETURN;
IF type = repeat1 THEN delta ← 6 ELSE delta ← -13;
FOR i: CARDINAL IN [0..staves.length) DO
[x1, y1] ← Sheet.Map[score.sheet, time, , i];
SetCP[score.sheet.context, x1+delta, y1+16+4]; DrawChar[score.sheet.context, '.];
SetCP[score.sheet.context, x1+delta, y1+16-4]; DrawChar[score.sheet.context, '.];
ENDLOOP;
};
****************************************************************************
data abstractions for a sync
CONSTRAINT: s.note[i] # NIL for all i < s.length
CONSTRAINT: s.note[i] = NIL for all s.length <= i < s.max
CONSTRAINT: chord.note[i].sync = chord.note[j].sync for all i, j < chord.length
****************************************************************************
AddNote:
PUBLIC
PROC[score: ScorePTR, sync: SyncPTR, note: NotePTR] = {
c: ChordPTR ← note.chord;
IF note.sync = sync THEN RETURN; -- already in the sync
FOR i:
CARDINAL
IN [0..(
IF c =
NIL
THEN 1
ELSE c.length))
DO
IF sync.length = sync.max THEN sync ← NewSync[score, sync.max + 4, sync];
IF c # NIL THEN note ← c.note[i];
RemoveNote[score, note.sync, note, FALSE];
note.sync ← sync;
sync.note[sync.length] ← note;
sync.length ← sync.length + 1;
IF note.beam # NIL THEN Beam.SetSyncs[note.beam];
ENDLOOP;
};
RemoveNote:
PUBLIC
PROC[score: ScorePTR, sync: SyncPTR, note: NotePTR, free:
BOOL] = {
does not automatically remove empty syncs! (see Voice.Correct)
IF note = NIL OR sync = NIL THEN RETURN;
FOR i:
CARDINAL
IN [0..sync.length)
DO
IF sync.note[i] # note THEN LOOP;
sync.length ← sync.length - 1;
sync.note[i] ← sync.note[sync.length];
sync.note[sync.length] ← NIL;
ENDLOOP;
IF free AND sync.length = 0 THEN Event.Free[score, sync];
};
NewSync:
PUBLIC
PROC[score: ScorePTR, length:
CARDINAL]
RETURNS[sync: SyncPTR] = {
sync ← NEW[EventRec.sync[length]];
IF old # NIL THEN {
nullSize: CARDINAL = SIZE[EventRec.sync[0]];
oldSize: CARDINAL = SIZE[EventRec.sync[old.max]];
Inline.LongCOPY[from: old, nwords: nullSize-1, to: sync]; -- skip 'max'
Inline.LongCOPY[from: old+nullSize, nwords: oldSize-nullSize, to: sync+nullSize];
SetBackPointers[score, old, sync];
zone.FREE[@old];
RETURN};
};
Free:
PUBLIC
PROC[score: ScorePTR, event: EventPTR] = {
SetBackPointers[score, event, NIL];
zone.FREE[@event];
};
SetBackPointers:
PROC[score: ScorePTR, event, new: EventPTR] =
{
changes all pointers to event to be pointers to new
an event can be pointed at by a score, the cache, notes, the sheet and the styles
index: CARDINAL ← Event.GetScoreIndex[score, event];
IF index # score.length THEN score[index] ← new;
IF index # score.length
AND new =
NIL
THEN {
score.length ← score.length - 1;
FOR i: CARDINAL IN [0..score.length) DO score[i] ← score[i+1]; ENDLOOP;
score[score.length] ← NIL;
};
SELECT event.type
FROM
sync => {
sync: SyncPTR ← Event.Sync[event];
newSync: SyncPTR ← Event.Sync[new];
FOR i: CARDINAL IN [0..sync.length) DO sync[i].sync ← newSync; ENDLOOP};
measure => NULL;
timeSignature => {
IF score.cache.ts1 = event THEN score.cache.ts1 ← Event.TimeSignature[new];
IF score.cache.ts2 = event THEN score.cache.ts2 ← Event.TimeSignature[new]};
keySignature => {
IF score.cache.key1 = event THEN score.cache.key1 ← Event.KeySignature[new];
IF score.cache.key2 = event THEN score.cache.key2 ← Event.KeySignature[new]};
metronome => {
IF score.cache.met1 = event THEN score.cache.met1 ← Event.Metronome[new];
IF score.cache.met2 = event THEN score.cache.met2 ← Event.Metronome[new]};
staves => Sheet.Reset[score];
ENDCASE => ERROR;
};
AddTimes:
PUBLIC
PROC[event: EventPTR, time, toc: Time] ~ {
-- add times to notes
event.time ← event.time + time;
WITH event
SELECT
FROM
sync: SyncPTR =>
FOR i:
CARDINAL
IN [0..sync.length)
DO
sync.note[i].toc ← sync.note[i].toc + toc;
ENDLOOP;
ENDCASE;
Grace:
PUBLIC
PROC[sync: SyncPTR]
RETURNS[
BOOLEAN] ~ {
FOR j:
CARDINAL
IN[0..sync.length)
DO
IF ~sync.note[j].grace THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
InVoice:
PUBLIC
PROC[sync: SyncPTR, voice:
CARDINAL]
RETURNS[
BOOLEAN] ~ {
FOR j:
CARDINAL
IN [0..sync.length)
DO
IF sync.note[j].voice = voice THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
DrawSync:
PROC[score: ScorePTR, s: SyncPTR] = {
b: BeamPTR;
c: ChordPTR;
sync: ARRAY [0..16) OF NotePTR;
x, y: INTEGER;
min, vMin: INTEGER ← 1000;
max, vMax: INTEGER ← -1000;
FOR i: CARDINAL IN [0..s.length) DO sync[i] ← s[i]; ENDLOOP;
FOR i:
CARDINAL
IN [0..s.length)
DO
IF s[i] = NIL THEN EXIT;
IF sync[i] = NIL THEN LOOP;
c ← s[i].chord;
b ← s[i].beam;
IF b # NIL AND b.beam # NIL THEN b ← b.beam;
IF c #
NIL
THEN
FOR j:
CARDINAL
IN [0..c.length)
DO
IF c.note[j] = NIL THEN EXIT;
FOR k:
CARDINAL
IN (i..s.length)
DO
IF sync[k] = c.note[j] THEN sync[k] ← NIL;
ENDLOOP;
ENDLOOP;
IF b # NIL AND NOT Beam.Drawn[score, b] THEN { [] ← Beam.Draw[score, b]; LOOP; };
IF c # NIL AND b = NIL THEN { Chord.Draw[score, c]; LOOP; };
IF b = NIL THEN Note.Draw[score, s[i]];
ENDLOOP;
IF NOT score.sheet.sync THEN RETURN;
draw the sync line
FOR i:
CARDINAL
IN [0..s.length)
DO
IF s[i] = NIL THEN EXIT;
[x, y] ← Sheet.MapNote[score.sheet, s[i]];
min ← MIN[min, y];
max ←
MAX[max, y];
IF ~Note.InVoice[s[i], score.sheet.voice] THEN LOOP;
vMin ← MIN[vMin, y];
vMax ← MAX[vMax, y];
ENDLOOP;
Graphics.SetStipple[score.sheet.context, light];
IF min # vMin
OR max # vMax
THEN
Utility.DrawLine[score.sheet.context, x+4, min-6, x+4, max+6];
Graphics.SetStipple[score.sheet.context, black];
IF vMin # vMax
AND vMin # 1000
THEN
Utility.DrawLine[score.sheet.context, x+4, vMin-6, x+4, vMax+6];
};
Adjust: PUBLIC PROC[score: ScorePTR, s: SyncPTR] = { -- dummy -- };
END.
Adjust:
PUBLIC
PROC[score: ScorePTR, s: SyncPTR] = {
n: NotePTR;
c: ChordPTR;
sync: ARRAY [0..16) OF NotePTR;
delta: INTEGER;
pad: ScratchPad;
top: CARDINAL ← s.length-1;
bottom: CARDINAL ← 0;
length: CARDINAL ← 0;
IF s = NIL THEN RETURN;
IF s.type # sync THEN RETURN;
FOR i: CARDINAL IN [0..s.length) DO sync[i] ← s[i]; ENDLOOP;
build the pad
FOR i:
CARDINAL
IN [0..s.length)
DO
IF (n ← s[i]) = NIL THEN EXIT;
n.delta ← n.accDelta ← 0;
IF n.rest THEN LOOP;
c ← n.chord;
pad[length].n ← n;
pad[length].c ← c;
pad[length].y ← Sheet.MapNote[score.sheet, n].y;
pad[length].acc ← Score.GetAccidental[score, n];
pad[length].stemUp ← IF c = NIL THEN n.stemUp ELSE c.stemUp;
pad[length].stem ← IF pad[length].stemUp THEN pad[length].y+32 ELSE pad[length].y-32;
length ← length+1;
ENDLOOP;
SortPad[FALSE, @pad];
FOR i:
CARDINAL
IN [0..length)
DO
IF i # 0 AND pad[i].y = pad[i-1].y AND pad[i].acc = pad[i-1].acc THEN pad[i].acc ← inKey;
IF pad[i].acc # inKey THEN top ← MIN[top, i];
IF pad[i].acc # inKey THEN bottom ← MAX[bottom, i];
ENDLOOP;
handle note adjacencies within a chord
FOR i:
CARDINAL
IN [0..length)
DO
IF sync[i] = NIL THEN LOOP;
c ← sync[i].chord;
IF c # NIL THEN Chord.Adjust[score.sheet, c] ELSE LOOP;
no need to process a chord more than once
FOR j:
CARDINAL
IN [0..c.length)
DO
FOR k:
CARDINAL
IN [0..s.length)
DO
IF sync[k] = c.note[j] THEN {sync[k] ← NIL; EXIT};
ENDLOOP;
ENDLOOP;
ENDLOOP;
determine the deltas
FOR i:
CARDINAL
IN [1..length)
DO
FOR j:
CARDINAL
DECREASING
IN [0..i)
DO
IF pad[j].c = pad[i].c AND pad[i].c # NIL THEN LOOP;
special case: allow some notes to overlap
IF Overlap[score, i, j, @pad, length] THEN LOOP;
delta ← Note.Delta[score.sheet, pad[j].n];
special case: higher note with stem down and lower with stem up
IF pad[j].stemUp
AND
NOT pad[i].stemUp
THEN
{ IF pad[j].y-pad[i].y >= 8 THEN LOOP;
MoveRightTo[pad[i], delta];
MoveLeft[pad[i], 8];
FOR k: CARDINAL IN [0..i] DO IF pad[j].c = pad[k].c AND pad[k].c # NIL THEN LOOP;
MoveLeft[pad[k], IF pad[k].n.value = whole THEN 10 ELSE 8];
ENDLOOP;
EXIT; };
general case: shift lower note to the right
IF pad[j].y-pad[i].y < 8
THEN
MoveRightTo[pad[i], delta+(IF pad[j].n.value = whole THEN 10 ELSE 8)];
IF pad[i].n.value = whole OR pad[i].n.value = unknown THEN LOOP;
IF pad[j].stem < pad[i].y THEN MoveRightTo[pad[i], delta+2];
IF pad[i].stem > pad[j].y THEN MoveRightTo[pad[i], delta+2];
ENDLOOP;
ENDLOOP;
IF top > bottom THEN RETURN;
determine the deltas for the accidentals
RULES: (in order of precedence)
1) all of the accidentals appear to the left of all of the notes
2) accidentals for notes offset in a chord have the same offset
3) there must be at least three lines vertically between accidentals
4) the highest and lowest accidentals should have the same horizontal position
5) lower accidentals should go to the left of higher accidentals
FOR i: CARDINAL IN [0..length) DO pad[i].x ← -10; ENDLOOP;
FOR i:
CARDINAL
IN [1..length)
DO
IF pad[i-1].y-pad[i].y >= 8 THEN LOOP;
IF pad[i-1].acc = inKey OR pad[i].acc = inKey THEN LOOP;
IF Note.Delta[score.sheet, pad[i-1].n] > Note.Delta[score.sheet, pad[i].n]
THEN pad[i-1].push ← i
ELSE pad[i].push ← i-1;
ENDLOOP;
do top, then whatever pushes bottom, then bottom,
then whatever pushes, then whatever.
PlaceAccidental[score, @pad, top, length];
FOR i:
CARDINAL
IN [0..length)
DO
IF i = top THEN LOOP;
IF pad[i].push # bottom THEN LOOP;
PlaceAccidental[score, @pad, i, length];
ENDLOOP;
PlaceAccidental[score, @pad, bottom, length];
FOR i:
CARDINAL
IN [0..length)
DO
IF i = top OR i = bottom THEN LOOP;
IF pad[i].push = bottom THEN LOOP;
IF pad[i].push = padLength THEN LOOP;
PlaceAccidental[score, @pad, i, length];
ENDLOOP;
FOR i:
CARDINAL
IN [0..length)
DO
IF i = top OR i = bottom THEN LOOP;
IF pad[i].push # padLength THEN LOOP;
PlaceAccidental[score, @pad, i, length];
ENDLOOP;
put the results in accDelta
FOR i:
CARDINAL
IN [0..length)
DO
IF pad[i].acc = inKey THEN LOOP;
pad[i].n.accDelta ← -pad[i].n.delta - 8*pad[i].x;
ENDLOOP;
};
Overlap:
PROC[score: ScorePTR, a, b:
CARDINAL, pad:
POINTER
TO ScratchPad, length:
CARDINAL]
RETURNS[
BOOL] = {
found: BOOL;
top, bottom: CARDINAL ← length;
PartOf:
PROC[i, a:
CARDINAL]
RETURNS[
BOOL] =
INLINE {
RETURN[a = i OR (pad[i].c = pad[a].c AND pad[a].c # NIL)]};
IF pad[a].c =
NIL
AND pad[b].c =
NIL
THEN {
-- just single notes
IF pad[a].y # pad[b].y THEN RETURN[FALSE];
IF pad[a].n.pitch # pad[b].n.pitch THEN RETURN[FALSE];
IF pad[a].stemUp = pad[b].stemUp THEN RETURN[FALSE];
IF pad[a].n.value = whole OR pad[b].n.value = whole THEN RETURN[FALSE];
RETURN[TRUE]};
we have at least one chord; can a and b overlap?
find the tops and bottoms for a
FOR i:
CARDINAL
IN [0..length)
DO
IF ~PartOf[i, a] THEN LOOP;
IF top = length THEN top ← i;
bottom ← i;
ENDLOOP;
IF pad[a].stemUp THEN top ← 0 ELSE bottom ← length-1;
do all of the notes in b which are within top and bottom have a corresponding note in a?
FOR i:
CARDINAL
IN [top..bottom]
DO
IF ~PartOf[i, b] THEN LOOP;
found ← FALSE;
FOR j:
CARDINAL
IN [top..bottom]
DO
IF ~PartOf[j, a] THEN LOOP;
IF pad[i].n.pitch # pad[j].n.pitch OR pad[i].y # pad[j].y THEN LOOP;
IF Note.Delta[score.sheet, pad[i].n] # Note.Delta[score.sheet, pad[j].n] THEN LOOP;
found ← TRUE;
EXIT; ENDLOOP;
IF ~found THEN RETURN[FALSE];
ENDLOOP;
top ← length;
find the tops and bottoms for b
FOR i:
CARDINAL
IN [0..length)
DO
IF ~PartOf[i, b] THEN LOOP;
IF top = length THEN top ← i;
bottom ← i;
ENDLOOP;
IF pad[b].stemUp THEN top ← 0 ELSE bottom ← length-1;
do all of the notes in a which are within top and bottom have a corresponding note in b?
FOR i:
CARDINAL
IN [top..bottom]
DO
IF ~PartOf[i, a] THEN LOOP;
found ← FALSE;
FOR j:
CARDINAL
IN [top..bottom]
DO
IF ~PartOf[j, b] THEN LOOP;
IF pad[i].n.pitch # pad[j].n.pitch OR pad[i].y # pad[j].y THEN LOOP;
IF Note.Delta[score.sheet, pad[i].n] # Note.Delta[score.sheet, pad[j].n] THEN LOOP;
found ← TRUE;
EXIT; ENDLOOP;
IF ~found THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
PlaceAccidental:
PROC[score: ScorePTR, pad:
POINTER
TO ScratchPad, i, length:
CARDINAL] = {
blocked: BOOL;
d, h: INTEGER;
IF i = padLength THEN RETURN;
IF pad[i].acc = inKey THEN RETURN;
FOR j: CARDINAL IN [0..length) DO IF pad[j].push = i THEN pad[i].x ← MAX[pad[j].x, pad[i].x]; ENDLOOP;
FOR d
IN [
MAX[pad[i].x, 0]..6)
DO blocked ←
FALSE;
FOR j:
CARDINAL
IN [0..length)
DO
SELECT (
IF pad[j].y > pad[i].y
THEN pad[j].acc
ELSE pad[i].acc)
FROM
flat, doubleFlat, doubleSharp => h ← 8;
ENDCASE => h ← 16;
SELECT (
IF pad[j].y > pad[i].y
THEN pad[i].acc
ELSE pad[j].acc)
FROM
doubleSharp => NULL;
ENDCASE => h ← h+8;
IF pad[j].y-pad[i].y >= h THEN LOOP;
IF pad[i].y-pad[j].y >= 24 THEN EXIT;
IF pad[i].y-pad[j].y >= h THEN LOOP;
IF d = 0 AND Note.Delta[score.sheet, pad[j].n] < 0 THEN {blocked ← TRUE; EXIT};
IF j = i THEN LOOP;
IF j = pad[i].push THEN LOOP;
IF pad[j].acc # inKey AND pad[j].x = d THEN {blocked ← TRUE; EXIT};
ENDLOOP;
IF blocked THEN LOOP;
MoveAccTo[pad, i, d];
RETURN;
ENDLOOP;
ERROR;
};
MoveRightTo:
PROC[p: Scratch, x:
INTEGER] =
INLINE
{
IF p.c =
NIL
THEN p.n.delta ←
MAX[p.n.delta, x]
ELSE p.c.delta ← MAX[p.c.delta, x - p.n.delta];
};
MoveLeft:
PROC[p: Scratch, delta:
INTEGER] =
INLINE
{
IF p.c =
NIL
THEN p.n.delta ←
MIN[p.n.delta, -delta]
ELSE p.c.delta ← MIN[p.c.delta, -delta - p.n.delta];
};
MoveAccTo:
PROC[p:
POINTER
TO ScratchPad, i:
CARDINAL, delta:
INTEGER] =
{
p[i].x ← MAX[p[i].x, delta];
IF p[i].push # padLength AND p[p[i].push].x > -1 THEN MoveAccTo[p, p[i].push, p[i].x+1];
};
SortPad:
PROC[ascending:
BOOL, pad:
POINTER
TO ScratchPad] =
INLINE
{
temp: Scratch;
FOR i:
CARDINAL
IN [0..padLength)
DO
IF pad[i].n = NIL THEN EXIT;
FOR j:
CARDINAL
IN (i..padLength)
DO
IF pad[j].n = NIL THEN EXIT;
IF
NOT ascending
AND pad[i].y < pad[j].y
OR ascending AND pad[i].y > pad[j].y
THEN { temp ← pad[i]; pad[i] ← pad[j]; pad[j] ← temp; };
ENDLOOP;
ENDLOOP;
};
padLength: CARDINAL = 16;
ScratchPad: TYPE = ARRAY [0..padLength) OF Scratch;
Scratch:
TYPE =
RECORD[x, y, stem:
INTEGER ← 0, push:
CARDINAL ← padLength,
acc: Accidental, stemUp: BOOL,
c: ChordPTR, n: NotePTR ← NIL];