ScoreImplB.mesa
Copyright (C) 1983, 1984 Xerox Corporation. All rights reserved.
Author: John Maxwell
Last Edited by: Maxwell, November 22, 1983 12:29 pm
Last Edited by: Doug Wyatt, June 12, 1984 11:55:05 am PDT
DIRECTORY
Beam USING [Drawn],
Event USING [Adjust, Draw, GetScoreIndex],
Graphics USING [Context, DrawBox, DrawChar, GetBounds, SetCP, SetStipple],
MusicDefs,
Note USING [DrawTie, InVoice],
Piece USING [NearestEvent],
Score USING [Draw, GetKey, Justify, LogicalToPhysical, ScalePhysical, SetStyle, ShowPitch],
Selection USING [Draw, selection],
Sheet USING [default, Draw, FindSection, FindStaves, Height, HiLite, MapNote, Scale, SetBegin];
ScoreImplB: CEDAR PROGRAM
IMPORTS Beam, Graphics, MusicDefs, Note, Piece, Score, Selection, Sheet, Event
EXPORTS Score
= BEGIN OPEN MusicDefs;
****************************************************************************
drawing the score
****************************************************************************
Draw: PUBLIC PROC[score: ScorePTR, erase: BOOLEAN] = {
sheet: SheetPTR ← score.sheet;
Selection.Draw[]; -- remove selection from screen
IF erase THEN {
SetBrush[score, white, opaque];
Graphics.DrawBox[sheet.context, Graphics.GetBounds[sheet.context]];
SetBrush[score, black, transparent];
Sheet.Draw[sheet]};
DrawInterval[score, sheet.begin, sheet.endTime];
Selection.Draw[];
};
Redraw: PUBLIC PROC[score: ScorePTR, t1, t2: Time] = {
x, y: INTEGER;
select: BOOLEAN;
selection: SelectionPTR ← Selection.selection;
SetBrush[score, black, transparent];
SELECT score.sheet.scale FROM
1 => IF t2-t1 > 700 THEN {Score.Draw[score]; RETURN};
2 => IF t2-t1 > 2000 THEN {Score.Draw[score]; RETURN};
4 => IF t2-t1 > 8000 THEN {Score.Draw[score]; RETURN};
ENDCASE;
select ← selection.lineSelect AND selection.select2 > t1-40 AND selection.select1 < t2+50;
IF select THEN Selection.Draw[]; -- remove selection from screen
Sheet.HiLite[score.sheet, white, t1-20, t2+30];
DrawInterval[score, t1-30, t2+40];
IF select THEN Selection.Draw[];
IF selection.lineSelect THEN RETURN;
SetBrush[score, black, invert];
FOR i: CARDINAL IN [0..selection.length) DO
IF selection.note[i] = NIL THEN LOOP;
IF selection[i].sync.time > t2+40 OR selection[i].sync.time < t1-30 THEN LOOP;
IF ~Note.InVoice[selection[i], score.sheet.voice] THEN LOOP;
[x, y] ← Sheet.MapNote[score.sheet, selection[i]];
Graphics.SetCP[score.sheet.context, x, y];
Graphics.DrawChar[score.sheet.context, 170C];
ENDLOOP;
};
DrawInterval: PROC[score: ScorePTR, t1, t2: Time] = {
n: NotePTR;
staves: StavesPTR;
j: CARDINAL ← 0;
sheet: SheetPTR ← score.sheet;
[] ← Beam.Drawn[score, NIL]; -- clears the beam cache
SetBrush[score, black, transparent];
draw octava markings first
staves ← Sheet.FindStaves[sheet, t1];
FOR i: CARDINAL IN [0..staves.length) DO
IF staves.staff[i].pitch # 15 AND staves.staff[i].pitch # 60 THEN LOOP;
FOR j: CARDINAL DECREASING IN [0..score.length) DO
IF score.event[j].time >= t1 THEN LOOP;
IF score.event[j].type # staves THEN LOOP;
WITH score.event[j] SELECT FROM
ev: StavesPTR => {
IF ev.staves NOT IN [octava1..octava2] THEN LOOP;
IF ev.value # i THEN LOOP;
IF ev.staves = octava2 THEN EXIT};
ENDCASE => ERROR;
Event.Draw[score, score.event[j]];
EXIT; ENDLOOP;
ENDLOOP;
pre-Adjust the first 10 syncs (beams may draw notes in syncs downstream)
j ← 0;
IF score.sheet.display = graphical THEN
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].time < t1 THEN LOOP;
IF score.event[i].time > t2 THEN EXIT;
IF score.event[i].type = sync THEN {
IF j = 10 THEN EXIT ELSE j ← j+1;
Event.Adjust[score, NARROW[score.event[i]]]};
ENDLOOP;
draw the syncs
j ← 0;
FOR i: CARDINAL IN [0..score.length) DO
IF score.event[i].time < t1 THEN LOOP;
IF score.event[i].time > t2 THEN {j ← i; EXIT};
IF score.sheet.display = graphical THEN {
plus10: CARDINALMIN[i+10, score.length-1];
IF score.event[plus10].type = sync
THEN Event.Adjust[score, NARROW[score.event[plus10]]]};
Event.Draw[score, score.event[i]];
IF NOT sheet.printing AND AnyBug[] THEN EXIT;
ENDLOOP;
draw ties that go off the end of the section
IF j # 0 THEN FOR i: CARDINAL IN [j..score.length) DO
IF score.event[i].time > t2+200 THEN EXIT;
WITH score.event[i] SELECT FROM
sync: SyncPTR => {
FOR j: CARDINAL IN [0..sync.length) DO
IF (n ← sync.note[j]) = NIL THEN EXIT;
IF n.tie = NIL THEN LOOP;
IF n.tie.sync.time > t2 THEN LOOP;
IF ~Note.InVoice[n, score.sheet.voice]
THEN Graphics.SetStipple[score.sheet.context, light]
ELSE Graphics.SetStipple[score.sheet.context, black];
Note.DrawTie[score, n];
ENDLOOP;
};
ENDCASE;
ENDLOOP;
[] ← Beam.Drawn[score, NIL];
};
****************************************************************************
printing the score
****************************************************************************
Print: PUBLIC PROC[score: ScorePTR, splines: BOOLEAN] = {};
****************************************************************************
changing the view
****************************************************************************
Look: PUBLIC PROC[score: ScorePTR, look: LookCommand, switch: BOOLEAN, n: INTEGER] = {
ENABLE Piece.Overflow => IF score = old THEN score ← new;
draw: BOOLEANTRUE;
SELECT look FROM
accidental => score.sheet.accidental ← switch;
hardcopy => {
Sheet.Scale[score, IF switch THEN 2 ELSE 1];
score.sheet.hardcopy ← switch};
justified => {
score.sheet.density ← n;
Score.Justify[score, Selection.selection.select1, Selection.selection.select2]};
logical => Score.LogicalToPhysical[score, Selection.selection.select1, Selection.selection.select2];
noCarry => score.sheet.noCarry ← switch;
notehead => score.sheet.notehead ← switch;
overview => Overview[score, switch];
physical => Score.ScalePhysical[score, 512/n];
style => Score.SetStyle[score, n, 0, EndOfScore[score]];
sync => score.sheet.sync ← switch;
voice => {score.sheet.voice ← IF ~switch THEN noVoice ELSE n};
ENDCASE;
};
Overview: PROC[score: ScorePTR, switch: BOOLEAN] = {
time: Time;
x, y: INTEGER;
SELECT TRUE FROM
score.sheet.scale < 4 AND ~switch => {score.flash ← TRUE; RETURN};
score.sheet.scale > 2 AND switch => {score.flash ← TRUE; RETURN};
switch => {Sheet.Scale[score, 4]; Sheet.SetBegin[score.sheet, 0]};
YellowBug[] => {
[x, y] ← Sheet.ScreenPoint[score.sheet];
time ← Sheet.NearestTime[score.sheet, x, y].time;
Sheet.Scale[score, IF score.sheet.hardcopy THEN 2 ELSE 1];
Sheet.SetBegin[score.sheet, time]};
ENDCASE => {
Sheet.Scale[score, IF score.sheet.hardcopy THEN 2 ELSE 1];
Sheet.SetBegin[score.sheet, 0]};
};
****************************************************************************
getting accidentals
****************************************************************************
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];
GetAccidental: PUBLIC PROC[score: ScorePTR, n: NotePTR] RETURNS[Accidental] = {
hack to set n.shown
n.shown ← GetAccInternal[score, n];
RETURN[n.shown];
};
GetAccInternal: PROC[score: ScorePTR, n: NotePTR] RETURNS[Accidental] = {
a: Accidental;
prior: NotePTR;
index: CARDINAL;
pitch, key: INTEGER;
normal: Accidental;
noteHeight: INTEGER;
IF NOT score.sheet.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 score.sheet.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[score, n.sync.time];
pitch ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key];
noteHeight ← Sheet.Height[score.sheet, n.sync.time, pitch, n.staff];
index ← Event.GetScoreIndex[score, n.sync];
IF index = score.length THEN index ← EventIndex[score, n.sync.time];
prior ← PriorNote[score, 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];
};
EventIndex: PROC[score: ScorePTR, time: Time] RETURNS[s: CARDINAL] = {
s ← Piece.NearestEvent[score, time];
IF score.event[s] # NIL AND score.event[s].time >= time THEN RETURN[s];
FOR i: CARDINAL DECREASING IN [0..s+10) DO
IF score.event[i] = NIL THEN LOOP;
IF score.event[i].time > time THEN LOOP;
RETURN[i+1];
ENDLOOP;
};
PriorNote: PUBLIC PROC[score: ScorePTR, pitch, height: INTEGER, index: CARDINAL] RETURNS[NotePTR] = {
n: NotePTR;
sync: SyncPTR;
newPitch, key: INTEGER;
FOR i: CARDINAL DECREASING IN [0..index) DO
IF Measure[score.event[i]] THEN RETURN[NIL];
IF score.event[i].type # sync THEN LOOP;
key ← Score.GetKey[score, score.event[i].time];
sync ← NARROW[score.event[i]];
FOR j: CARDINAL IN [0..sync.length) DO
IF (n ← sync.note[j]) = NIL THEN EXIT;
IF n.rest THEN LOOP;
newPitch ← Score.ShowPitch[score.sheet, n.pitch, n.spelled, key];
IF NOT newPitch IN [pitch-2..pitch+2] THEN LOOP;
IF Sheet.Height[score.sheet, score.event[i].time, newPitch, n.staff] # height THEN LOOP;
RETURN[n];
ENDLOOP;
ENDLOOP;
RETURN[NIL];
};
assert: Accidental = LOOPHOLE[LOOPHOLE[LAST[Accidental], CARDINAL]+1];
NormalAcc: PUBLIC PROC[key, pitch: INTEGER] RETURNS[Accidental] = {
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];
};
DefaultAcc: PROC[key, pitch: INTEGER] RETURNS[Accidental] = {
SELECT NormalAcc[0, pitch] FROM
inKey => RETURN[natural];
sharp => RETURN[IF key < 0 THEN flat ELSE sharp];
ENDCASE => ERROR;
};
AddToPitch: PUBLIC PROC[key, pitch, delta: INTEGER] RETURNS[INTEGER] = {
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;
};
KeyHeight: PUBLIC PROC[key, pitch: INTEGER] RETURNS[INTEGER] = {
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.
Print: PUBLIC PROC[splines: BOOLEAN] = {
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
Event.Adjust[score.event[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];
};