--Author: John Maxwell --last modified: October 23, 1981 12:58 PM DIRECTORY Beam USING [AddChord,Remove], Chord USING [default,InVoice,RemoveNote,Sort], Graphics USING [DrawRectangle,MoveTo,SetTexture], MusicDefs, Note USING [DrawHead,FindChord,Width], Sheet USING [Map,MapNote], Sync USING [AddNote], Utility USING [DrawChar,DrawLine,FreeChord]; ChordImpl: PROGRAM IMPORTS Beam, Chord, Graphics, MusicDefs, Note, Sheet, Sync, Utility EXPORTS Chord = BEGIN OPEN Chord, MusicDefs, Graphics, Utility; --**************************************************************************** --data abstractions for a chord --CONSTRAINT: c.note[i].sync = c.note[j].sync for all i,j --CONSTRAINT: c.note[i].beam = c.note[j].beam for all i,j --**************************************************************************** AddNote:PUBLIC PROCEDURE[c:ChordPTR,n:NotePTR] = BEGIN oldBeam:BeamPTR = n.beam; oldChord:ChordPTR = Note.FindChord[n]; chordSync:SyncPTR = IF c.note[0]#NIL THEN c.note[0].sync ELSE n.sync; chordBeam:BeamPTR = IF c.note[0]#NIL THEN c.note[0].beam ELSE NIL; --check for aborted attempts IF c=oldChord THEN RETURN; IF c.note[chordLength-1]#NIL THEN Overflow[chord]; --put the note in the chord FOR i:CARDINAL IN [0..chordLength) DO IF c.note[i]=NIL THEN {c.note[i] _ n; EXIT}; ENDLOOP; n.chord _ c; n.beam _ chordBeam; IF chordBeam=NIL AND oldBeam#NIL THEN Beam.AddChord[oldBeam,c]; IF n.sync#chordSync THEN Sync.AddNote[chordSync,n]; --remove the note from old places IF oldChord#NIL THEN Chord.RemoveNote[oldChord,n]; IF oldBeam#NIL AND oldChord=NIL AND oldBeam#n.beam THEN Beam.Remove[oldBeam,n,NIL,NIL]; END; RemoveNote:PUBLIC PROCEDURE[c:ChordPTR,n:NotePTR] = BEGIN i,k:CARDINAL _ chordLength; IF n=NIL OR c=NIL THEN RETURN; --keep the notes packed; FOR i DECREASING IN [0..chordLength) DO IF c.note[i]=NIL THEN k_i; IF c.note[i]#n THEN LOOP; IF n.chord=c THEN n.chord _ NIL; n.beam _ NIL; -- removing a note from a chord also removes it from the beam c.note[i]_c.note[k-1]; c.note[k-1] _ NIL; ENDLOOP; IF c.note[1]=NIL THEN Utility.FreeChord[@c]; END; Sort:PUBLIC PROCEDURE[c:ChordPTR,up:BOOLEAN] RETURNS[n:CARDINAL] = BEGIN i,j:CARDINAL; temp:NotePTR; FOR i IN [0..chordLength) DO IF c.note[i]=NIL THEN BEGIN n_i; EXIT; END; FOR j IN (i..chordLength) DO IF c.note[j]=NIL THEN EXIT; IF NOT up AND c.note[i].pitchc.note[j].pitch THEN BEGIN temp_ c.note[i]; c.note[i]_ c.note[j]; c.note[j]_ temp; END; ENDLOOP; ENDLOOP; END; SetDefaultStem:PUBLIC PROCEDURE[c:ChordPTR] = BEGIN n:CARDINAL; x0,xn,y0,yn,middle0,middlen:INTEGER; n _ Chord.Sort[c,TRUE]; [x0,y0] _ Sheet.Map[c.note[0].sync.time,c.note[0].pitch,c.note[0].staff]; [xn,yn] _ Sheet.Map[c.note[n-1].sync.time,c.note[n-1].pitch,c.note[n-1].staff]; middle0 _ Sheet.Map[c.note[0].sync.time,,c.note[0].staff].y+16; middlen _ Sheet.Map[c.note[n-1].sync.time,,c.note[n-1].staff].y+16; SELECT TRUE FROM (yn+y0)/2 > middlen => c.stemUp _ FALSE; (yn+y0)/2 < middle0 => c.stemUp _ TRUE; (yn+y0)/2 > (middlen+middle0)/2 => c.stemUp _ TRUE; ENDCASE => c.stemUp _ FALSE; END; --****************************************************************** --procedures for drawing chords on the screen --****************************************************************** Draw:PUBLIC PROCEDURE[c:ChordPTR,stem:INTEGER] = BEGIN n:NotePTR; flag:NoteValue; two:REAL = 2; dotX:INTEGER_0; inVoice:BOOLEAN; --xWide,yWide:INTEGER; nonGrace:BOOLEAN _ FALSE; minStaff,maxStaff:CARDINAL; x,y,yMin,yMax,mid,width:INTEGER; inVoice _ ~(voice AND NOT Chord.InVoice[c,selectedVoice]); -- draw the note heads FOR i:CARDINAL IN [0..chordLength) DO IF c.note[i]=NIL THEN EXIT ELSE n _ c.note[i]; IF ~n.dotted THEN LOOP; [x,y] _ Sheet.MapNote[n]; dotX _ MAX[dotX,x+Note.Width[n]+2]; ENDLOOP; mid _ Sheet.Map[n.sync.time,,n.staff].y+16; FOR i:CARDINAL IN [0..chordLength) DO IF c.note[i]=NIL THEN EXIT ELSE n _ c.note[i]; [x,y] _ Sheet.MapNote[n]; Note.DrawHead[n,x,y,dotX]; IF i=0 THEN {yMin _ yMax _ y; width_0}; width _ MAX[width,Note.Width[n]]; -- IF n.delta#0 THEN { -- xWide _ MIN[xWide,x]; -- yWide _ IF y>mid THEN MIN[yWide,y] ELSE MAX[yWide,y]}; IF ~n.grace THEN nonGrace_TRUE; IF y<=yMin THEN {yMin_y; minStaff _ n.staff}; IF y>=yMax THEN {yMax_y; maxStaff _ n.staff}; ENDLOOP; IF NOT show.notehead THEN RETURN; -- draw the ledger lines Graphics.SetTexture[context,IF inVoice THEN black ELSE light]; IF show.display=graphical THEN x _ x - n.delta; --subtract off x offset before drawing stem FOR i:INTEGER IN [3..9] WHILE yMax-mid >= i*d DO IF nonGrace THEN DrawRectangle[context,[x-3,mid+1+i*d],[x+width+3,mid-1+i*d]] ELSE DrawRectangle[context,[x-3,mid+1/two+i*d],[x+width+3,mid-1/two+i*d]]; ENDLOOP; FOR i:INTEGER IN [3..9] WHILE mid-yMin >= i*d DO IF nonGrace THEN DrawRectangle[context,[x-3,mid+1-i*d],[x+width+3,mid-1-i*d]] ELSE DrawRectangle[context,[x-3,mid+1/two-i*d],[x+width+3,mid-1/two-i*d]]; ENDLOOP; IF n.value=whole OR n.value=unknown THEN RETURN; --draw the stem flag _ n.value; IF stem#default -- drawing part of or beam THEN {stem_(IF c.stemUp THEN stem+1 ELSE stem-1); flag_quarter} ELSE SELECT TRUE FROM nonGrace AND c.stemUp => stem _ MAX[yMax+(7*d)/2,mid]; nonGrace AND ~c.stemUp => stem _ MIN[yMin-(7*d)/2,mid]; c.stemUp => stem _ yMax+2*d; ~c.stemUp => stem _ yMin-2*d; ENDCASE; IF c.stemUp THEN DrawLine[x+width,yMin,x+width,stem] ELSE DrawLine[x,yMax,x,stem]; --draw the flag IF nonGrace THEN MoveTo[context,[x,IF c.stemUp THEN stem-24 ELSE stem+24]] ELSE MoveTo[context,[x,IF c.stemUp THEN stem-17 ELSE stem+17]]; SELECT TRUE FROM ~nonGrace => IF c.stemUp THEN DrawChar[context,133C] ELSE DrawChar[context,134C]; flag=eighth => IF c.stemUp THEN DrawChar[context,153C] ELSE DrawChar[context,163C]; flag=sixteenth => IF c.stemUp THEN DrawChar[context,152C] ELSE DrawChar[context,162C]; flag=thirtysecond => IF c.stemUp THEN DrawChar[context,151C] ELSE DrawChar[context,161C]; flag=sixtyfourth => IF c.stemUp THEN DrawChar[context,151C] ELSE DrawChar[context,161C]; ENDCASE; END; d:CARDINAL=8; --**************************************************************************** --look graphical --**************************************************************************** Adjust:PUBLIC PROCEDURE[c:ChordPTR] = BEGIN n:NotePTR; pad:ScratchPad; delta:INTEGER; last:BOOLEAN _ FALSE; i,length:CARDINAL; --build the pad, initialize FOR i IN [0..chordLength) DO IF (n_c.note[i])=NIL THEN BEGIN length _ i; EXIT; END; [,pad[i].y] _ Sheet.MapNote[n]; pad[i].n _ n; n.delta _ 0; ENDLOOP; SortPad[c.stemUp,@pad]; c.delta _ 0; --determine the deltas on the notes IF c.stemUp THEN delta _ 8 ELSE delta _ -8; FOR i IN [1..length) DO IF last THEN BEGIN last_FALSE; LOOP; END; IF ABS[pad[i-1].y-pad[i].y]>= 8 THEN LOOP; pad[i].n.delta _ delta; last _ TRUE; ENDLOOP; END; SortPad:PROCEDURE[ascending:BOOLEAN,pad:POINTER TO ScratchPad] = BEGIN i,j:CARDINAL; temp:Scratch; FOR i IN [0..syncLength) DO IF pad[i].n=NIL THEN EXIT; FOR j IN (i..syncLength) DO IF pad[j].n=NIL THEN EXIT; IF NOT ascending AND pad[i].ypad[j].y THEN BEGIN temp_ pad[i]; pad[i]_ pad[j]; pad[j]_ temp; END; ENDLOOP; ENDLOOP; END; Delta:PROCEDURE[n:NotePTR] RETURNS[INTEGER] = BEGIN c:ChordPTR _ Note.FindChord[n]; IF c=NIL THEN RETURN[n.delta] ELSE RETURN[n.delta+c.delta]; END; ScratchPad:TYPE= ARRAY [0..syncLength) OF Scratch; Scratch:TYPE = RECORD[x,y,stem:INTEGER_0,push:CARDINAL_syncLength, acc:Accidental,stemUp:BOOLEAN, c:ChordPTR,n:NotePTR_NIL]; END.. (0,3648)(1,4288)(2,4939)(3,5574)\638i5I197b7B808b10B109i22I331b4B428b14B774b4B1184i41I1i1I1671i14I81b6B123i26I196i34I