-- MusicDefs.mesa
-- Author: John Maxwell
-- last modified: May 21, 1982 11:29 am

DIRECTORY
   Graphics USING [Context, FontRef, PaintMode, SetPaintMode, SetStipple],
	KeyboardFace USING [keyboard],
	MouseFace USING [buttons];

MusicDefs: DEFINITIONS IMPORTS Graphics, KeyboardFace, MouseFace =
BEGIN

Error: SIGNAL[s: STRING];
Notify: SIGNAL[s: STRING];
Overflow: SIGNAL[type: Sequence];
Sequence: TYPE = {beam,beamHeap,chord,chordHeap,piece,score,sheet,sync};

-- ****************************************************************************
-- the basic music types
-- ****************************************************************************

score: ScorePTR; -- the root of all of the data
ScorePTR: TYPE = LONG POINTER TO ScoreRec;
ScoreRec: TYPE = RECORD [
	name: LONG STRING ← NIL, -- name of the score
   beamHeap: BeamHeapPTR ← NIL, -- collection of beams
   chordHeap: ChordHeapPTR ← NIL, -- collection of chords
   cache: CachePTR ← NIL, -- current key, time signature, etc.
   sheet: SheetPTR ← NIL, -- cached information about the sheet (derived from the piece)
   selection: SelectionPTR ← NIL, -- information about the currect selection
   view: ViewRec ← [],-- global view information
   length: CARDINAL ← 0, -- length of score
   event: SEQUENCE max: CARDINAL OF EventPTR]; -- collection of events
endOfScore: CARDINAL = LAST[CARDINAL];
       
-- PiecePTR: TYPE = LONG POINTER TO PieceRec;
-- PieceRec: TYPE = RECORD[
	-- length: CARDINAL ← 0, ++ actual length of piece
	-- sync: SEQUENCE max: CARDINAL OF EventPTR]; ++ collection of syncs

EventPTR: TYPE = LONG POINTER TO EventRec;
EventRec: TYPE = RECORD[
   time: Time ← 0, -- measured in pixels from beginning of piece
	variant: SELECT type: EventType FROM
	   sync => [length: CARDINAL, -- actual length of the sequence
	      note: SEQUENCE max: CARDINAL OF NotePTR], -- collection of notes
	   measure => [measure: MeasureType, eol: BOOLEAN], -- eol = force to end of line  
	   timeSignature => [ts: TimeSignature ← [4,4]],
	   keySignature => [key: INTEGER],
	   metrenome => [metrenome: INTEGER],
	   staves => [ -- marks a change in staffing
	      staves: StavesType ← style,  
	      height: INTEGER ← 0, 
	      offset: INTEGER ← 0,
	      length: [0..6) ← 0, -- number of staffs in following array
	      index: [0..6) ← 0, -- index of the staff that we are interested in (for clefs and ottava)
	      staff: ARRAY [0..6) OF Staff],
	   ENDCASE];
Time: TYPE = LONG INTEGER;
SyncPTR: TYPE = LONG POINTER TO sync EventRec; -- 'sync' comes from 'synchronize'
StavesPTR: TYPE = LONG POINTER TO staves EventRec;
StavesType: TYPE = {style, clef, octava1, octava2};
EventType: TYPE = MACHINE DEPENDENT {sync (0), measure (1),  
	timeSignature (7), keySignature (8), metrenome (9), staves (10), spare14 (31)};
MeasureType: TYPE = {measure, repeat1, repeat2, endMeasure, doubleMeasure, m5};
TimeSignature: TYPE = RECORD [top, bottom: [0..32)];

NotePTR: TYPE = LONG POINTER TO NoteRec;
NoteRec: TYPE = MACHINE DEPENDENT RECORD[
	sync: SyncPTR ← NIL, -- pointer back to sync
	beam: BeamPTR ← NIL, -- pointer back to beam
	chord: ChordPTR ← NIL, -- pointer back to chord
	pitch: INTEGER ← 44, -- pitch of the note
	voice: CARDINAL ← 0, -- the voice that this note belongs to
	value: NoteValue ← unknown, -- what is the logical value of this note?
	spelled: Accidental ← inKey, -- does this note have an explicit spelling on it?
	rest: BOOLEAN ← FALSE, -- is this a rest note?
	dotted: BOOLEAN ← FALSE, -- is this note dotted?
	doubleDotted: BOOLEAN ← FALSE, -- is this note double dotted?
	grace: BOOLEAN ← FALSE, -- is this a grace note?
	stemUp: BOOLEAN ← TRUE, -- is the stem up or down?
	spare1: BOOLEAN ← FALSE, 
	embellish: Embellishment ← none, -- does this note have an embellishment on it?
	tied: BOOLEAN ← FALSE, -- is this the second note of a tied note?
	tie: NotePTR ← NIL, -- pointer to the second note of a tie
	tieHeight: INTEGER ← 0, -- the height of said tie
	show: BOOLEAN ← FALSE,
	shown: Accidental ← inKey, -- the accidental displayed on the screen (cached)
	spare: [0..256) ← 0,
	staff: [0..16) ← 1, -- the index of the staff that this note is on
	delta: [-128..128) ← 0, -- how far to move the note relative to the sync
	accDelta: [-128..128) ← 0, -- how far to move the accidental relative to the sync
	toc: LONG CARDINAL ← 0, -- physical-note time of occurance (in clock pulses)
	duration: CARDINAL ← 0]; -- physical-note duration (in clock pulses)
NoteValue: TYPE= {whole, half, quarter, eighth, sixteenth, thirtysecond, sixtyfourth, unknown};
Accidental: TYPE={doubleSharp, sharp, natural, inKey, flat, doubleFlat};
Embellishment: TYPE = {none, trill, mordent1, mordent2, e1, e2, e3, e4};

PhysicalNote: TYPE = RECORD[
	pitch: INTEGER ← 0,
	duration: CARDINAL ← 0,
	loudness: CARDINAL ← 0,
	toc: LONG CARDINAL ← 0];  --toc is the physical time of occurrance

ChordPTR: TYPE = LONG POINTER TO ChordRec;
ChordRec: TYPE = RECORD[
	stemUp: BOOLEAN ← TRUE,
	delta: INTEGER ← 0, -- offset from center line of sync (cached)
	length: CARDINAL ← 0, -- length of note sequence
	note: SEQUENCE max: CARDINAL OF NotePTR];

BeamPTR: TYPE = LONG POINTER TO BeamRec;
BeamRec: TYPE = RECORD[
	tilt: REAL, -- tilt of beam
	beamed: BOOLEAN, -- does the beam show, or do you use brackets instead?
	ntuple, against: [0..128), -- for ntupled beams; the ratio is what is important
	sync1, sync2: SyncPTR ← NIL, -- first and last sync in beam (cached)
	beam: BeamPTR ← NIL, -- beam that this beam belongs to
	height: INTEGER, -- height of beam from top of left-most note
	invisible: BOOLEAN ← FALSE,
	length: [0..1024), -- length of chord sequence
	staff: [0..32), -- graphical information
	chord: SEQUENCE max: CARDINAL OF VariousPTR];
VariousPTR: TYPE = RECORD[SELECT type: {note,chord,beam} FROM
       note => [n: NotePTR], chord=> [c: ChordPTR], beam=>[b: BeamPTR], ENDCASE];
endOfBeam: VariousPTR;  --avoids a call to BlockEqualCodeLong

-- ******************************************************************************
-- other types
-- ******************************************************************************

BeamHeapPTR: TYPE = LONG POINTER TO RECORD[
	length: CARDINAL ← 0,
	beam: SEQUENCE max: CARDINAL OF BeamPTR];
ChordHeapPTR: TYPE = LONG POINTER TO RECORD[
	length: CARDINAL ← 0,
	chord: SEQUENCE max: CARDINAL OF ChordPTR];

CachePTR: TYPE = LONG POINTER TO CacheRec;
CacheRec: TYPE = RECORD[ -- all of the structural (non-sync) events
	length: CARDINAL ← 0,
	event: SEQUENCE max: CARDINAL OF EventPTR];

SheetPTR: TYPE = LONG POINTER TO SheetRec;
SheetRec: TYPE = RECORD[
	styles: StylePTR ← NIL,
	context: Graphics.Context ← NIL,
	top: INTEGER ← 0, -- the y position of the top of the score (for scrolling)
	begin, endTime: Time ← 0, -- start and end of the part of the score that is currently visible
	dirty1, dirty2: Time ← 0, -- the portion of the score that needs to be repainted
	length: CARDINAL ← 0, -- length of sheet sequence
	section: SEQUENCE max: CARDINAL OF Section]; -- sequence of sections
Section: TYPE = RECORD[  
	time: Time ← 0, -- actual time of beginning of section.  (may be different from staves.time)
	x, y: INTEGER ← 0, -- position of the lower left corner of the section
	page: INTEGER ← 0, -- the page that this section is on
	key: INTEGER ← 0, -- the key for this section
	staves: StavesPTR ← NIL];
	
StylePTR: TYPE = LONG POINTER TO StyleRec;
StyleRec: TYPE = RECORD[
	length: CARDINAL ← 0,
	style: SEQUENCE max: CARDINAL OF Style];
Style: TYPE = RECORD[
	height: INTEGER ← 0, 
	offset: INTEGER ← 0,
	length: [0..6) ← 0, -- number of staffs in following array
	staff: ARRAY [0..6) OF Staff];
Staff: TYPE = RECORD[
	pitch: INTEGER ← 0, -- pitch of note on bottom line
	y: INTEGER ← 0]; -- height relative to bottom line

SelectionPTR: TYPE = LONG POINTER TO SelectionRec;
SelectionRec: TYPE = RECORD[
	voice: CARDINAL ← noVoice, -- currently selected voice
	maxVoice: CARDINAL ← 0, -- index of the highest numbered voice
	lineSelect: BOOLEAN ← FALSE, -- line selection or note selection?
	select1, select2: Time ← 0, -- line selected
	greySelect1, greySelect2: Time ← 0, -- secondary line selection
	length: CARDINAL ← 0, -- length of note selection
	note: SEQUENCE max: CARDINAL OF NotePTR]; -- notes selected	
noVoice: CARDINAL = 1000;

ViewRec: TYPE = RECORD[
	scale: INTEGER ← 1, -- means 'scale' in physical mode, 'justification' in graphical mode.
	accidental: BOOLEAN ← TRUE,
	notehead: BOOLEAN ← TRUE,
	sync: BOOLEAN ← FALSE,
	display: DisplayMode ← graphical,
	noCarry: BOOLEAN ← FALSE,
	hardcopy: BOOLEAN ← FALSE, -- display in hardcopy mode
	printing: BOOLEAN ← FALSE, -- we are printing instead of displaying
	command: BOOLEAN ← FALSE, -- we have a new command
	flash: BOOLEAN ← FALSE]; -- flash the screen for an error 
	
DisplayMode: TYPE = {physical, logical, graphical};

ObjectType: TYPE = {measure, note, leftBeam, rightBeam, tie};
LookCommand: TYPE = {accidental, graphical, hardcopy, justified, logical, noCarry, notehead, overview, physical, sheet, sync, voice};

-- chordLength: CARDINAL = 10;
-- syncLength: CARDINAL = 13;
-- beamLength: CARDINAL = 16;
sheetLength: CARDINAL= 350;
maxScoreLength: CARDINAL = MAX[maxPieceLength,3000]; --6000
maxPieceLength: CARDINAL = 1000;
maxBeamHeapLength: CARDINAL = 1000; --2000
maxChordHeapLength: CARDINAL = 1000; --2000
maxSelectionLength: CARDINAL = 100;

--******************************************************************************
--global variables
--******************************************************************************

	-- style: ARRAY [0..20] OF Staves;
--view on score
	-- TF: CARDINAL; ++ "Time Factor" = horizontal scale of score on sheet.
	-- scale,staffLength,top: INTEGER;
--return info for commands
	-- min,max: Time; ++ extent of change
	-- command,flash: BOOLEAN; ++ flash = "error"
--graphics
	-- context: Graphics.Context;
	text,music: Graphics.FontRef;
	-- print: BOOLEAN;
--communicating with the synthesizer
	dataStructureInFlux: BOOLEAN;
	playing,listening: READONLY BOOLEAN;
--data storage
	
-- ****************************************************************************
-- INLINE Procedures
-- ****************************************************************************

 white: CARDINAL = 000000B;
 light: CARDINAL;    
 grey: CARDINAL = 122645B;
 black: CARDINAL = 177777B;

-- Measure: PROC[e: EventType] RETURNS[BOOLEAN] = INLINE
   -- {RETURN[e=measure OR e=staves]};

EndOfScore: PROCEDURE RETURNS[Time] =INLINE
   {IF score.length#0 THEN RETURN[score.event[score.length-1].time+40] ELSE RETURN[10]};

LMod: PROCEDURE[m,n: Time] RETURNS[k: Time] = INLINE
   {k ← m - n*(m/n); IF k>n THEN k ← k+n};

Mod: PROCEDURE[m,n: INTEGER] RETURNS[k: INTEGER] = INLINE
   {k ← m MOD n; IF k<0 THEN k ← k+n};

SetBrush: PROCEDURE[context: Graphics.Context, tex: CARDINAL, pnt: Graphics.PaintMode] = INLINE
   {Graphics.SetStipple[context,tex]; [] ← Graphics.SetPaintMode[context,pnt]};

SetDirty: PROCEDURE[score: ScorePTR, begin,end: Time] = INLINE
   {score.sheet.dirty1 ← MIN[score.sheet.dirty1, begin]; 
    score.sheet.dirty2 ← MAX[score.sheet.dirty2, end]};

-- *************************************************************************
-- mouse and keyboard
-- *************************************************************************

AnyBug: PROCEDURE RETURNS[BOOLEAN] = 
INLINE {RETURN[RedBug[] OR BlueBug[] OR YellowBug[]]};

RedBug: PROCEDURE RETURNS[BOOLEAN] = 
INLINE {RETURN[MouseFace.buttons[Mouse1] = down]};

YellowBug: PROCEDURE RETURNS[BOOLEAN] =
INLINE {RETURN[MouseFace.buttons[Mouse2] = down]};

BlueBug: PROCEDURE RETURNS[BOOLEAN] = 
INLINE {RETURN[MouseFace.buttons[Mouse3] = down]};

Shift: PROCEDURE RETURNS[BOOLEAN] = INLINE {RETURN[
   KeyboardFace.keyboard[57] = down OR -- A5
   KeyboardFace.keyboard[76] = down]}; -- A6

Control: PROCEDURE RETURNS[BOOLEAN] = 
INLINE {RETURN[KeyboardFace.keyboard[52] = down]}; -- L11

END.