--Author: John Maxwell --last modified: January 16, 1982 11:09 AM DIRECTORY CursorDefs, Graphics USING [DisplayChar, DisplayContext, DisplayString, Map, MoveTo, ScreenToUser, SetFont, SetPaint, SetTexture, Vec], Heuristic USING [MakeBeams,MakeChords,MakeSyncs, SetNoteValues], Interface USING [count, DeleteGraphical, Flash, MoveNote, MoveGraphical, Object, Wait], IODefs USING [ReadChar,ReadDecimal,ReadID,Rubout], MusicDefs, Piece USING [AddSync, CleanUpNotes, Delete, Insert, Merge, NearestNote, Replace], Real USING [Fix, FixI], Score USING [Draw, FileIn, FileOut, Look, Print, Redraw, SetKey, SetMetrenome, SetTimeSignature, ShowLogical, StartListening, StartPlaying, StopListening, StopPlaying, Test], Screen USING [CommandProcs, DisplayMessage, screen], Selection, -- USING everything Sheet USING [FindLine, Initialize, LineNumber, Map, NearestTime, Reset, ScreenPoint, Scroll, SetBegin, SetStyle], StreamDefs USING [GetDefaultKey,KeyboardHandle], StringDefs USING [AppendString,UpperCase], Utility, -- USING everything Voice USING [Check, Set]; InterfaceImplA: MONITOR RETURNS[POINTER TO Screen.CommandProcs] IMPORTS Graphics, Heuristic, Interface, IODefs, MusicDefs, Piece, Real, Score, Screen, Selection, Sheet, StreamDefs, StringDefs, Utility, Voice = BEGIN OPEN Graphics, MusicDefs; keyboard:StreamDefs.KeyboardHandle; Error:SIGNAL; test:BOOLEAN_FALSE; commands:Screen.CommandProcs _ [Play, Listen, HandleRed, HandleYellow, HandleBlue, HandleKeyboard, Scroll, Thumb, Score.Draw, Score.FileIn, FileOut, Hardcopy, Initialize, 0]; --****************************************************************** --Initialization --****************************************************************** Initialize:PROCEDURE[dc:Graphics.DisplayContext] = BEGIN keyboard _ StreamDefs.GetDefaultKey[]; Utility.InitStorage[]; Sheet.Initialize[dc]; END; --****************************************************************** --Command parser --****************************************************************** HandleKeyboard:PROCEDURE = BEGIN ClearDirty[]; Do[IODefs.ReadChar[]]; IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out END; HandleBlue:PROCEDURE = BEGIN OPEN Interface; ClearDirty[]; count_FALSE; SELECT TRUE FROM Control[] => {HandleMenu[]; count_FALSE}; Shift[] => {Selection.Clear[]; WHILE BlueBug[] DO Interface.MoveNote[Piece.NearestNote[]]; ENDLOOP}; ENDCASE => Interface.MoveGraphical[defaultObject]; IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out IF count THEN commands.count_commands.count+1; END; HandleYellow:PROCEDURE = -- used to delete objects or unselect notes BEGIN OPEN CursorDefs, Interface; temp:Cursor _ cursor^; cursor^ _ textCursor; ClearDirty[]; count_FALSE; SELECT TRUE FROM scale>3 => ChangeLook['O]; Shift[] AND Control[] => NULL; Control[] => NULL; ENDCASE => Interface.DeleteGraphical[]; cursor^ _ temp; WHILE YellowBug[] DO NULL; ENDLOOP; IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out IF count THEN commands.count_commands.count+1; END; HandleRed:PROCEDURE = -- Only used to change the selection BEGIN OPEN CursorDefs; x,y:INTEGER; i,click:CARDINAL_0; temp:Cursor _ cursor^; cursor^ _ textCursor; ClearDirty[]; WHILE RedBug[] DO [x,y] _ Sheet.ScreenPoint[]; SELECT TRUE FROM Control[] => { FOR i IN [0..5) DO Interface.Wait[1]; IF ~RedBug[] THEN {click_1; EXIT}; ENDLOOP; IF click=1 THEN FOR i IN [0..5) DO Interface.Wait[1]; IF RedBug[] THEN {click_2; EXIT}; ENDLOOP; SELECT click FROM 0 => ChooseLine[Sheet.NearestTime[x,y].time]; 1 => EXIT; 2 => ExtendLine[]; ENDCASE => ERROR}; Shift[] => Selection.AddNote[Piece.NearestNote[x,y]]; ENDCASE => {IF command THEN Selection.Clear[]; Selection.AddNote[Piece.NearestNote[x,y]]}; command_FALSE; ENDLOOP; cursor^ _ temp; IF test AND Score.Test[] THEN Error; --you may proceed, but you cannot file out END; ClearDirty:PROCEDURE = INLINE {min _ 100000; max _ -1}; ExtendLine:PROCEDURE = BEGIN time:Time; grey:BOOLEAN _ FALSE; switch:BOOLEAN _ TRUE; beginning:BOOLEAN _ FALSE; temp1,temp2,gTemp1,gTemp2:Time; temp1 _ select1; temp2 _ select2; gTemp1 _ greySelect1; gTemp2 _ greySelect2; WHILE RedBug[] DO -- is the user switching between colors? IF Shift[] AND NOT grey THEN BEGIN grey _ TRUE; switch _ TRUE; Selection.AddLine[temp1,temp2]; END; IF NOT Shift[] AND grey THEN BEGIN grey _ FALSE; switch _ TRUE; Selection.AddGreyLine[gTemp1,gTemp2]; END; [time,] _ Sheet.NearestTime[]; -- if the user is switching, then which end does he want? IF switch THEN IF grey THEN beginning _ Beginning[time,greySelect1,greySelect2] ELSE beginning _ Beginning[time,select1,select2]; switch _ FALSE; -- draw the appropriate line IF grey THEN IF beginning THEN Selection.AddGreyLine[time,greySelect2] ELSE Selection.AddGreyLine[greySelect1,time] ELSE IF beginning THEN Selection.AddLine[time,select2] ELSE Selection.AddLine[select1,time] ENDLOOP; END; Beginning:PROCEDURE[time,begin,end:Time] RETURNS[BOOLEAN] = INLINE BEGIN IF time<=begin THEN RETURN[TRUE]; IF time>=end THEN RETURN[FALSE]; RETURN[ABS[time-begin] IF lineSelect THEN Heuristic.MakeBeams[select1,select2] ELSE Selection.MakeBeam[]; 'B => Selection.ClearBeam[]; 002C=> Selection.MakeBeamOfBeams[]; 'c => IF lineSelect THEN Heuristic.MakeChords[select1,select2] ELSE Selection.MakeChord[]; 'C => Selection.ClearChord[]; 'd => Selection.Delete[]; 'e => {Selection.AddLine[0,EndOfScore[]]; RETURN}; -- 'f => {ReadFileName[".logical"]; Score.FileInOld[fileName]}; -- 'F => {ReadFileName[".logical"]; Score.FileOutOld[fileName]}; -- 'g => GuessNoteValues[select1,select2]; 'g => Selection.SetGrace[TRUE]; 'G => Selection.SetGrace[FALSE]; 007C => Heuristic.SetNoteValues[select1,select2]; 'k => Score.SetKey[select1,select2,ReadKey[]]; 'l => {ChangeLook[IODefs.ReadChar[]]; RETURN}; 014C => {Score.ShowLogical[0,scoreLength-1]; Sheet.Reset[]}; -- debugging aid 'm => Score.SetMetrenome[select1,select2,MAX[16,ReadNumbers["metrenome: CR",].a]]; 'n => {[a,b] _ ReadNumbers["n-tuplet: CR CR",TRUE]; Selection.MakeNTuplet[b,a]}; 'N => Selection.HideNTuplets[TRUE]; 016C=> {[a,b] _ ReadNumbers["n-tuplet: CR CR",TRUE]; Selection.MakeNTupletOfBeams[b,a]}; 'p => Selection.Transpose[ReadNumbers["transpose: +/- CR",].a]; 'r => IF lineSelect THEN Piece.Replace[select1,select2,greySelect1,greySelect2] ELSE Selection.SetRest[TRUE]; 'R => IF ~lineSelect THEN Selection.SetRest[FALSE] ELSE flash_TRUE; 's => IF lineSelect THEN Heuristic.MakeSyncs[select1,select2] ELSE Selection.MakeSync[]; 'S => Selection.ClearSync[]; 't => Selection.MakeTie[]; 'T => Selection.ClearTie[]; 'x => {Voice.Check[]; RETURN}; 'v => Voice.Set[ReadDigit[]]; ' => {insertMeasure _ TRUE; RETURN}; --values, dotted values, doubly dotted values, triply dotted valuese30(0,3810)(1,4445)(2,5696)(3,6592)\1965i14I181i40I340i41I462i40I895i40I1896i26I1914i65I '0 => Selection.SetNoteValue[unknown,0]; '1 => Selection.SetNoteValue[whole,0]; '! => Selection.SetNoteValue[whole,1]; '2 => Selection.SetNoteValue[half,0]; '@ => Selection.SetNoteValue[half,1]; '4 => Selection.SetNoteValue[quarter,0]; '$ => Selection.SetNoteValue[quarter,1]; '8 => Selection.SetNoteValue[eighth,0]; '* => Selection.SetNoteValue[eighth,1]; '6 => Selection.SetNoteValue[sixteenth,0]; '~ => Selection.SetNoteValue[sixteenth,1]; '3 => Selection.SetNoteValue[thirtysecond,0]; '# => Selection.SetNoteValue[thirtysecond,1]; '7 => Selection.SetNoteValue[sixtyfourth,0]; '& => Selection.SetNoteValue[sixtyfourth,1]; '[ => Selection.SetStaff[ReadDigit[]]; '^ => Selection.SetStem[TRUE]; '_ => Selection.SetStem[FALSE]; '/ => {[a,b] _ ReadNumbers["time signature: CR CR",TRUE]; Score.SetTimeSignature[select1,select2,[a,b]]}; 033C => {Selection.Clear[]; RETURN}; ENDCASE => RETURN; command _ TRUE; IF flash THEN {Interface.Flash[]; RETURN}; commands.count _ commands.count+1; IF lineSelect AND min>max THEN Score.Redraw[select1,select2] ELSE Score.Redraw[min,max]; END; ChangeLook:PROCEDURE[c:CHARACTER] = BEGIN count:BOOLEAN _ FALSE; repaint:BOOLEAN _ TRUE; SELECT c FROM 'a=> Score.Look[accidental,TRUE,]; 'A=> Score.Look[accidental,FALSE,]; 'c=> Score.Look[noCarry,FALSE,]; 'C=> Score.Look[noCarry,TRUE,]; 'g=> flash _ TRUE; 'h=> Score.Look[hardcopy,TRUE,]; 'H=> Score.Look[hardcopy,FALSE,]; 'j=> {Score.Look[justified,,ReadDigit[]]; count_TRUE}; 'l=> {Score.Look[logical,,]; count_TRUE}; 'n=> Score.Look[notehead,TRUE,]; 'N=> Score.Look[notehead,FALSE,]; 'o=> Score.Look[overview,TRUE,]; 'O=> Score.Look[overview,FALSE,]; 'p=> {Score.Look[physical,,ReadDigit[]]; count_TRUE}; 's=> {Score.Look[sync,TRUE,]; repaint _ FALSE}; 'S=> Score.Look[sync,FALSE,]; 'v=> Score.Look[voice,TRUE,ReadDigit[]]; 'V=> {Score.Look[voice,FALSE,]; repaint _ FALSE}; 177C=> IODefs.Rubout; ENDCASE => {Sheet.SetStyle[Digit[c],select1,select2]; count_TRUE}; command _ TRUE; IF flash THEN {Interface.Flash[]; RETURN}; Score.Draw[repaint]; IF count THEN commands.count _ commands.count+1; END; ReadKey:PROCEDURE RETURNS[k:INTEGER] = BEGIN key,c:CHARACTER; accidental:Accidental_inKey; CR:CHARACTER=015C; Major:BOOLEAN _ TRUE; negative:BOOLEAN _ FALSE; -- key may be expressed as [-7..7] Screen.DisplayMessage["Please enter key name (A, Bb, C#m...) or number."]; key _ IODefs.ReadChar[]; IF key='- THEN { key _ IODefs.ReadChar[]; Screen.DisplayMessage[NIL]; RETURN[-Digit[key]]}; IF key IN ['0..'9] THEN { Screen.DisplayMessage[NIL]; RETURN[Digit[key]]}; -- key must have been named (Am, C#, DM, e, etc.) key _ StringDefs.UpperCase[key]; IF key=177C THEN IODefs.Rubout; WHILE (c_IODefs.ReadChar[])#CR DO SELECT c FROM 'm => Major _ FALSE; 'b => accidental _ flat; '# => accidental _ sharp; 177C=> IODefs.Rubout; ENDCASE; ENDLOOP; Screen.DisplayMessage[NIL]; SELECT key FROM 'C => k _ 0; 'D => k _ 2; 'E => k _ 4; 'F => k_ -1; 'G => k _ 1; 'A => k _ 3; 'B => k _ 5; ENDCASE => {Interface.Flash[]; RETURN[-100]}; IF accidental=flat THEN k _ k- 7; IF accidental=sharp THEN k_ k+ 7; IF NOT Major THEN k _ k- 3; RETURN[k]; END; ReadFileName:PROCEDURE[s:STRING] = BEGIN IODefs.ReadID[fileName !IODefs.Rubout=> RETRY]; StringDefs.AppendString[fileName,s]; RETURN; END; fileName:STRING _ [50]; ReadNumbers:PROCEDURE[s:STRING,two:BOOLEAN_FALSE] RETURNS[a,b:INTEGER] = BEGIN a _ b _ 0; Screen.DisplayMessage[s]; a _ IODefs.ReadDecimal[]; Screen.DisplayMessage[NIL]; IF NOT two THEN RETURN; Screen.DisplayMessage["and the second number..."]; b _ IODefs.ReadDecimal[]; Screen.DisplayMessage[NIL]; END; ReadDigit:PROCEDURE RETURNS[d:CARDINAL] = BEGIN Screen.DisplayMessage["Please enter a digit"]; d _ Digit[IODefs.ReadChar[]]; Screen.DisplayMessage[NIL]; END; Digit:PROCEDURE[c:CHARACTER] RETURNS[CARDINAL] = BEGIN SELECT c FROM '0 => RETURN[0]; '1 => RETURN[1]; '2 => RETURN[2]; '3 => RETURN[3]; '4 => RETURN[4]; '5 => RETURN[5]; '6 => RETURN[6]; '7 => RETURN[7]; '8 => RETURN[8]; '9 => RETURN[9]; --shifted number keys ') => RETURN[10]; '! => RETURN[11]; '@ => RETURN[12]; '# => RETURN[13]; '$ => RETURN[14]; '% => RETURN[15]; '~ => RETURN[16]; '& => RETURN[17]; '* => RETURN[18]; '( => RETURN[19]; 033C => RETURN[TF]; -- default for justify ENDCASE => IODefs.Rubout; RETURN[0]; END; --****************************************************************** --The menu: change attributes, insertion --****************************************************************** defaultObject:Interface.Object_none; HandleMenu:PROCEDURE = BEGIN OPEN CursorDefs, Interface; x,y,i,j,newI,newJ:CARDINAL_0; x _ CursorX^; y _ CursorY^; DisplayMenu[x+4,y+4]; CursorGetsMenu[0,0]; WHILE BlueBug[] DO newI _ (CursorX^-x)/16; newJ _ (CursorY^-y+8)/16; IF i=newI AND j=newJ THEN LOOP; i _ newI; j _ newJ; CursorGetsMenu[i,j]; ENDLOOP; DisplayMenu[x+4,y+4]; SELECT j FROM 0 => SELECT i FROM 0 => defaultObject _ note; 1 => defaultObject _ rest; 2 => defaultObject _ staves; 3 => defaultObject _ measure; 4 => defaultObject _ doubleMeasure; 5 => defaultObject _ repeat1; 6 => defaultObject _ repeat2; 7 => defaultObject _ endMeasure; 8 => defaultObject _ treble; 9 => defaultObject _ bass; 10=> defaultObject _ octava; ENDCASE => defaultObject _ none; 1 => SELECT i FROM 0 => defaultObject _ doubleFlat; 1 => defaultObject _ flat; 2 => defaultObject _ natural; 3 => defaultObject _ inKey; 4 => defaultObject _ sharp; 5 => defaultObject _ doubleSharp; 6 => defaultObject _ trill; 7 => defaultObject _ mordent1; 8 => defaultObject _ mordent2; ENDCASE => defaultObject _ none; ENDCASE => defaultObject _ none; END; CursorGetsMenu:PROCEDURE[i,j:CARDINAL] = BEGIN OPEN CursorDefs; SELECT j FROM 0 => SELECT i FROM 0 => cursor^ _ quarter; 1 => cursor^ _ rest; 2 => cursor^ _ measure; 3 => cursor^ _ measure; 4 => cursor^ _ doubleMeasure; 5 => cursor^ _ repeat1; 6 => cursor^ _ repeat2; 7 => cursor^ _ endMeasure; 8 => cursor^ _ trebleClef; 9 => cursor^ _ bassClef; 10=> cursor^ _ octava; ENDCASE => cursor^ _ textCursor; 1 => SELECT i FROM 0 => cursor^ _ doubleFlat; 1 => cursor^ _ flat; 2 => cursor^ _ natural; 3 => cursor^ _ inKey; 4 => cursor^ _ sharp; 5 => cursor^ _ doubleSharp; 6 => cursor^ _ trill; 7 => cursor^ _ mordent1; 8 => cursor^ _ mordent2; ENDCASE => cursor^ _ textCursor; ENDCASE => cursor^ _ textCursor; END; DisplayMenu:PROCEDURE[x,y:INTEGER] = BEGIN OPEN Screen; sp,p:Graphics.Vec _ [x,y]; p_ Graphics.ScreenToUser[screen,sp]; Graphics.MoveTo[screen,p]; Graphics.SetPaint[screen,invert]; Graphics.SetTexture[screen,black]; Graphics.SetFont[screen,music,8]; Graphics.DisplayString[screen,"tzffghopRS"]; --note,rest,measures,clefs Utility.SetFont[screen,text,12]; Graphics.DisplayChar[screen,'8]; Utility.SetFont[screen,music,8]; p.y _ p.y-16; Graphics.MoveTo[screen,p]; Graphics.DisplayString[screen,"EFG.MOUVW"]; --accidentals,embellishments Graphics.SetFont[screen,text,12]; END; --****************************************************************** --Utility procedures --****************************************************************** Hardcopy:PROCEDURE[s:STRING]={Score.Print[splines:~hardcopy OR BlueBug[]]}; FileOut:PROCEDURE[s:STRING]= BEGIN IF NOT Score.FileOut[s] THEN { Screen.DisplayMessage["FileOut aborted-- see mesa.typescript"]; Interface.Flash[]}; END; Play:PROCEDURE = BEGIN start:CARDINAL; IF playing THEN {Score.StopPlaying[]; RETURN}; -- ELSE TURN THE PLAYER ON start _ scoreLength; insertMeasure _ FALSE; -- find the indices of the selection IF select10 THEN Sheet.Scroll[MAX[Lines[begin,time],1]] ELSE Sheet.Scroll[-MAX[Lines[begin,time],1]]; Score.Draw[]; END; Thumb:PROCEDURE = BEGIN height:INTEGER_680; endOfScore:Time; sp,p:Graphics.Vec; oldBegin:Time=begin; endOfScore_EndOfScore[]; SELECT scale FROM 1 => height _ height; 2 => height _ (3*height)/2; 4 => height _ 4*height; ENDCASE => ERROR; WHILE YellowBug[] DO [p.x,p.y] _ Sheet.ScreenPoint[]; IF p.x>-10 THEN RETURN; p.y _ -(height*begin)/endOfScore; sp _ Graphics.Map[context,Screen.screen,p]; Interface.Wait[1]; CursorY^ _ MAX[808 - Real.FixI[sp.y],0]; ENDLOOP; [p.x,p.y] _ Sheet.ScreenPoint[]; begin _ (Real.Fix[top-p.y]*endOfScore)/height; begin _ MIN[begin,endOfScore-60]; begin _ MAX[begin,0]; Sheet.SetBegin[begin]; IF begin#oldBegin THEN Score.Draw[]; END; Lines:PROCEDURE[time1,time2:Time] RETURNS[CARDINAL] = INLINE BEGIN line,topLine:INTEGER; line _ Sheet.LineNumber[Sheet.FindLine[time2]]; topLine _ Sheet.LineNumber[Sheet.FindLine[time1]]; RETURN[line-topLine]; END; RETURN[@commands]; END. Thumb:PROCEDURE = BEGIN time:Time; x,y:INTEGER; lines:INTEGER; [x,y] _ Sheet.ScreenPoint[]; time _ Sheet.NearestTime[x,y].time; lines _ Lines[begin,time]; SELECT TRUE FROM lines=0 AND x<0 => {Sheet.SetBegin[0]}; scale=1 => {Sheet.Scale[4]; Sheet.SetBegin[0]}; scale=2 => {Sheet.Scale[4]; Sheet.SetBegin[0]}; scale=4 => {Sheet.Scale[IF hardcopy THEN 2 ELSE 1]; Sheet.SetBegin[time]}; ENDCASE; Score.Draw[]; END; e6\4160i19I357i38I2304i25I65i1I121i27I111i18I