-- MusicDefs.mesa
-- Author: John Maxwell
-- Last Edited by: Maxwell, November 22, 1983 12:46 pm
-- This module gives the types used internally by the editor.
-- MusicFileDefs gives the types used externally in the file format.

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

MusicDefs: DEFINITIONS IMPORTS Graphics, KeyboardFace, MouseFace =
BEGIN

Error: SIGNAL[s: STRING];
Notify: SIGNAL[s: STRING];

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

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)
   style: StylePTR ← NIL, -- 
   maxVoice: CARDINAL ← 0, -- index of the highest numbered voice
	command: BOOLEAN ← FALSE, -- we have a new command
	flash: BOOLEAN ← FALSE, -- flash the screen for an error
   length: CARDINAL ← 0, -- length of score
   event: SEQUENCE max: CARDINAL OF EventPTR]; -- collection of events
-- endOfScore: CARDINAL = LAST[CARDINAL];
       
ScorePortionPTR: TYPE = LONG POINTER TO ScorePortionRec;
ScorePortionRec: TYPE = RECORD[ -- a portion of a score.
	score: ScorePTR ← NIL,
	toc: Time ← 0, -- length of portion in seconds of playing time.
	duration: Time ← 0, -- length in portion in pixels on the screen.
	-- duration = 0 means score is from synthesizer (ignore ss1 and ss2).
	ss1, ss2: ScoreState ← []]; -- state of score at beginning and end
ScoreStatePTR: TYPE = LONG POINTER TO ScoreState;
ScoreState: TYPE = RECORD[
	key: INTEGER ← 0, 
	style: CARDINAL ← 1, 
	staff: ARRAY [0..4) OF Staff ← ALL[[]]]; 

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 ← 0, -- actual length of the sequence
	      note: SEQUENCE max: CARDINAL OF NotePTR], -- collection of notes
	   measure => [measure: MeasureType ← measure, eol: BOOLEAN ← FALSE], -- eol = force to end of line  
	   timeSignature => [ts: TimeSignature ← [4,4]],
	   keySignature => [key: INTEGER ← 0],
	   metrenome => [metrenome: INTEGER ← 128],
	   staves => [ -- marks a change in staffing
	      staves: StavesType ← style,  
	      height: INTEGER ← 0, 
	      offset: INTEGER ← 0,
	      value: CARDINAL ← 0, -- index of the staff OR style number
	      length: [0..4] ← 4, -- number of staffs in following array
	      staff: ARRAY [0..4) OF Staff ← ALL[[]]],
	   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 = MusicFileDefs.NoteValueFormat;
Accidental: TYPE = MusicFileDefs.AccidentalFormat;
Embellishment: TYPE = MusicFileDefs.EmbellishmentFormat;

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 ← TRUE, -- does the beam show, or do you use brackets instead?
	ntuple, against: [0..128) ← 1, -- 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 ← 0, -- height of beam from top of left-most note
	invisible: BOOLEAN ← FALSE,
	length: [0..1024) ← 0, -- length of chord sequence
	staff: [0..32) ← 0, -- 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; -- saves a call to CodeBlockEqualLong

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

BeamHeapPTR: TYPE = LONG POINTER TO BeamHeapRec;
BeamHeapRec: TYPE = RECORD[
	length: CARDINAL ← 0,
	beam: SEQUENCE max: CARDINAL OF BeamPTR];
ChordHeapPTR: TYPE = LONG POINTER TO ChordHeapRec;
ChordHeapRec: TYPE = 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
	beamQueueLength: CARDINAL ← 0,
	beamQueue: ARRAY [0..3) OF BeamPTR ← ALL[NIL], -- drawn beams
	key1, key2: LONG POINTER TO EventRec.keySignature ← NIL, -- bracketing keys 
	ts1, ts2: LONG POINTER TO EventRec.timeSignature ← NIL, -- bracketing time signatures
	met1, met2: LONG POINTER TO EventRec.metrenome ← NIL];-- bracketing metrenomes

SheetPTR: TYPE = LONG POINTER TO SheetRec;
SheetRec: TYPE = RECORD[
	context: Graphics.Context ← NIL,
	voice: CARDINAL ← noVoice, -- currently selected voice
	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
	scale: INTEGER ← 1, -- normal, hardcopy and overview
	density: INTEGER ← 256, -- scale factor in physical mode.
	justification: INTEGER ← 1, -- density of justification.  used 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
	width: INTEGER ← 550, -- width of staff
	current: CARDINAL ← 0, -- current section index
	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];
noVoice: CARDINAL = 1000;
	
StylePTR: TYPE = LONG POINTER TO StyleRec;
StyleRec: TYPE = RECORD[
	length: CARDINAL ← 0,
	style: SEQUENCE max: CARDINAL OF StavesPTR];
Staff: TYPE = MusicFileDefs.Staff;

SelectionPTR: TYPE = LONG POINTER TO SelectionRec;
SelectionRec: TYPE = RECORD[
	lineSelect: BOOLEAN ← FALSE, -- line selection or note selection?
	score: ScorePTR ← NIL, -- score of primary selection or note selection
	select1, select2: Time ← 0, -- line selected
	score2: ScorePTR ← NIL, -- score of secondary selection
	greySelect1, greySelect2: Time ← 0, -- secondary line selection
	length: CARDINAL ← 0, -- length of note selection
	note: SEQUENCE max: CARDINAL OF NotePTR]; -- notes selected	

DisplayMode: TYPE = MusicFileDefs.DisplayModeFormat;

ObjectType: TYPE = {measure, note, leftBeam, rightBeam, tie};
LookCommand: TYPE = {accidental, graphical, hardcopy, justified, logical, noCarry, notehead, overview, physical, style, 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
	zone: UNCOUNTED ZONE;
	
-- ****************************************************************************
-- INLINE Procedures
-- ****************************************************************************

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

Measure: PROC[event: EventPTR] RETURNS[BOOLEAN] = INLINE
    {IF event.type = measure THEN RETURN[TRUE]; 
     IF event.type = staves THEN WITH ev: event SELECT FROM 
     	staves => RETURN[ev.staves = style]; 
     	ENDCASE; 
     RETURN[FALSE]};

EndOfScore: PROCEDURE[score: ScorePTR] 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[score: ScorePTR, tex: CARDINAL, pnt: Graphics.PaintMode] = INLINE
   {Graphics.SetStipple[score.sheet.context,tex]; 
    [] ← Graphics.SetPaintMode[score.sheet.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.