Home » Personal collection » Acorn ADFS disks » Greaseweazled » adfs_RiscOs_Apps_2.adf » !Maestro/!RunImage
!Maestro/!RunImage
This website contains an archive of files for the Acorn Electron, BBC Micro, Acorn Archimedes, Commodore 16 and Commodore 64 computers, which Dominic Ford has rescued from his private collection of floppy disks and cassettes.
Some of these files were originally commercial releases in the 1980s and 1990s, but they are now widely available online. I assume that copyright over them is no longer being asserted. If you own the copyright and would like files to be removed, please contact me.
| Tape/disk: | Home » Personal collection » Acorn ADFS disks » Greaseweazled » adfs_RiscOs_Apps_2.adf |
| Filename: | !Maestro/!RunImage |
| Read OK: | ✔ |
| File size: | 173C0 bytes |
| Load address: | FFFFFB41 |
| Exec address: | 666169DF |
Duplicates
There are 8 duplicate copies of this file in the archive:
- Personal collection » Acorn hard disk » apps » !Maestro » !RunImage
- Personal collection » Acorn ADFS disks » Zipped » zipped_disks » 2000_apps_1 » apps1/Apps/!Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Archimedes » Dominic_1.ADF » !Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Archimedes » Dominic_1B.ADF » !Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Archimedes » RiscOs_Apps_2.ADF » !Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Greaseweazled » adfs_1.6M_Apps1.adf » Apps/!Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Greaseweazled » adfs_Dominic_1.adf » !Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Greaseweazled » adfs_Dominic_1B.adf » !Maestro/!RunImage
- Personal collection » Acorn ADFS disks » Greaseweazled » adfs_RiscOs_Apps_2.adf » !Maestro/!RunImage
File contents
10REM > Music.Maestxt
20REM (c) Acorn Computers 1988
30
40VersionStr$="1.65 (4-Jan-89)"
50
60Task_h%=0
70MusicFileType%=&AF1
80INITIALISED%=FALSE
90DIM ERRBLOCK% 200
100REM space for an error window
110SPACE%=HIMEM-END :REM check enough space available
120IF SPACE%<100000 STOP
130REMdebug=OPENOUT "debugfile"
140ON ERROR PROCerror:END
150REM get name of file to load
160SYS "OS_GetEnv" TO EnvStr$
170IF INSTR(EnvStr$," -quit ") THEN
180 I%=INSTR(EnvStr$,"""")
190 I%=INSTR(EnvStr$,"""",I%+1)
200 REPEATI%+=1:UNTILMID$(EnvStr$,I%,1)<>" "
210 f$=MID$(EnvStr$,I%)
220 ENDIF
230INITIALISED%=FNinitialise
240mask%=0 : REM mask out unwanted reason codes
250ON ERROR PROCerror
260REPEAT
270 IF PLAYING% PROCCheckQ
280 SYS Poll%,mask%,Window%+handle% TO R%
290 IF PLAYING% PROCCheckQ: REM recheck since poll might have stayed away for a while
300 CASE R% OF
310 WHEN 0
320 IF AwaitingAck% IFFNCheckOK("Bad Data Transfer, Receiver Dead",3) :AwaitingAck%=FALSE : CHANGED%=wasCHANGED%
330 IF SCORING% PROCsymbol_pointer
340 IF f$<>"" PROCload_music(f$) : f$=""
350 IF SCROLLING% PROCCheckScroll :REM auto-scrolling
360 WHEN 1:PROCredraw_window_request
370 WHEN 2:PROCopen_window_request
380 WHEN 3:PROCclose_window_request
390 WHEN 4 :REM pointer leaving window
400 IF Window%!handle%=ScoreWind_h% THEN
410 PROCrelease
420 wasSCORING%=SCORING%
430 SCORING%=FALSE
440 ELSE
450 IF Window%!handle%=AbortQuery_h% PROCCloseWindow(AbortQuery_h%)
460 ENDIF
470 WHEN 5: IF Window%!handle%=ScoreWind_h% SCORING% = (wasSCORING% AND NOT stopSCORING%)=TRUE
480 WHEN 6:PROCmouse_button_click
490 WHEN 7:PROCUserDragBox
500 WHEN 8:PROCKeyPressed
510 WHEN 9:PROCMenuSelect
520 WHEN 10: PROCScrollReq(Window%!(handle%+32), Window%!(handle%+36))
530 WHEN 17,18:PROCreceive
540 ENDCASE
550 UNTIL FALSE
560END
570
580DEF PROCCheckQ
590B1%=B2%: B2%=BEAT
600IFB2%<B1% PROCplay_bar
610ENDPROC
620
630DEF PROCreceive
640LOCAL task%, ref%, block,F$
650block=Window% :REM temporary buffer
660ref%=!(Window%+handle%+8)
670task%=!(Window%+handle%+4)
680IF task%=Task_h% ENDPROC : REM ignore messages from this task
690CASE Window%!(handle%+16) OF
700 WHEN 0
710 IF NOT CHANGED% THEN
720 PROCterminate
730 ELSE
740 !block=20
750 block!12=ref%
760 block!16=0 :REM quit block
770 SYS SendMessage,19,block :REM acknowledge, and refuse quit request
780 IF FNCheckOK("Unsaved music. Do you really want to quit?",3) THEN
790 PROCterminate
800 ELSE
810 SYS GetPointerInfo%,,Mouse%
820 PROCOpenWindow(Save_h%, Mouse%!x0%, Mouse%!y0%)
830 ENDIF
840 ENDIF
850 WHEN 2 : REM save file
860 IF SAVING% THEN
870 wasCHANGED%=CHANGED%
880 PROCsave_music(FNGetStr(Window%+handle%+44))
890 SAVING%=FALSE
900 SYS GetPointerInfo%, ,block
910 block!20=block!12
920 block!24=block!16
930 block!28=block!0
940 block!32=block!4
950 block!0=44
960 block!12=ref%
970 block!16=3 :REM DataLoad application to filer
980 block!40=MusicFileType%
990 SYS SendMessage,17,block
1000 AwaitingAck%=TRUE
1010 PROCCloseMenu
1020 ENDIF
1030 WHEN 3 : REM load file
1040 PROCload_music(FNGetStr(Window%+handle%+44))
1050 PROCDataLoadAck(ref%)
1060 WHEN 4 :AwaitingAck%=FALSE : REM DataLoadAck. End of DataSave protocol
1070 WHEN 5 : REM open double-clicked file
1080 IF !(Window%+handle%+40) = MusicFileType% THEN
1090 PROCload_music(FNGetStr(Window%+handle%+44))
1100 PROCDataLoadAck(ref%)
1110 ENDIF
1120 WHEN &502:PROCHelp(ref%)
1130 WHEN &400C1:PROCgetmodeinfo(FALSE)
1140 ENDCASE
1150ENDPROC
1160
1170DEF PROCHelp(ref%)
1180LOCAL block%,text$
1190block%=Window%+handle%
1200block%!12 = ref%
1210block%!16 = &503 :REM Send help message
1220text$=""
1230CASE block%!32 OF
1240 WHEN -2 : text$="This is the Maestro icon.|MClick SELECT to open score."
1250 WHEN ScoreWind_h%
1260 IFSCORING%ANDSCRIBE%(drawn%) THEN
1270 text$="Click SELECT to place item in score"
1280 ELSE
1290 text$="Select an item in a pane to place on the score"
1300 ENDIF
1310 WHEN NotesPane_h%
1320 text$="Click SELECT to select a "
1330 CASE block%!36 OF
1340 WHEN 0 : text$=text$+"breve."
1350 WHEN 1 : text$=text$+"semibreve."
1360 WHEN 2 : text$=text$+"minim."
1370 WHEN 3 : text$=text$+"crochet."
1380 WHEN 4 : text$=text$+"quaver."
1390 WHEN 5 : text$=text$+"semiquaver."
1400 WHEN 6 : text$=text$+"demisemiquaver."
1410 WHEN 7 : text$=text$+"hemidemisemiquaver."
1420 OTHERWISE text$=""
1430 ENDCASE
1440 WHEN SharpsPane_h%
1450 text$="Click SELECT to select a "
1460 CASE block%!36 OF
1470 WHEN 0 : text$=text$+"natural."
1480 WHEN 1 : text$=text$+"sharp."
1490 WHEN 2 : text$=text$+"flat."
1500 WHEN 3 : text$=text$+"double-sharp."
1510 WHEN 4 : text$=text$+"double-flat."
1520 WHEN 5 : text$=text$+"naturalised sharp."
1530 WHEN 6 : text$=text$+"naturalised flat."
1540 WHEN 7 : text$=text$+"dot."
1550 WHEN 8 : text$=text$+"double-dot."
1560 WHEN 9 : text$=text$+"triple-dot."
1570 WHEN 10 : text$=text$+"tie."
1580 WHEN 11 : text$=text$+"bar line."
1590 WHEN 12 : text$=text$+"treble clef."
1600 WHEN 13 : text$=text$+"bass clef."
1610 WHEN 14 : text$=text$+"key signature."
1620 WHEN 15 : text$=text$+"time signature."
1630 OTHERWISE text$=""
1640 ENDCASE
1650 WHEN RestsPane_h%
1660 text$="Click SELECT to select a "
1670 CASE block%!36 OF
1680 WHEN 0 : text$=text$+"breve rest."
1690 WHEN 1 : text$=text$+"semibreve rest."
1700 WHEN 2 : text$=text$+"minim rest."
1710 WHEN 3 : text$=text$+"crochet rest."
1720 WHEN 4 : text$=text$+"quaver rest."
1730 WHEN 5 : text$=text$+"semiquaver rest."
1740 WHEN 6 : text$=text$+"demisemiquaver rest."
1750 WHEN 7 : text$=text$+"hemidemisemiquaver rest."
1760 OTHERWISE text$=""
1770 ENDCASE
1780 WHEN InstrWind_h%
1790 IF (block%!36>7) THEN
1800 IF (block%!36<16) THEN
1810 text$="Click SELECT or ADJUST to select an instrument for this stave"
1820 ELSE
1830 IF (block%!36<24) THEN
1840 text$="Click SELECT or ADJUST to set the volume of this channel"
1850 ELSE IF (block%!36<32) text$="Click SELECT or ADJUST to set the stereo position of this channel"
1860 ENDIF
1870 ENDIF
1880 ENDIF
1890 WHEN Save_h%
1900 IF block%!36=2 text$="Drag this icon into a directory window to save it in that directory" ELSE text$="Fill in the file name and then drag the icon into a filer directory window to save it"
1910 WHEN TimeSig_h%
1920 text$="use SELECT and ADJUST on each field to set a time signature"
1930 WHEN AbortQuery_h%
1940 text$="click YES to terminate Maestro"
1950 ENDCASE
1960$(block%+20)=text$
1970block%!0 = (((20+LEN(text$)+1)DIV4)*4)+4
1980$(block%+21+LEN(text$)) = CHR$(0) :REM null-terminate
1990SYS SendMessage,17,block% :REM acknowledge message
2000ENDPROC
2010
2020DEF PROCDataLoadAck(ref%)
2030LOCAL block
2040block=Window%
2050block!0 = 20
2060block!12 = ref%
2070block!16 = 4 :REM DataLoadAck
2080SYS SendMessage,17,block :REM acknowledge message
2090ENDPROC
2100
2110DEF PROCredraw_window_request
2120LOCAL R%
2130SYS RedrawWindow%,,Window%+handle% TO R%
2140WHILE R%
2150 IF Window%!handle% = ScoreWind_h% PROCdraw_staves:IF PLAYING% PROCCheckQ
2160REM try to avoid interruptions in the music
2170 SYS GetRectangle%,,Window%+handle% TO R%
2180 ENDWHILE
2190ENDPROC
2200
2210DEF PROCplace_top_panes(behind)
2220LOCAL xsize%
2230xsize%=Window%!x1%-Window%!x0%
2240RestPBlk%!x1%=Window%!x1%
2250RestPBlk%!x0%=Window%!x0%+xsize%/2
2260RestPBlk%!y1%=Window%!y1%
2270RestPBlk%!y0%=RestPBlk%!y1%-PaneHeight%
2280RestPBlk%!under%=behind
2290SYS OpenWindow%, RestsPane_h%, RestPBlk%+handle%
2300NotePBlk%!x0%=Window%!x0%
2310NotePBlk%!x1%=RestPBlk%!x0%
2320NotePBlk%!y1%=Window%!y1%
2330NotePBlk%!y0%=NotePBlk%!y1%-PaneHeight%
2340NotePBlk%!under%=RestsPane_h%
2350SYS OpenWindow%, NotesPane_h%, NotePBlk%+handle%
2360ENDPROC
2370
2380DEF PROCplace_bottom_panes(behind)
2390LOCAL xsize%
2400xsize%=Window%!x0%+Window%!x1%
2410SharpPBlk%!x0%=Window%!x0%
2420SharpPBlk%!x1%=SharpPBlk%!x0%+xsize%
2430SharpPBlk%!x1%=Window%!x1%
2440SharpPBlk%!y0%=Window%!y0%
2450SharpPBlk%!y1%=SharpPBlk%!y0%+PaneHeight%
2460SharpPBlk%!under%=behind
2470SYS OpenWindow%, SharpsPane_h%, SharpPBlk%+handle%
2480ENDPROC
2490
2500DEF PROCremove_panes
2510PROCCloseWindow(NotesPane_h%)
2520PROCCloseWindow(SharpsPane_h%)
2530PROCCloseWindow(RestsPane_h%)
2540ENDPROC
2550
2560DEF PROCopen_window_request
2570LOCAL bhandle%
2580bhandle%=Window%!under%
2590PROCrelease
2600IF Window%!handle%=ScoreWind_h% THEN
2610 IF TRANSCRIBE% THEN
2620REM define window stack as top->bottom sharpspane->restspane->notespane->score
2630REM open top or bottom panes before score depending on direction of drag
2640 SYS GetWindowState%,,SharpPBlk%+handle%
2650 IF SharpPBlk%!under%=bhandle% Window%!under%=NotesPane_h%
2660 SYS GetWindowState%,,ScoreWBlk%+handle%
2670 IF Window%!y1%>ScoreWBlk%!y1% THEN
2680 PROCplace_top_panes(bhandle%)
2690 ELSE
2700 PROCplace_bottom_panes(bhandle%)
2710 ENDIF
2720 ENDIF
2730 IF PLAYING% AND Window%!scx%<>ScoreWBlk%!scx% ENDPROC:REM no user-scrolling while playing
2740 SYS OpenWindow%,,Window%+handle%
2750 ScoreClosed%=FALSE
2760 IF TRANSCRIBE% THEN
2770 IF bhandle%=-2 THEN
2780 REM find behind handle% if pushed to the back
2790 SYS GetWindowState%,,ScoreWBlk%+handle%
2800 bhandle% = ScoreWBlk%!under%
2810 ENDIF
2820 REM re-open all panes
2830 PROCplace_bottom_panes(bhandle%)
2840 PROCplace_top_panes(SharpsPane_h%)
2850 IF bhandle%=ScoreWind_h% SYS OpenWindow%,,Window%+handle% :REM ensure window is behind panes in unusual case
2860 ENDIF
2870 LHBAR%=FNFindBar(ScoreWBlk%!scx%) :REM the bar number at the left of the window
2880 ELSE
2890 SYS OpenWindow%,,Window%+handle%
2900 ENDIF
2910ENDPROC
2920
2930DEF PROCclose_window_request
2940SYS CloseWindow%,,Window%+handle%
2950IF Window%!handle%=ScoreWind_h% PROCremove_panes : ScoreClosed%=TRUE
2960ENDPROC
2970
2980DEF PROCmouse_button_click
2990LOCALB%,C%,W%,I%,inc%
3000W%=Mouse%!window:I%=Mouse%!icon
3010B%=%111ANDMouse%!buttons
3020Mouse_X%=Mouse%!x0%:Mouse_Y%=Mouse%!y0%
3030IF B%=%010 THEN
3040 PROCCheckInstalledVoices
3050 PROCStopScoring
3060 IF W%=-2 PROCOpenMenu(IconMenu%) ELSE PROCOpenMainMenu
3070ELSE
3080IF B%=%100 inc%=-1 ELSE IF B%=%001 inc%=1 ELSE inc%=0
3090CASE W% OF
3100WHEN -2 : IF ScoreClosed% OR NOT PLAYING% PROCsetup_staves:ENDPROC :REM iconbar
3110WHEN AbortQuery_h%
3120 IF I%=2 THEN
3130 PROCterminate
3140 ELSE
3150 PROCCloseMenu
3160 PROCCloseWindow(AbortQuery_h%)
3170 ENDIF
3180WHEN NotesPane_h%
3190 IF I%>=0 THEN
3200 PROCrelease
3210 wasSCORING%=TRUE
3220 stopSCORING%=FALSE
3230 PROCUpdateIcon(SelW%, SelI%, 0, 1<<2)
3240 PROCUpdateIcon(W%, I%, 1<<2, 0)
3250 SelW%=W%
3260 SelI%=I%
3270 PROCattach(note%+I%,%111000)
3280 ENDIF
3290WHEN RestsPane_h%
3300 IF I%>=0 THEN
3310 PROCrelease
3320 wasSCORING%=TRUE
3330 stopSCORING%=FALSE
3340 PROCUpdateIcon(SelW%, SelI%, 0, 1<<2)
3350 PROCUpdateIcon(W%, I%, 1<<2, 0)
3360 SelW%=W%
3370 SelI%=I%
3380 PROCattach(rest%+I%,%11000)
3390 ENDIF
3400WHEN SharpsPane_h%
3410 IF I%>=0 THEN
3420 PROCrelease
3430 wasSCORING%=TRUE
3440 stopSCORING%=FALSE
3450 PROCUpdateIcon(SelW%, SelI%, 0, 1<<2)
3460 PROCUpdateIcon(W%, I%, 1<<2, 0)
3470 SelW%=W%
3480 SelI%=I%
3490 IF I%<7 PROCattach(accidental%+I%+1,%1100000) ELSEIF I%<11 PROCattach(dot%+I%-6,%1101000)
3500 IF I%=10 PROCattach(tie%,%1101000)
3510 IF I%=11 PROCattach(bar%,%10010000)
3520 IF I%=12 PROCattach(clef%,%100) :REM Treble clef
3530 IF I%=13 PROCattach(clef%+3,%100):REM Bass clef
3540 IF I%=14 PROCattach(key%,%10) :REM key signature
3550 IF I%=15 PROCattach(time%,%1) :REM time signature
3560 ENDIF
3570WHEN ScoreWind_h%
3580 IF B%=%100 IFSCORING%ANDSCRIBE%(drawn%) PROCput_down
3590WHEN InstrWind_h%
3600 LOCAL chan%, v%
3610 PROCCheckInstalledVoices
3620 PROCStopScoring
3630 IF I%>=8 THEN
3640 chan%=I%-8
3650 IF chan%<8 THEN
3660 v%=FNAttachVoice(chan%, inc%)
3670 SYS Sound_AttachVoice, chan%+1, v%
3680 $(VoiceStr%(chan%))=LEFT$(Voice$(v%), VoiceSize%)
3690 Instrument%(chan%,1) = v%
3700 PROCUpdateIcon(W%, I%, 0,0)
3710 ELSE
3720 chan%-=8
3730 IF chan%<8 THEN
3740 v%=Volumes%(chan%)
3750 v%+=inc% : IF v%>NVolumes% v%=NVolumes% ELSEIF v%<0 v%=0
3760 Volumes%(chan%)=v%
3770 $(VolumeStr%(chan%))=LEFT$(Volume$(Volumes%(chan%)), VolSize%)
3780 PROCUpdateIcon(W%, I%, 0,0)
3790 ELSE
3800 chan%-=8
3810 IF chan%<8 THEN
3820 v%=Stereo_Position%(chan%)
3830 v%+=inc% : IF v%>NStereos% v%=NStereos% ELSEIF v%<0 v%=0: REM no wrap
3840 Stereo_Position%(chan%)=v%
3850 SYS Sound_Stereo,chan%+1,Stereo%(Stereo_Position%(chan%))
3860 $(StereoStr%(chan%)) = LEFT$(Stereo$(Stereo_Position%(chan%), 0), SterSize%)
3870 PROCUpdateIcon(W%, I%, 0,0)
3880 ELSE
3890 IF MIDIpresent% THEN
3900 chan%-=8
3910 IF chan%<8 THEN
3920 v%=MIDIChannel%(chan%)
3930 v%+=inc% : IF v%>NMIDIChannels% v%=1 ELSEIF v%<1 v%=NMIDIChannels%
3940 MIDIChannel%(chan%)=v%
3950 $(MIDIChStr%(chan%)) = STR$(MIDIChannel%(chan%))
3960 PROCUpdateIcon(W%, I%, 0,0)
3970 ENDIF
3980 ENDIF
3990 ENDIF
4000 ENDIF
4010 ENDIF
4020 ENDIF
4030WHEN Save_h%
4040 PROCStopScoring
4050 IF I%=0 PROCsave_music(FNGetStr(SaveText)) : REM 0 is OK.
4060 IF I%=2 THEN
4070 LOCAL x%, y% : REM dragging icon
4080 Window%!handle%=W%
4090 SYS GetWindowState%,,Window%+handle%
4100 ysize%=Window%!y1%-Window%!y0%
4110 x%=Window%!x0%
4120 y%=Window%!y0%
4130 !Window% = W%
4140 Window%!4 = I%
4150 SYS GetIconInfo%, ,Window% : REM returns icon box in right place for drag box
4160 Window%!8 += x%
4170 Window%!12 += y% + ysize%
4180 Window%!16 += x%
4190 Window%!20 += y% + ysize%
4200REM get size in appropriate part of block: parent box=screen boundary
4210 Window%!24 = 0
4220 Window%!28 = 0
4230 Window%!32 = S_Width%
4240 Window%!36 = S_Height%
4250 !Window%=0
4260 Window%!4=5: REM fixed size drag box
4270 SAVING%=TRUE
4280 DRAGGING%=TRUE
4290 SYS DragBox, ,Window%
4300 ELSE
4310 PROCCloseWindow(Save_h%)
4320 PROCCloseMenu
4330 ENDIF
4340WHEN Load_h%
4350 PROCStopScoring
4360 IF I%=0 PROCload_music(FNGetStr(LoadText)) : REM 0 is OK.
4370 PROCCloseWindow(Load_h%)
4380 PROCCloseMenu
4390WHEN TimeSig_h%
4400 CASE I% OF
4410 WHEN 0
4420 TIME_SIG%(0)=1+(TIME_SIG%(0)+inc%+14)MOD15
4430 $BarLength%=STR$(TIME_SIG%(0)+1)
4440 WHEN 1
4450 TIME_SIG%(1)=(TIME_SIG%(1)-inc%)MOD4+2
4460 $NoteValue%=STR$(1<<(TIME_SIG%(1)-1))
4470 ENDCASE
4480 IF I%=0 OR I%=1 PROCUpdateIcon(W%,I%,0,0)
4490ENDCASE
4500ENDIF
4510ENDPROC
4520
4530DEF PROCCheckInstalledVoices
4540LOCAL NewVoice$, NewNVoices%, changedVoices%
4550changedVoices%=FALSE
4560SYS Sound_InstallVoice TO I$,NewNVoices%:NewNVoices%-=1
4570IF FNassert(NVoices%>0,"No sound voices are installed.") STOP
4580IF MIDIpresent% NewNVoices%+=1:Voice$(NewNVoices%)="":REM allow a NULL voice to permit just MIDI on this channel
4590IF NewNVoices%<>NVoices% THEN
4600 changedVoices%=TRUE
4610 NVoices%=NewNVoices%
4620 ENDIF
4630IF NVoices%>MAX_Voices% NVoices%=MAX_Voices%
4640FORR%=1TONVoices%
4650IF (MIDIpresent% AND R%=NVoices%) THEN
4660 Voice$(R%)=""
4670ELSE
4680 SYS Sound_InstallVoice,0,R% TO NewVoice$
4690 IF NewVoice$<>Voice$(R%) THEN
4700 changedVoices%=TRUE
4710 Voice$(R%)=NewVoice$
4720 ENDIF
4730 ENDIF
4740NEXT:REM find if instruments have changed
4750IF NOT changedVoices% ENDPROC
4760FORR%=0TO7
4770SYS Sound_AttachVoice,R%+1,0 TO L%,S%
4780IFS%<1ORS%>NVoices% S%=1:REM Make sure a voice is attached to all channels
4790SYS Sound_AttachVoice,L%,S%
4800Instrument%(R%,0)=S_C%(R%)+1:REM Instrument stave
4810Instrument%(R%,1)=S%:REM Instrument voice
4820$(VoiceStr%(R%)) = LEFT$(Voice$(S%), VoiceSize%)
4830PROCUpdateIcon(InstrWind_h%, R%+8, 0,0)
4840NEXT:REM Get details of each channel
4850PROCSetDefaultChannels
4860ENDPROC
4870
4880DEF FNAttachVoice(chan%,inc%) :REM inc% is +1 or -1 or 0 to find nearest
4890LOCAL stop%,v%,perc%
4900PROCCheckInstalledVoices
4910IF NVoices%<2 SYS Sound_AttachVoice, chan%+1, 1 : =1
4920REM attach next voice to channel. Attach percussion voice only on perc line, and not on stave lines
4930perc%=(INSTR($StaveStr%(chan%),"perc")>0) :REM is it a percussion channel?
4940SYS Sound_AttachVoice, chan%+1 TO , v%
4950IF v%=0 v%=1
4960SYS Sound_AttachVoice, chan%+1, v% :REM restore it
4970REM check if current voice is valid for this channel
4980IF inc%=0 THEN IF (perc% = (INSTR(Voice$(v%),"Perc")>0)) THEN =v%
4990REM inc or dec until found
5000IF ABS(inc%)<>1 THEN inc%=1
5010stop%=v%
5020REPEAT
5030 v%+=inc% : IF v%>NVoices% v%=1 ELSEIF v%<1 v%=NVoices%
5040 IF MIDIpresent% AND Voice$(v%)="" stop%=v% :REM permit NULL voice on any channel if MIDI installed
5050 UNTIL (perc% = (INSTR(Voice$(v%),"Perc")>0)) OR stop%=v%
5060=v%
5070
5080DEF PROCUpdateIcon(W%, I%, st%, msk%)
5090Window%!handle%=W%:!Icon%=I%
5100Icon%!state=st%:Icon%!mask=msk%
5110SYS SetIconState%,,Window%+handle% : REM update icon text
5120ENDPROC
5130
5140DEF PROCUserDragBox
5150LOCAL block
5160DRAGGING%=FALSE
5170block=Window% :REM temporary buffer
5180IF SAVING% THEN
5190 SYS GetPointerInfo%, ,block
5200 block!32=block!4
5210 block!28=!block
5220 block!24=block!16
5230 block!20=block!12 : REM this is the destination window handle%
5240 block!16=1 : REM DataSave
5250 block!12=0
5260 block!36=0 :REM don't know file size
5270 block!40=MusicFileType%
5280 $(block+44)=FNGetLeafName(SaveText)
5290 !block = 60
5300 SYS SendMessage, 17, block, block!20
5310 ENDIF
5320ENDPROC
5330
5340DEF FNSetBit(test, word, bitnum) : REM set or clear bit depending on test
5350IF test THEN
5360 = word OR (1<<bitnum)
5370ELSE
5380 = word AND NOT (1<<bitnum)
5390ENDIF
5400
5410DEF PROCMenuSelect
5420LOCAL item%, n, selection, SoundEnable%,F$
5430selection=Window%+handle%
5440SYS DecodeMenu, ,CurrentMenu%, selection, MenuString
5450CASE LEFT$($MenuString,INSTR($MenuString, ".")-1) OF
5460 WHEN "Save"
5470 F$=FNGetStr(SaveText)
5480 IF ( INSTR(F$,".")=0 AND INSTR(F$,":")=0 ) THEN
5490 PROCOpenWindow(Save_h%, Mouse_X%, Mouse_Y%)
5500 ELSE
5510 PROCsave_music(F$)
5520 ENDIF
5530 WHEN "Staves"
5540 SYS "Hourglass_On"
5550 IF LEFT$($StaveNum%,1)="" $StaveNum%="1"
5560 STAVE%=VAL(LEFT$($StaveNum%,1))-1
5570 IF INSTR($MenuString,"+perc",6) THEN
5580 IF PERC%=1 PERC%=0 ELSE PERC%=1
5590 ENDIF
5600 item% = FNFindMenuItem("+percussion", StaveMenu%)
5610 IF (item%>=0) !item% = FNSetBit(PERC%=1, !item%, 0):REM set or clear tick
5620 PROCsetup_score
5630 PROCstart_music
5640 PROCrescore(0)
5650 SYS "Hourglass_Off"
5660 WHEN "Instruments" : PROCOpenWindow(InstrWind_h%,Mouse_X%,Mouse_Y%)
5670 WHEN "Volume"
5680 IF selection!4>=0 THEN
5690 PROCSetVolume(selection!4)
5700 PROCSetMenuTick(VolumeMenu%, selection!4)
5710 ENDIF
5720 WHEN "Tempo"
5730 IF selection!4>=0 THEN
5740 PROCSetTempo(selection!4)
5750 PROCSetMenuTick(TempoMenu%, selection!4)
5760 ENDIF
5770 WHEN "Time sig" : TIME_SIG%(0)=VAL($BarLength%)-1
5780 WHEN "Key sig"
5790 IF selection!4>=0 THEN
5800 KEY_SIG%(0) = ((selection!8)>=7)+1
5810 KEY_SIG%(1) = ABS((selection!8)-7)
5820 PROCSetMenuTick(MajorMenu%, selection!8)
5830 PROCSetMenuTick(MinorMenu%, selection!8)
5840 IF KEY_SIG%(1) n=accidental%+2+KEY_SIG%(0) : X%(key%)=(x%(n)+X%(n))*KEY_SIG%(1) ELSE X%(key%)=x%(accidental%+2)+X%(accidental%+2)
5850 ENDIF
5860 WHEN "Play"
5870 SYS Sound_Enable,0 TO SoundEnable%:REM dont pretend to be able to play if the sound system is disabled
5880 IF SoundEnable%=2 THEN
5890 SCORING%=FALSE
5900 PLAYING%=NOT PLAYING%
5910 SCROLLING%=PLAYING%
5920 IF PLAYING% PROCplay_start ELSE PROCplay_stop
5930 ELSE n=FNCheckOK("Sound is not enabled. You cannot play", 1)
5940 ENDIF
5950 WHEN "Clear"
5960 IF CHANGED% IF NOT FNCheckOK("Are you sure? Current music is unsaved",3) ENDPROC
5970 PROCClearAllMusic
5980 Window%!handle%=ScoreWind_h%
5990 SYS GetWindowState%,,Window%+handle%
6000 Window%!scx%=0
6010 LHBAR%=0
6020 SYS OpenWindow%,,Window%+handle%
6030 SYS GetWindowState%,,ScoreWBlk%+handle%
6040 WHEN "Info" : PROCOpenWindow(ProgInfo_h%, Mouse_X%, Mouse_Y%)
6050 WHEN "Quit" : IF CHANGED% PROCOpenAbortQuery(Mouse_X%, Mouse_Y%) ELSE PROCterminate
6060 WHEN "Goto"
6070 Window%!handle%=ScoreWind_h%
6080 SYS GetWindowState%,,Window%+handle%
6090 Window%!scx%=0
6100 SYS OpenWindow%,,Window%+handle% : REM set scroll to 0
6110 SYS GetWindowState%,,ScoreWBlk%+handle%
6120 LHBAR%=0 :REM the bar number at the left of the window
6130 ENDCASE
6140 IF (CurrentMenu%=MenuStart) THEN
6150 SYS GetPointerInfo%,,Mouse%
6160 IF %001 AND Mouse%!buttons PROCOpenMainMenu :REM persistent menu
6170 ENDIF
6180ENDPROC
6190
6200DEF PROCSetMenuTick(menu%, this%)
6210REM set 1 tick in menu and clear all others
6220LOCAL n, item%
6230n = 0
6240item%=menu%+28
6250!item% = FNSetBit(n=this%, !item%, 0) :REM set or clear tick
6260REPEAT
6270 item% += 24
6280 n += 1
6290 !item% = FNSetBit(n=this%, !item%, 0) :REM set or clear tick
6300 UNTIL (!item% AND &80)
6310ENDPROC
6320
6330DEF PROCKeyPressed
6340LOCAL ThisWindow%
6350IF !(Window%+handle%+24)=13 THEN
6360 ThisWindow%=Window%!handle%
6370 CASE (ThisWindow%) OF
6380 WHEN Save_h% : PROCsave_music(FNGetStr(SaveText)) : REM c/r in save window
6390 WHEN Load_h% : PROCload_music(FNGetStr(LoadText)) : REM c/r in load window
6400 WHEN Bar_h%
6410 BAR%=VAL($BarNum%)
6420 IF BAR%>NBars% BAR%=NBars% ELSEIF BAR%<0 BAR%=0
6430 Window%!handle%=ScoreWind_h%
6440 SYS GetWindowState%,,Window%+handle%
6450 Window%!scx%=PX%(PXn%(BAR%))
6460 SYS OpenWindow%,,Window%+handle% : REM set scroll to requested bar number
6470 SYS GetWindowState%,,ScoreWBlk%+handle%
6480 LHBAR%=FNFindBar(ScoreWBlk%!scx%) :REM the bar number at the left of the window
6490 OTHERWISE : SYS "Wimp_ProcessKey",!(Window%+handle%+24) : REM ie. pass on key code in R0
6500 ENDCASE
6510 PROCCloseWindow(ThisWindow%)
6520 PROCCloseMenu
6530 ELSE
6540 SYS "Wimp_ProcessKey",!(Window%+handle%+24) : REM ie. pass on key code in R0
6550 ENDIF
6560ENDPROC
6570
6580DEF PROCScrollReq(x_scroll%, y_scroll%)
6590REM if bad mode disallow scroll to prevent mode warning being garbled
6600IF BADMODE% ENDPROC
6610IF Window%!handle%=ScoreWind_h% THEN
6620 IF PLAYING%AND(x_scroll%<>0) ENDPROC
6630 CASE ABS(x_scroll%) OF
6640 WHEN 1:Window%!scx%+=x_scroll%*4*Pgap%
6650 WHEN 2:Window%!scx%+=(x_scroll%/2)*(Window%!x1%-Window%!x0%)
6660 ENDCASE
6670 CASE ABS(y_scroll%) OF
6680 WHEN 1:Window%!scy%+=y_scroll%*C_Height%
6690 WHEN 2:Window%!scy%+=(y_scroll%/2)*(Window%!y1%-Window%!y0%)
6700 ENDCASE
6710 SYS OpenWindow%, ,Window%+handle%
6720 SYS GetWindowState%,,ScoreWBlk%+handle%
6730 ScoreClosed%=FALSE
6740 LHBAR%=FNFindBar(ScoreWBlk%!scx%) :REM the bar number at the left of the window
6750 ENDIF
6760ENDPROC
6770
6780DEF PROCOpenMainMenu
6790LOCAL item%
6800item% = FNFindMenuItem("Play", MenuStart)
6810IF (item%>=0) !item% = FNSetBit(PLAYING%, !item%, 0)
6820REM set or clear tick
6830item% = FNFindMenuItem("Goto", MenuStart)
6840IF (item%>=0) item%!8 = FNSetBit(PLAYING%, item%!8, 22)
6850REM shaded bit. disable Goto when playing
6860SYS CreateMenu, ,MenuStart, Mouse_X%-C_Width%*4, Mouse_Y%+C_Height%*4
6870CurrentMenu%=MenuStart
6880PROCCloseWindow(Save_h%)
6890PROCCloseWindow(Load_h%)
6900ENDPROC
6910
6920DEF PROCOpenMenu(Menu%)
6930IF (Menu%=IconMenu%) THEN
6940 SYS CreateMenu, ,IconMenu%, Mouse_X%-C_Width%*4, 112+C_Height%*2:REM y adjusted to line up
6950ELSE
6960 SYS CreateMenu, ,Menu%, Mouse_X%-C_Width%*4, Mouse_Y%+C_Height%
6970ENDIF
6980CurrentMenu%=Menu%
6990PROCCloseWindow(Save_h%)
7000PROCCloseWindow(Load_h%)
7010ENDPROC
7020
7030DEF PROCCloseMenu
7040SYS CreateMenu, ,-1
7050PROCCloseWindow(Save_h%)
7060PROCCloseWindow(Load_h%)
7070ENDPROC
7080
7090DEF PROCsymbol_pointer
7100SYS GetPointerInfo%,,Mouse%
7110IF (Mouse%!window=ScoreWind_h%) PROCscribe(Mouse%!x0%,Mouse%!y0%)
7120ENDPROC
7130
7140DEF PROCStopScoring
7150stopSCORING%=TRUE
7160PROCrelease
7170PROCUpdateIcon(SelW%, SelI%, 0, 1<<2) :REM remove highlight on icon in pane
7180ENDPROC
7190
7200DEF PROCUpdateTitle(T$)
7210LOCAL a,b,c,d
7220$ScoreTitle%=T$+CHR$(0)
7230Window%!handle%=ScoreWind_h%
7240SYS GetWindowState%,,Window%+handle%
7250a=Window%!x0% : b=Window%!y1% : c=Window%!x1% :REM get title area and force redraw
7260SYS GetWindowOutline,,Window%+handle%
7270d=Window%!y1%
7280SYS ForceRedraw,-1,a+Hi%,b+Vi%,c-Hi%,d+Vi%
7290ENDPROC
7300
7310DEF PROCOpenWindow(h%, x%, y%)
7320LOCAL xsize%, ysize%
7330Window%!handle% = h%
7340SYS GetWindowState%, ,Window%+handle%
7350xsize%=Window%!x1%-Window%!x0%
7360ysize%=Window%!y1%-Window%!y0%
7370Window%!under%=-1
7380Window%!x0%=x%
7390Window%!x1%=Window%!x0%+xsize%
7400Window%!y0%=y%
7410Window%!y1%=Window%!y0%+ysize%
7420SYS OpenWindow%, ,Window%+handle%
7430ENDPROC
7440
7450DEF PROCCloseWindow(h%)
7460Window%!handle%=h%
7470SYS CloseWindow%, ,Window%+handle%
7480ENDPROC
7490
7500DEF PROCSetExtent(W%)
7510Score_Width%=W%
7520Window%!x0%=0
7530Window%!y0%=-Score_Height%-PaneHeight%
7540Window%!x1%=Score_Width%
7550Window%!y1%=PaneHeight%
7560SYS SetExtent,ScoreWind_h%,Window%
7570ENDPROC
7580
7590DEF FNGetLeafName(name%) :REM returns leaf name
7600LOCAL ch$,n%,name$
7610name$=FNGetStr(name%)
7620IF ( (INSTR(name$,".")=0) AND (INSTR(name$,":")=0) ) THEN=name$
7630n%=LEN(name$)
7640REM scan string to find leaf name of file
7650REPEAT
7660 ch$= MID$(name$, n%, 1)
7670 n%-=1
7680 UNTIL (n%<=0 OR ch$="." OR ch$=":")
7690IF n%>0 THEN =RIGHT$(name$, LEN(name$)-n%-1)
7700
7710DEF FNGetStr(s%) : REM get string
7720LOCAL n$
7730WHILE?s%:n$+=CHR$?s%:s%+=1:ENDWHILE
7740=n$
7750
7760DEF PROCSetVolume(R%)
7770SYS Sound_Volume,Volume%(R%)
7780ENDPROC
7790
7800DEF PROCSetDefaultChannels
7810LOCALS%,P%
7820REM only 1 percussion channel permitted now
7830FORS%=0TO7:S_C%(S%)=Stave_Channels%(STAVE%,S%):NEXT:REM Channel assignment changes if staves change
7840IF PERC% S_C%(7)=STAVE%+1:REM Steal channel for percussion lines
7850FORS%=0TO7
7860Instrument%(S%,0)=S_C%(S%)+1
7870$StaveStr%(S%)=Nth$(Instrument%(S%,0)+(STAVE%-3)*((Instrument%(S%,0)-1)>STAVE%))+";" :REM set up stave names in instrument wimdow
7880PROCUpdateIcon(InstrWind_h%,S%,0,0) :REM update text in icons
7890v%=FNAttachVoice(S%,0)
7900SYS Sound_AttachVoice,S%+1,v%
7910$(VoiceStr%(S%))=LEFT$(Voice$(v%), VoiceSize%)
7920Instrument%(S%,1) = v%
7930PROCUpdateIcon(InstrWind_h%,S%+8, 0,0)
7940NEXT:REM Instrument allocation
7950IF PERC% $StaveStr%(7)=Nth$(5)+";" :REM steal 1 channel for percussion
7960ENDPROC
7970
7980DEF PROCsetup_score
7990LOCALS%,P%
8000REM only 1 percussion channel permitted now
8010PROCsetup_staves:REM Calculate new stave positions
8020PROCSetDefaultChannels
8030ENDPROC
8040
8050DEF FNGetFileInfo(F$)
8060LOCALT%,L%,A%,M$,time%,FileType$,r2,r3
8070IF F$<>"" SYSOS_File,5,F$ TO T%,,laddr%,eaddr%,L%,A%
8080IF (F$="") OR ((T%=1) AND (A% AND 1) AND (L%>8)) = 0 THEN
8090$ThisFile%="<untitled>"+CHR$(0)
8100$FileSize%=""
8110$FileType%=""
8120$FileDate%=""
8130=FALSE
8140ELSE
8150$ThisFile%=F$
8160$FileSize%=STR$(L%)
8170IF ((laddr%>>20)AND&FFF)=&FFF THEN
8180 IF (laddr%>>8 AND &FFF) = MusicFileType% THEN
8190 FileType$= "Music "
8200 ELSE
8210 SYS "OS_FSControl",18,,laddr%>>8 AND &FFF TO ,,r2,r3
8220 FileType$=CHR$(r2 AND &FF) + CHR$((r2>>8) AND &FF) + CHR$((r2>>16) AND &FF) + CHR$((r2>>24) AND &FF)
8230 FileType$ += CHR$(r3 AND &FF) + CHR$((r3>>8) AND &FF) + CHR$((r3>>16) AND &FF) + CHR$((r3>>24) AND &FF)
8240 ENDIF
8250 $FileType%=FileType$+STR$~(laddr%>>8 AND &FFF)
8260 time%=Window% :REM a convenient buffer
8270REM load and execution addresses are, in fact, time stamp
8280 time%?4=laddr% AND &FF
8290 time%?3=eaddr%>>24 AND &FF
8300 time%?2=eaddr%>>16 AND &FF
8310 time%?1=eaddr%>>8 AND &FF
8320 time%?0=eaddr% AND &FF
8330 SYS "OS_ConvertStandardDateAndTime", time%, FileDate%, 28
8340 REM put timestamp of file into file info window
8350 ENDIF
8360ENDIF
8370=TRUE
8380
8390DEF PROCload_music(F$)
8400LOCAL F%,M$
8410LOCAL ERROR
8420IF CHANGED% IF NOT FNCheckOK("Are you sure? Current music is unsaved",3) ENDPROC
8430SCORING%=FALSE
8440IF PLAYING% PROCplay_stop
8450SCROLLING%=FALSE
8460IF LENF$=0 THEN VDU7 : T%=FNCheckOK("Invalid filename",1) : ENDPROC
8470SYS "Hourglass_On"
8480ON ERROR LOCAL VDU7: OSCLI("FX 229,1") : PROCClearAllMusic : SYS "Hourglass_Off" : T%=FNCheckOK(REPORT$,1) : ENDPROC
8490OSCLI("FX 229,0") :REM enable escape key
8500FILE%=OPENINF$
8510M$="":FORR%=1TO7:M$+=CHR$BGET#FILE%:NEXT
8520B%=BGET#FILE%
8530IFM$="Maestro" THEN
8540$LoadText=F$+CHR$(0)
8550$SaveText=F$+CHR$(0)
8560IF NOT FNGetFileInfo(F$) THEN VDU7 :OSCLI("FX 229,1") : SYS "Hourglass_Off" :T%=FNCheckOK("Invalid or locked file",1) : ENDPROC
8570T%=TRUE
8580NBars%=0
8590CASE BGET#FILE% OF
8600WHEN 0:T%=FALSE
8610WHEN 1
8620PROClTempo:PROClInstruments:PROClStaves:PROClMusic
8630OTHERWISEREM File id version 2 and above
8640A%=FALSE
8650REPEAT
8660ON BGET#FILE% PROClMusic,PROClStaves,PROClInstruments,PROClVolumes,PROClStereos,PROClTempo ELSEA%=TRUE
8670UNTILEOF#FILE%ORA%
8680ENDCASE
8690CLOSE#FILE%:FILE%=FALSE
8700OSCLI("FX 229,1")
8710$Updated%="NO"
8720PROCUpdateTitle(F$)
8730SYS "Hourglass_Off"
8740SYS "Hourglass_On"
8750IFT% THEN
8760 PROCposition_staves
8770 PROCstart_music
8780 PROCset_score(0)
8790 PROCSetupBarStarts(0)
8800 PROCsetup_score
8810 PROCupdate_score(0,-Score_Height%,Score_Width%,0)
8820 PROCStopScoring
8830 CHANGED%=FALSE
8840 ENDIF
8850ELSE
8860OSCLI("FX 229,1")
8870T%=FALSE
8880ENDIF
8890IF NOT T% THEN VDU7 : T%=FNCheckOK("Invalid music file",1)
8900SYS "Hourglass_Off"
8910ENDPROC
8920
8930DEF PROClMusic
8940LOCALC%,B%
8950INPUT#FILE%,GATE%:GATE%+=MUSIC%
8960FORC%=0TO7
8970INPUT#FILE%,FINE%(C%):FINE%(C%)+=MUSIC%(C%)
8980NEXT
8990B%=MUSIC%:WHILEB%<GATE%:?B%=BGET#FILE%:B%+=1:ENDWHILE
9000FORC%=0TO7
9010B%=MUSIC%(C%):WHILEB%<FINE%(C%):?B%=BGET#FILE%:B%+=1:ENDWHILE
9020NEXT
9030PP%=MUSIC%:P%()=MUSIC%()
9040ENDPROC
9050
9060DEF PROClStaves
9070LOCAL item%
9080STAVE%=BGET#FILE%
9090$StaveNum%=LEFT$(STR$(STAVE%+1),1)
9100PERC%=BGET#FILE%
9110item% = FNFindMenuItem("+percussion", StaveMenu%)
9120IF item%>0 !item%=FNSetBit(PERC%=1, !item%, 0) :REM set or clear tick
9130ENDPROC
9140
9150DEF PROClInstruments
9160LOCALC%, chan%,v%
9170FORC%=0TO7
9180chan%=BGET#FILE%:REM Channel
9190v%=BGET#FILE%MOD(NVoices%+1):REM Write voice numbers allocated to each channel
9200IF v%=0 v%=1
9210SYS Sound_AttachVoice, chan%+1, v%
9220v%=FNAttachVoice(chan%, 0) :REM check if it is a valid voice
9230SYS Sound_AttachVoice, chan%+1, v%
9240Instrument%(chan%,1) = v%
9250$(VoiceStr%(chan%))=LEFT$(Voice$(v%), VoiceSize%)
9260PROCUpdateIcon(InstrWind_h%, chan%+8, 0,0)
9270NEXT
9280ENDPROC
9290
9300DEF PROClVolumes
9310LOCALC%
9320FORC%=0TO7
9330Volumes%(C%)=BGET#FILE%
9340IFVolumes%(C%)>7 Volumes%(C%)=7
9350IFVolumes%(C%)<0 Volumes%(C%)=0
9360$(VolumeStr%(C%))=LEFT$(Volume$(Volumes%(C%)), VolSize%)
9370PROCUpdateIcon(InstrWind_h%, C%+16, 0,0) :REM update icon in instrument window
9380NEXT
9390ENDPROC
9400
9410DEF PROClStereos
9420LOCALC%
9430FORC%=0TO7
9440Stereo_Position%(C%)=BGET#FILE%
9450SYS Sound_Stereo,C%+1,Stereo%(Stereo_Position%(C%))
9460$(StereoStr%(C%)) = LEFT$(Stereo$(Stereo_Position%(C%), 0), SterSize%)
9470PROCUpdateIcon(InstrWind_h%, C%+24, 0,0)
9480NEXT
9490ENDPROC
9500
9510DEF PROClTempo
9520LOCAL t
9530t=BGET#FILE%
9540PROCSetTempo(t)
9550PROCSetMenuTick(TempoMenu%, t)
9560ENDPROC
9570
9580DEF PROCsave_music(F$)
9590LOCAL block,n
9600LOCAL ERROR
9610block=Window%
9620$SaveText=F$+CHR$(0)
9630$LoadText=F$+CHR$(0)
9640REM simple check for pathname rather than local name
9650IF ( INSTR(F$,".")=0 AND INSTR(F$,":")=0 ) THEN
9660 n=FNCheckOK("To save, drag the icon to a directory viewer.",1)
9670 ENDPROC
9680 ENDIF
9690SYS "Hourglass_On"
9700ON ERROR LOCAL OSCLI("FX 229,1") : SYS "Hourglass_Off" : T%=FNCheckOK(REPORT$,1) : ENDPROC
9710OSCLI("FX 229,0") :REM enable escape key
9720IF CHANGED% OR (((laddr%>>20)AND&FFF) <> &FFF) THEN
9730 REM file changed or wasn't timestamped
9740 REM get current time
9750 block?0=3:SYS "OS_Word",&0E,block
9760 laddr%=block?4
9770 eaddr%=block!0
9780 ENDIF
9790REM force music file type, and preserve timestamp
9800laddr%=(laddr% AND &FF) OR (&FFF<<20) OR (MusicFileType%<<8)
9810REM I don't know what the length will be, so use zero
9820SYS "OS_File", &07, F$, laddr%, eaddr%, 0, 0
9830REM OPENUP, error if directory or not found
9840SYS "OS_Find", &CC, F$ TO FILE%
9850REM timestamp is automatically updated on 1st byte write
9860BPUT#FILE%,"Maestro"
9870BPUT#FILE%,2
9880PROCsMusic
9890PROCsStaves
9900PROCsInstruments
9910PROCsVolumes
9920PROCsStereos
9930PROCsTempo
9940CLOSE#FILE%:FILE%=FALSE
9950IF (NOT CHANGED%) AND (((laddr%>>20) AND &FFF) = &FFF) THEN
9960 REM file not changed and was timestamped
9970 REM preserve original timestamp
9980 SYS OS_File,2,F$,laddr% :REM re-stamp with old stamp
9990 SYS OS_File,3,F$,,eaddr% :REM nb eaddr% in r3
10000 ENDIF
10010OSCLI("FX 229,1")
10020F%=FNGetFileInfo(F$)
10030CHANGED%=FALSE
10040$Updated%="NO"
10050PROCUpdateTitle(F$)
10060IF NOT T% THEN VDU7 : T%=FNCheckOK("Invalid music file",1)
10070SYS "Hourglass_Off"
10080ENDPROC
10090
10100DEF PROCsMusic
10110LOCALC%,B%
10120BPUT#FILE%,1
10130PRINT#FILE%,GATE%-MUSIC%
10140FORC%=0TO7
10150PRINT#FILE%,FINE%(C%)-MUSIC%(C%)
10160NEXT
10170B%=MUSIC%:WHILEB%<GATE%:BPUT#FILE%,?B%:B%+=1:ENDWHILE
10180FORC%=0TO7
10190B%=MUSIC%(C%):WHILEB%<FINE%(C%):BPUT#FILE%,?B%:B%+=1:ENDWHILE
10200NEXT
10210ENDPROC
10220
10230DEF PROCsStaves
10240BPUT#FILE%,2
10250BPUT#FILE%,STAVE%
10260BPUT#FILE%,PERC%
10270ENDPROC
10280
10290DEF PROCsInstruments
10300LOCALC%
10310BPUT#FILE%,3
10320FORC%=0TO7
10330BPUT#FILE%,C%
10340BPUT#FILE%,Instrument%(C%,1)
10350NEXT
10360ENDPROC
10370
10380DEF PROCsVolumes
10390LOCALC%
10400BPUT#FILE%,4
10410FORC%=0TO7
10420BPUT#FILE%,Volumes%(C%)
10430NEXT
10440ENDPROC
10450
10460DEF PROCsStereos
10470LOCALC%
10480BPUT#FILE%,5
10490FORC%=0TO7
10500BPUT#FILE%,Stereo_Position%(C%)
10510NEXT
10520ENDPROC
10530
10540DEF PROCsTempo
10550BPUT#FILE%,6
10560BPUT#FILE%,Tempo%
10570ENDPROC
10580
10590DEF PROCsprite(s%,X%,Y%) : REM plot sprite S%(s%) at X%,Y%
10600SYS SpriteOp%, SprPlot%, SprBlk%, S%(s%), X%-x%(s%),Y%-y%(s%), 8, factors%, pixtrans%
10610REM overwrite screen colour, but using sprite mask
10620ENDPROC
10630
10640DEF PROCfloat(s%,X%,Y%)
10650LOCALR%
10660Window%!handle%=ScoreWind_h%
10670Window%!x1%=X%+X%(s%)
10680X%-=x%(s%)
10690Window%!x0%=X%
10700Window%!y1%=Y%+Y%(s%)
10710Y%-=y%(s%)
10720Window%!y0%=Y%
10730SYS UpdateWindow%,,Window%+handle% TO R%
10740X%+=Window%!x0%-Window%!scx%
10750Y%+=Window%!y1%-Window%!scy%
10760ChSprite%=S%(s%)
10770WHILE R%
10780 CASE s% OF
10790 WHEN key% : PROCfloat_key_sig(X%,Y%+y%(s%))
10800 WHEN time% : PROCfloat_time_sig(X%,Y%)
10810 OTHERWISE : pixtrans%?1 = pixtrans%?0 EOR pixtrans%?1
10820 SYS SpriteOp%, SprPlot%, SprBlk%, ChSprite%, X%,Y%, 11, factors%, pixtrans%
10830 pixtrans%?1 = pixtrans%?0 EOR pixtrans%?1
10840 ENDCASE
10850 SYS GetRectangle%,,Window%+handle% TO R%
10860 ENDWHILE
10870ENDPROC
10880
10890DEF PROCfloat_key_sig(X%,Y%)
10900LOCALI%,A%,C%,W%
10910IFKEY_SIG%(1) THEN
10920C%=SCRIBE%(sclef%)
10930A%=KEY_SIG%(0)
10940I%=accidental%+2+A%
10950W%=x%(I%)+X%(I%):Y%-=y%(I%)
10960ChSprite%=S%(I%)
10970FOR I%=0 TO KEY_SIG%(1)-1
10980SYS SpriteOp%, SprPlot%, SprBlk%, ChSprite%, X%, Y%+Li%*Key_Y%(C%,A%,I%), 3, factors%, pixtrans%
10990X%+=W%
11000NEXT
11010ELSE
11020SYS SpriteOp%, SprPlot%, SprBlk%, ChSprite%, X%, Y%-y%(s%), 3, factors%, pixtrans%
11030ENDIF
11040ENDPROC
11050
11060DEF PROCfloat_time_sig(X%,Y%)
11070SYS SetColour, 7 OR 3<<4
11080IF LEN($NoteValue%)>1 MOVE X%+C_Width%DIV2, Y%+C_Height% ELSE MOVE X%+C_Width%, Y%+C_Height%
11090PRINT $NoteValue%
11100IF LEN($BarLength%)>1 MOVE X%+C_Width%DIV2, Y%+2*C_Height% ELSE MOVE X%+C_Width%, Y%+2*C_Height%
11110PRINT $BarLength%
11120ENDPROC
11130
11140REM *********************************************************************** REM
11150REM REM
11160REM M U S I C T R A N S C R I P T I O N R O U T I N E S REM
11170REM REM
11180REM *********************************************************************** REM
11190::
11200REM PROCEDURE: start_music
11210REM
11220REM DESCRIPTION: Initialise to start of music
11230:
11240DEF PROCstart_music
11250LOCAL n%
11260BAR%=0 :REM current bar
11270GP%=MUSIC%:REM Set current gate pointer to start of displayed music
11280N%()=MUSIC%()
11290CLEF%()=0:REM Start with base clefs (NOT BASS!)
11300SIG%(0)=%01100111:REM (Default time sig of 4/4 time)
11310SIG%(1)=%00000010:REM (Default of C Major) First displayed signatures attribute byte
11320PX%=0
11330PROCPutBarInfo(0)
11340ENDPROC
11350
11360REM PROCEDURE: note_type(Channel C%, Type T%)
11370
11380DEF PROCnote_type(C%,T%)
11390N%(C%)?1=N%(C%)?1AND&1F ORT%<<5
11400ENDPROC
11410
11420DEF PROCnote_dots(C%,D%)
11430N%(C%)?1=N%(C%)?1AND&E7 ORD%<<3
11440ENDPROC
11450
11460DEF PROCnote_accidental(C%,A%)
11470N%(C%)?1=N%(C%)?1AND&F8 ORA%
11480ENDPROC
11490
11500REM PROCEDURE: note_line(Channel C%, Stave line L%)
11510REM
11520REM NOTE: If a note's position is not defined or specified as -16, it is treated as a rest
11530
11540DEF PROCnote_line(C%,L%)
11550?N%(C%)=?N%(C%)AND7 OR(16+L%)<<3
11560ENDPROC
11570
11580DEF PROCnote_tie(C%,T%)
11590?N%(C%)=?N%(C%)AND&FB OR(T%<>0)AND4
11600ENDPROC
11610
11620DEF PROCnote_join(C%,J%)
11630?N%(C%)=?N%(C%)AND&FD OR(J%<>0)AND2
11640ENDPROC
11650
11660DEF PROCnote_stem(C%,D%)
11670?N%(C%)=?N%(C%)AND&FE OR(D%<>0)AND%1
11680ENDPROC
11690
11700DEF PROCnote_clear(C%)
11710?N%(C%)=0:N%(C%)?1=0
11720ENDPROC
11730
11740REM PROCEDURE: time_sig(Number of beats-1 N%,Beat type B%)
11750
11760DEF PROCtime_sig(N%,B%)
11770?GP%=0:GP%?1=Time%ORN%<<1ORB%<<5
11780ENDPROC
11790
11800REM PROCEDURE: key_sig(Key K%)
11810
11820DEF PROCkey_sig(A%,N%)
11830?GP%=0:GP%?1=Key%ORA%<<2ORN%<<3
11840ENDPROC
11850
11860REM PROCEDURE: clef(Stave S%, Clef C%)
11870
11880DEF PROCclef(S%,C%)
11890?GP%=0:GP%?1=Clef%ORC%<<3ORS%<<6
11900ENDPROC
11910
11920DEF PROCbar
11930?GP%=0:GP%?1=Bar%
11940ENDPROC
11950
11960DEF PROCinsert_gate(W%)
11970LOCALG%
11980IFGP%<GATE% THEN
11990FORG%=GATE%-W%TOGP%STEP-4:G%!W%=!G%:NEXT:REM Shift up by W% from insertion
12000G%+=3:REM Byte before last copied
12010WHILEG%>=GP%:G%?W%=?G%:G%+=TRUE:ENDWHILE:REM Clear up in odd word
12020ENDIF
12030GATE%+=W%:REM Insert gate of size W% into gate queue
12040EP%+=W%:REM This assumes gates will only be inserted below EP%
12050?GP%=0:GP%?(W%-1)=0:REM Zeroise inserted gate
12060ENDPROC
12070
12080DEF PROCinsert_note(C%)
12090LOCALN%
12100IFN%(C%)<FINE%(C%) THEN
12110FORN%=FINE%(C%)-2TON%(C%)STEP-4:N%!2=!N%:NEXT:REM Shift up from insertion
12120N%+=3:REM Last copied word
12130WHILEN%>=N%(C%):N%?2=?N%:N%+=TRUE:ENDWHILE:REM Clear up in odd word
12140ENDIF
12150FINE%(C%)+=2:REM Insert a note word into this queue
12160?GP%=?GP%OR%1<<C%:REM Mark in gate
12170PROCnote_clear(C%)
12180ENDPROC
12190::
12200DEF PROCdelete_gate(W%)
12210LOCALG%
12220GATE%-=W%
12230IFGP%<GATE% FORG%=GP%TOGATE%STEP4:!G%=G%!W%:NEXT
12240EP%-=W%:REM This assumes gates will only be deleted below EP%
12250ENDPROC
12260::
12270DEF PROCdelete_note(C%)
12280LOCALN%
12290FINE%(C%)-=2
12300IFN%(C%)<FINE%(C%) FORN%=N%(C%)TOFINE%(C%)STEP4:!N%=N%!2:NEXT
12310?GP%=?GP%ANDNOT(%1<<C%)
12320ENDPROC
12330
12340DEF FNallocate_channel(S%)
12350LOCALC%,c%,G%
12360G%=?GP%
12370C%=-1:c%=7
12380REPEAT
12390WHILEc%>=0ANDG%AND%1<<c%:c%+=TRUE:ENDWHILE
12400IFc%>=0 IFS_C%(c%)=S% C%=c%
12410c%+=TRUE
12420UNTILc%<0
12430=C%
12440
12450DEF PROCarrange_stave(S%)
12460LOCALC%,B%
12470B%=TRUE:C%=TRUE
12480IF?GP% ELSEREPEATGP%+=2:UNTIL?GP%ORGP%>=GATE%
12490WHILEB%ANDGP%<GATE%
12500C%=C%ORFNsort_gate
12510PROCskip_notes(?GP%):GP%+=1
12520IF?GP% ELSEB%=C%:C%=FALSE:REPEATGP%+=2:UNTIL?GP%ORGP%>=GATE%
12530ENDWHILE
12540ENDPROC
12550
12560DEF FNsort_gate
12570LOCALC%,NC%,NN%,G%,g%,pg%,d%,shortest%,Gchanged%
12580shortest%=255
12590G%=?GP%
12600g%=FNprevious_gate(GP%)
12610pg%=FNpreceding_gate(GP%)
12620NC%=-1
12630NN%=-1
12640FORC%=0TO7
12650IFS_C%(C%)=S% THEN
12660NC%+=1:n%(NC%)=C%:IFG%AND%1<<C% NN%+=1:C%(NN%)=C%
12670IFpg%AND%1<<C% d%=N%(C%)?-1>>3EOR%11100:IFd%<shortest% shortest%=d%
12680ENDIF
12690NEXT
12700shortest%=shortest%EOR%11100
12710IFg%ANDNC%>0ANDNN%>=0 PROCsort
12720=Gchanged%
12730
12740DEF PROCsort
12750LOCALN%,M%
12760c%()=-1
12770FORM%=0TONC%
12780FORN%=0TONN%
12790IFFNsame_pitch(n%(M%),C%(N%)) c%(N%)=n%(M%)
12800NEXT
12810NEXT
12820FORN%=0TONN%
12830IFc%(N%)<0 c%(N%)=FNbest
12840NEXT
12850FORN%=0TONN%
12860IFC%(N%)=c%(N%) ELSEGchanged%=TRUE:M%=FNin(c%(N%),C%()):IFM%>N% PROCswap_notes(N%,M%) ELSEPROCmove_note(N%)
12870NEXT
12880ENDPROC
12890
12900DEF PROCswap_notes(N%,M%)
12910LOCALs%,d%
12920s%=C%(N%):d%=c%(N%)
12930SWAPC%(N%),C%(M%)
12940SWAP?N%(s%),?N%(d%)
12950SWAPN%(s%)?1,N%(d%)?1
12960ENDPROC
12970
12980DEF PROCmove_note(N%)
12990LOCALs%,d%
13000s%=C%(N%):d%=c%(N%)
13010PROCinsert_note(d%)
13020?N%(d%)=?N%(s%)
13030N%(d%)?1=N%(s%)?1
13040PROCdelete_note(s%)
13050ENDPROC
13060
13070DEF FNbest
13080LOCALN%,C%
13090LOCAL short%,free%,rest%,any%,tied%
13100FOR N%=NC%TO0STEP-1
13110C%=n%(N%)
13120IFFNin(C%,c%())<0 THEN
13130IFN%(C%)?-2AND4 THEN
13140tied%=C%+1
13150ELSE
13160IFpg%AND%1<<C% THEN
13170IF(N%(C%)?-1>>3)=shortest% short%=C%+1
13180IFN%(C%)?-2AND&F8 ELSErest%=C%+1
13190ELSE
13200free%=C%+1
13210ENDIF
13220any%=C%+1
13230ENDIF
13240ENDIF
13250NEXT
13260IFshort% C%=short% ELSEIFfree% C%=free% ELSEIFrest% C%=rest% ELSEIFany% C%=any% ELSEC%=tied%
13270=C%-1
13280
13290DEF FNin(U%,U%())
13300LOCALI%:I%=NN%
13310WHILEI%ANDU%<>U%(I%):I%-=1:ENDWHILE
13320=I%+(U%<>U%(I%))
13330
13340DEF FNsame_pitch(c%,C%):LOCALR%,r%:R%=?N%(C%)AND&F8:r%=N%(c%)?-2AND&F8:=N%(c%)-2>=MUSIC%(c%)AND(g%OR(N%(c%)?-2AND4)=4)AND%1<<c%AND(R%EORr%)=FALSE AND(FALSE<>R%EORr%=FALSE)
13350
13360DEF FNpreceding_gate(gp%)
13370LOCALC%,gm%
13380FORC%=0TO7:IFS_C%(C%)=S% gm%=gm%OR%1<<C%
13390NEXT
13400REPEAT
13410gp%-=1
13420UNTILgm%AND?gp%ORgp%<MUSIC%+2ORgp%?-1=FALSE
13430=?gp%ANDgp%>MUSIC%+1ANDgp%?-1<>FALSE
13440
13450DEF FNprevious_gate(gp%)
13460LOCALC%,gm%
13470FORC%=0TO7:IFS_C%(C%)=S% gm%=gm%OR%1<<C%
13480NEXT
13490REPEAT
13500gp%-=1
13510WHILEgp%>MUSIC%ANDgp%?-1=FALSE:gp%-=2:ENDWHILE
13520UNTILgm%AND?gp%ORgp%<MUSIC%+2
13530=?gp%ANDgp%>MUSIC%+1
13540
13550DEF FNconflict(T%,S%,L%)
13560LOCALC%,co%
13570L%+=16
13580C%=7
13590REPEAT
13600co%=?GP%AND%1<<C%ANDS_C%(C%)=S%AND(?N%(C%)>>3)=L%
13610C%+=TRUE
13620UNTILco%ORC%<FALSE
13630=C%-(co%<>0)
13640
13650REM *********************************************************************** REM
13660REM REM
13670REM M U S I C T Y P E S E T T I N G R O U T I N E S REM
13680REM REM
13690REM *********************************************************************** REM
13700::
13710REM PROCEDURE: set_score(Starting position PX%)
13720REM
13730REM DESCRIPTION: Typeset music score from current bar to end of music/screen
13740REM No drawing is done as only the positions are calculated.
13750REM
13760REM EFFECTS: Stores gate X positions & types in PX%() & PTYPE%()
13770:
13780DEF PROCset_score(PX%)
13790WHILE GP%<GATE%
13800IF?GP% PROCset_notes(?GP%):GP%+=1 ELSEPROCset_attribute(GP%?1):GP%+=2
13810ENDWHILE:REM Until edge of screen or end of music
13820EP%=GP%:REM Last GP%, pointer to first undrawn gate
13830EX%=PX%:REM Last PX%, index to last position
13840PX%(PX%+1)=PX%(PX%)+PW%(PX%)+Pgap%:REM Appendation position after last symbol
13850IF PX%(PX%+1)>S_Width% PROCSetExtent(PX%(PX%+1)+100*Hi%) ELSE PROCSetExtent(S_Width%)
13860PXn%(NBars%)=PX%
13870PTYPE%(PX%+1)=Note%:REM Note type by default
13880ENDPROC
13890::
13900REM NOTE: The key signature is the only attribute that must be kept track of in order to ensure correct setting.
13910REM This is because a naturalising signature consists of the same number of naturals as accidentals in the previous key signature
13920:
13930DEF PROCset_attribute(A%)
13940IFA%ANDPTYPE%(PX%)+TRUE ELSEIFA%ANDPTYPE%(PX%) ENDPROC
13950LOCALT%:T%=%1:IFA%AND%1 ELSEREPEATT%=T%<<1:UNTILA%ANDT%
13960PX%(PX%+1)=PX%(PX%)+PW%(PX%)+Pgap%
13970PX%+=1
13980PTYPE%(PX%)=T%
13990CASE T% OF
14000WHENTime%:PW%(PX%)=20*Hi%
14010WHENKey%
14020IFA%AND56 T%=accidental%+(A%>>2AND%1)+2:SIG%(1)=A% ELSET%=accidental%+1:SWAPA%,SIG%(1):IFA%AND56 ELSEA%=8:T%+=1
14030PW%(PX%)=(A%>>3AND7)*(x%(T%)+X%(T%))
14040WHENClef%:PW%(PX%)=x%(clef%+3)+X%(clef%+3)
14050WHENBar%:PW%(PX%)=Hi%*4
14060ENDCASE
14070ENDPROC
14080
14090DEF PROCset_notes(G%)
14100LOCALlx0%,lx1%,ly0%,ly1%
14110LOCALC%,P%,R%,s%:REM Channel, Note prefix & remainder widths, Sprite
14120C%=-1
14130REPEAT
14140REPEATC%-=TRUE:UNTILG%AND%1<<C%
14150PROCbound_note(!N%(C%))
14160IFlx0%>P% P%=lx0%:REM Maximum prefix
14170IFlx1%>R% R%=lx1%:REM Maximum remainder
14180N%(C%)+=2:REM Pull from note queue
14190UNTIL(2<<C%)>G%:REM Until no more notes (1 bits)
14200PX%(PX%+1)=PX%(PX%)+PW%(PX%)+Pgap%+P%:REM Next position is previous position + width+gap+prefix of next note
14210PX%+=1:REM next note position index
14220PW%(PX%)=R%:REM Width of new note
14230PTYPE%(PX%)=Note%:REM Note type
14240ENDPROC
14250
14260DEF PROCbound_note(L%)
14270LOCALH%,S%,s%
14280H%=L%>>8AND&FF
14290IFL%AND&F8 S%=H%>>5ORL%<<3AND8 ELSES%=rest%ORH%>>5:REM Note/rest sprite
14300lx0%=x%(S%):REM Prefix width
14310lx1%=X%(S%):REM Suffix width
14320ly0%=y%(S%):REM Decender height
14330ly1%=Y%(S%):REM Ascender height
14340IFH%AND7 THEN
14350s%=accidental%ORH%AND7
14360lx0%+=x%(s%):REM Add accidental width
14370IFy%(s%)>ly0% ly0%=y%(s%):REM Lower ?
14380IFY%(s%)>ly1% ly1%=Y%(s%):REM Higher ?
14390ENDIF
14400IFH%AND24 s%=dot%+(H%>>3AND3):lx1%=x%(S%)+X%(s%):IFy%(s%)>ly0% ly0%=y%(s%):REM Dot adds suffix and may be lower
14410ENDPROC
14420::
14430DEF PROCposition_staves
14440LOCAL Y%,S%
14450Score_Height%=(PERC%+1+3*(STAVE%+1)+1)*Stave_Height%
14460Y%=-Score_Height%-Stave_Height%DIV2
14470IFPERC% FORS%=PERC%TO1STEP-1:Y%+=Stave_Height%:Y_STAVE%(STAVE%+S%)=Y%:NEXT
14480FOR S%=STAVE%TO0STEP-1:Y%+=3*Stave_Height%:Y_STAVE%(S%)=Y%:NEXT
14490ENDPROC
14500
14510DEF PROCsetup_staves
14520LOCAL O%
14530PROCposition_staves
14540ScoreWBlk%!handle%=ScoreWind_h%
14550SYS GetWindowState%,,ScoreWBlk%+handle%
14560ScoreWBlk%!y0%=ScoreWBlk%!y1%-Score_Height%-PaneHeight%
14570ScoreWBlk%!scx%=0:ScoreWBlk%!scy%=PaneHeight%/2
14580ScoreWBlk%!under%=-1
14590SYS OpenWindow%,,ScoreWBlk%+handle%
14600LHBAR%=0
14610ScoreClosed%=FALSE
14620Window%!handle%=ScoreWind_h%
14630SYS GetWindowState%,,Window%+handle%
14640IF TRANSCRIBE% PROCplace_top_panes(-1): PROCplace_bottom_panes(-1)
14650IF PLAYING% PROCCheckScroll
14660ENDPROC
14670
14680DEF PROCupdate_score(Window%!x0%,Window%!y0%,Window%!x1%,Window%!y1%)
14690Window%!handle%=ScoreWind_h%
14700SYS UpdateWindow%,,Window%+handle% TO R%
14710WHILE R%
14720CLG:PROCdraw_staves
14730SYS GetRectangle%,,Window%+handle% TO R%
14740ENDWHILE
14750ENDPROC
14760
14770REM PROCEDURE: draw_staves
14780REM
14790REM DESCRIPTION: Draw current stave structure
14800REM 1 stave for 1 voice
14810REM 2 staves for keyboard
14820REM 3 staves for 1 voice and keyboard
14830REM 4 staves for 4 voice chorus
14840:
14850DEF PROCdraw_staves
14860LOCAL Y%,T%,B%,S%,L%:REM Y%,T%,B%=Position & Y bounds of each stave, S%=Stave index, L%=Line position
14870LOCAL x%,y%,lx1%:REM Virtual coordinates of bottom left & top right of score window
14880LOCAL c%,t%,b%:REM Left edge of clip window and Y bounds
14890y%=Window%!y1%-Window%!scy%
14900x%=Window%!x0%-Window%!scx%
14910lx1%=x%+Score_Width%:IF lx1%>Clip%!x1% lx1%=Clip%!x1%
14920c%=Clip%!x0%:IFc%<x%+Hi% c%=x%+Hi%
14930b%=Clip%!y0%:t%=Clip%!y1%
14940IFPERC% THEN
14950FOR S%=STAVE%+1TOSTAVE%+PERC%
14960Y%=y%+Y_STAVE%(S%)
14970IFb%<=Y%ANDt%>=Y% LINEc%,Y%,lx1%,Y%
14980NEXT
14990ENDIF
15000FOR S%=0 TO STAVE%
15010Y%=y%+Y_STAVE%(S%):T%=Y%+Stave_Height%DIV2:B%=T%-Stave_Height%
15020IFb%<=T%ANDt%>=B% THEN
15030LINEc%,Y%,lx1%,Y%:REM Plot centre bar line
15040FORL%=Li%*2TOL%*2STEPL%
15050LINEc%,Y%+L%,lx1%,Y%+L%:REM Plot upper line
15060LINEc%,Y%-L%,lx1%,Y%-L%:REM Plot lower line
15070NEXT
15080ENDIF
15090NEXT
15100B%=Score_Width%
15110T%=Clip%!x1%-x%:IFT%<B% B%=T%:REM Right bound
15120T%=Clip%!x0%-x%:IFT%<0 T%=0:REM Left bound
15130IF NOT BADMODE% THEN
15140 PROCdraw_score(x%,y%,T%,B%):REM Draw symbols on stave
15150ELSE
15160 MOVE Window%!x0%, Window%!y1%-100
15170 PRINT "Cannot display score in this (256-colour) mode"
15180 ENDIF
15190ENDPROC
15200::
15210REM PROCEDURE: draw_score(Based X%,Y%, PX bounds A%,B%)
15220REM
15230REM DESCRIPTION: Write current section of music score that appears between A%
15240REM and B%
15250:
15260DEF PROCdraw_score(X%,Y%,A%,B%)
15270LOCALPX%
15280BAR%=0
15290REM must be subtle about redrawing last note (or other item) of score to prevent picking up rubbish data after end
15300IF A%>PX%(PXn%(NBars%)) THEN
15310 IF A%>PX%(PXn%(NBars%))+2*Pgap% ENDPROC ELSE A%=PX%(PXn%(NBars%))
15320 ENDIF
15330REM ensure bar numbers are always completely updated; but don't lose 1st-note draw
15340IF NBars%>2 THEN
15350WHILE PX%(PXn%(BAR%+2))<A% AND BAR%<=NBars%-1
15360 BAR%+=1
15370 ENDWHILE :REM get to drawstart quickly
15380 IF BAR%>NBars% ENDPROC
15390 PROCGetBarInfo(BAR%)
15400 IF GP%>=GATE% ENDPROC
15410 WHILEPX%(PX%+3)<A%ANDPX%<EX%
15420 PROCskip_gate
15430 ENDWHILE:REM Skip music until A%
15440ELSE
15450 PROCstart_music
15460ENDIF
15470WHILEPX%(PX%)<=B%ANDGP%<GATE%
15480IF?GP% PROCdraw_notes(?GP%):GP%+=1 ELSEPROCdraw_attribute(GP%?1):GP%+=2:IF PLAYING% PROCCheckQ
15490ENDWHILE
15500ENDPROC
15510
15520REM PROCEDURE: draw_notes(Gate G%)
15530REM
15540REM DESCRIPTION: Get notes from the indicated channel queues and draw the
15550REM particular types of note in the correct places on the stave.
15560REM One may be a rest, may have accidentals preceding it, may have
15570REM dots following it, may be tied
15580REM
15590REM ASSUMPTIONS: Gate is non-zero
15600REM
15610REM Join=?N%(C%)AND2
15620:
15630DEF PROCdraw_notes(G%)
15640LOCAL C%,x%,y%,s%,l%,NC0%,NC1%,lasty%,checkstagger%
15650PX%+=1:REM Move on to next position
15660x%=X%+PX%(PX%):REM Calculate gate column X (convert from work- to screen-coord)
15670checkstagger%=FALSE
15680C%=-1
15690REPEAT
15700REPEATC%-=TRUE:UNTILG%AND%1<<C%
15710NC0%=?N%(C%) : NC1%=N%(C%)?1 :REM fast local vars
15720y%=Y%+Y_STAVE%(S_C%(C%))
15730IFNC0%AND&F8 THEN
15740l%=(NC0%>>3)-16
15750IFABSl%>5 PROCsprite(ledger%+l%DIV2,x%,y%):REM Ledger lines
15760y%+=Li%*l%
15770s%=NC1%>>5ORNC0%<<3AND8
15780IF checkstagger% THEN
15790REM stagger next note if too close to one below
15800REM stagger fails sometimes if channels of 2 adjacent notes are not in draw order?
15810 IF ABS(lasty%-y%)<2*Li% PROCsprite(s%,x%+Pgap%,y%):checkstagger%=FALSE ELSE PROCsprite(s%,x%,y%)
15820ELSE
15830 PROCsprite(s%,x%,y%):checkstagger%=TRUE
15840 ENDIF
15850lasty%=y%
15860IFNC1%AND7 PROCsprite(accidental%ORNC1%AND7,x%-x%(s%),y%)
15870ELSE
15880s%=rest%ORNC1%>>5
15890PROCsprite(s%,x%,y%)
15900ENDIF
15910IFNC1%AND24 PROCsprite(dot%+(NC1%>>3AND3),x%+x%(s%),y%)
15920IFNC0%AND4 PROCsprite(tie%,x%,y%)
15930N%(C%)+=2:REM Pull from note queue
15940UNTIL(2<<C%)>G%:REM Until no more notes (1 bits)
15950ENDPROC
15960::
15970DEF PROCdraw_attribute(A%)
15980LOCALx%,N%
15990N%=TRUE:REPEATN%-=TRUE:UNTILA%AND%1<<N%
16000IFPTYPE%(PX%)EOR%1<<N% PX%+=1
16010x%=X%+PX%(PX%)
16020ONN%+1 PROCdraw_time_sig(A%),PROCdraw_key_sig(A%),PROCdraw_clef(A%),PROCdraw_slur(A%),PROCdraw_octave(A%),PROCdraw_bar_line(A%)
16030ENDPROC
16040
16050DEF PROCPutBarInfo(bar%)
16060REM setup the various pointers to arrays at the start of this bar
16070FOR n%=0 TO 7
16080 BPn%(n%,bar%)=N%(n%)
16090 NEXT
16100PXn%(bar%)=PX%
16110GPn%(bar%)=GP%
16120FOR n%=0 TO Max_Stave%
16130 CLEFn%(n%,bar%)=CLEF%(n%)
16140 NEXT
16150SIGn%(0,bar%)=SIG%(0)
16160SIGn%(1,bar%)=SIG%(1)
16170ENDPROC
16180
16190DEF PROCGetBarInfo(bar%)
16200REM get the values of the data at the start of this bar
16210FOR n%=0 TO 7
16220 N%(n%)=BPn%(n%,bar%)
16230 NEXT
16240PX%=PXn%(bar%)
16250GP%=GPn%(bar%)
16260FOR n%=0 TO Max_Stave%
16270 CLEF%(n%)=CLEFn%(n%,bar%)
16280 NEXT
16290SIG%(0)=SIGn%(0,bar%)
16300SIG%(1)=SIGn%(1,bar%)
16310ENDPROC
16320
16330DEF PROCSetupBarStarts(bar%)
16340REM find pointers to various bits at the starts of all bars through
16350REM the music. Start at bar%
16360REM this is just to speed up redraw, avoiding all the note-skipping
16370LOCAL last%
16380BAR%=bar%
16390IF bar%>0 PROCGetBarInfo(bar%) ELSE PROCstart_music
16400last%=BAR%
16410PROCPutBarInfo(BAR%)
16420WHILE GP%<GATE%
16430 IF?GP% THEN
16440 PROCskip_notes(?GP%):GP%+=1
16450 ELSE
16460 PROCskip_attribute(GP%?1):GP%+=2
16470 IF BAR%<>last% last%=BAR% : PROCPutBarInfo(BAR%)
16480 ENDIF
16490 ENDWHILE
16500BAR%+=1
16510PROCPutBarInfo(BAR%)
16520NBars%=BAR%
16530ENDPROC
16540
16550DEF PROCskip_gate
16560IF?GP% PROCskip_notes(?GP%):GP%+=1 ELSEPROCskip_attribute(GP%?1):GP%+=2
16570ENDPROC
16580
16590DEF PROCskip_notes(G%)
16600PX%+=1
16610LOCALC%:C%=-1
16620REPEATREPEATC%+=1:UNTILG%AND%1<<C%:N%(C%)+=2:UNTIL(2<<C%)>G%
16630ENDPROC
16640
16650DEF PROCskip_attribute(A%)
16660LOCALT%
16670T%=%1:IFA%AND%1 ELSEREPEATT%=T%<<%1:UNTILA%ANDT%
16680CASET%OF
16690WHENTime%,Key%:SIG%(T%-1)=A%
16700WHENClef%:CLEF%(A%>>6)=A%>>3AND3
16710WHENBar%:BAR%+=1
16720ENDCASE
16730IFT%EORPTYPE%(PX%) PX%+=1
16740ENDPROC
16750
16760DEF PROCback_gate
16770IFGP%?-2 GP%-=1:PROCback_notes(?GP%) ELSEGP%-=2:PROCback_attribute(GP%?1)
16780ENDPROC
16790
16800DEF PROCback_notes(G%)
16810PX%-=1
16820LOCALC%:C%=TRUE
16830REPEATREPEATC%-=TRUE:UNTILG%AND%1<<C%:N%(C%)-=2:UNTIL(2<<C%)>G%
16840ENDPROC
16850
16860DEF PROCback_attribute(A%)
16870IFA%=Bar% BAR%-=1
16880IFA%ANDPTYPE%(PX%+1)+TRUE ELSEIFA%ANDPTYPE%(PX%+1) ENDPROC
16890PX%-=1
16900ENDPROC
16910
16920DEF PROCdraw_time_sig(A%)
16930SIG%(0)=A%
16940LOCALS%,B$,D$,w%
16950B$=STR$((A%>>1AND15)+1)
16960D$=STR$(%1<<(A%>>5)-1)
16970w%=x%
16980IFLENB$<2 w%+=PW%(PX%)>>2
16990IFLEND$<2 x%+=PW%(PX%)>>2
17000FORS%=0TOSTAVE%+PERC%
17010MOVEw%,Y%+Y_STAVE%(S%)+Li%*4-Vi%:PRINTB$
17020MOVEx%,Y%+Y_STAVE%(S%)-Vi%:PRINTD$
17030NEXT
17040ENDIF
17050ENDPROC
17060
17070DEF PROCdraw_key_sig(A%)
17080LOCALS%,C%,N%,a%,W%
17090IFA%AND56 SIG%(1)=A% ELSESWAPA%,SIG%(1):a%=accidental%+1:REM Change to C Major uses naturals in old key sig positions
17100N%=(A%>>3AND7)-1
17110IFN%>=0 THEN
17120A%=A%>>2AND%1:REM 0-Sharp or 1-flat signature
17130IFa% ELSEa%=accidental%+2+A%
17140W%=x%(a%)+X%(a%)
17150x%+=x%(a%)
17160FOR C%=0 TO N%
17170FOR S%=0TOSTAVE%
17180PROCsprite(a%,x%,Y%+Y_STAVE%(S%)+Li%*Key_Y%(CLEF%(S%),A%,C%))
17190NEXT
17200x%+=W%
17210NEXT
17220ENDIF
17230ENDPROC
17240
17250DEF PROCdraw_clef(A%)
17260LOCALS%
17270S%=A%>>6
17280CLEF%(S%)=A%>>3AND3
17290IFS%<=STAVE% PROCsprite(clef%+CLEF%(S%),x%,Y%+Y_STAVE%(S%))
17300ENDPROC
17310
17320DEF PROCdraw_slur(A%)
17330ENDPROC
17340
17350DEF PROCdraw_octave(A%)
17360ENDPROC
17370
17380DEF PROCdraw_bar_line(A%)
17390BAR%+=1
17400FORS%=0TOSTAVE%+PERC%
17410PROCsprite(bar%,x%,Y%+Y_STAVE%(S%))
17420NEXT
17430IF BAR%MOD5=0 THEN
17440 MOVE x%+Hi%,Y%+Y_STAVE%(0)+Stave_Height%+2*Vi%
17450 PRINT STR$(BAR%)
17460 ENDIF
17470IF(STAVE%+1)AND2 THEN
17480LOCAL y%
17490MOVEx%+Hi%,Y%+Y_STAVE%(STAVE%)+Stave_Height%DIV2
17500y%=Y_STAVE%(STAVE%-1)-Y_STAVE%(STAVE%)-Stave_Height%
17510DRAW BY 0,y% : MOVE BY Hi%,0 : DRAW BY 0,-y% : REM Connect keyboard staves
17520ENDIF
17530ENDPROC
17540
17550DEF PROCput_down
17560LOCALC%,PX%,X%,Y%,S%,s%
17570SYS "Hourglass_On"
17580GP%=SCRIBE%(sgp%)
17590FORC%=0TO7:N%(C%)=SCRIBE%(C%):NEXT
17600PX%=SCRIBE%(posx%)
17610X%=SCRIBE%(sx%)
17620Y%=SCRIBE%(sy%)
17630s%=SCRIBE%(sprite%)
17640S%=SCRIBE%(stave%)
17650C%=SCRIBE%(sc%)
17660PROCrelease:REM undraw sprite being dragged
17670IFs%<accidental% PROCput_note ELSEIFs%<clef% PROCput_accidental ELSEIFs%<dot% PROCput_clef ELSEIFs%<bar% PROCput_dot ELSEIFs%<time% PROCput_bar ELSEIFs%<key% PROCput_time ELSEIFs%<tie% PROCput_key ELSEPROCput_tie
17680GP%=SCRIBE%(sgp%)
17690FORC%=0TO7:N%(C%)=SCRIBE%(C%):NEXT
17700PROCarrange_stave(SCRIBE%(stave%))
17710IF PX%(EX%)+200*Hi%>S_Width% PROCSetExtent(PX%(EX%)+200*Hi%)
17720IF NOT CHANGED% THEN
17730 CHANGED%=TRUE
17740 $Updated%="YES"
17750 PROCUpdateTitle(FNGetStr(ScoreTitle%)+" *")
17760 ENDIF
17770SYS "Hourglass_Off"
17780ENDPROC
17790
17800DEF PROCput_note
17810LOCALL%,E%
17820L%=SCRIBE%(line%)
17830IFX%=PX%(PX%+1) THEN
17840IFs%>=rest% L%=-16
17850E%=GP%=EP%
17860IFE% PROCinsert_gate(1) ELSEC%=FNconflict(Note%,S%,L%)
17870IFE%ORC%=TRUE THEN
17880C%=FNallocate_channel(S%)
17890IFC%>=0 THEN
17900PROCinsert_note(C%)
17910PROCnote_type(C%,s%AND7)
17920IFs%<rest% PROCnote_line(C%,L%):PROCnote_stem(C%,s%AND8)
17930s%=!N%(C%)
17940IFE% PROCset_score(PX%):X%=PX%(PX%+1)
17950PROCupdate_note(S%,s%,X%,Y%)
17960ENDIF
17970ELSE
17980s%=!N%(C%)
17990PROCdelete_note(C%)
18000IF?GP% THEN
18010PROCupdate_note(S%,s%,X%,Y%)
18020ELSE
18030PROCdelete_gate(1)
18040IFGP%?-2=0 WHILE?GP%=0ANDGP%<EP%:PROCdelete_gate(2):ENDWHILE
18050PROCrescore(PX%)
18060ENDIF
18070ENDIF
18080ELSE
18090IFX%>PX%(PX%+1) PROCskip_gate
18100PROCinsert_gate(1)
18110C%=FNallocate_channel(S%)
18120PROCinsert_note(C%)
18130PROCnote_type(C%,s%AND7)
18140IFs%<rest% PROCnote_line(C%,L%):PROCnote_stem(C%,s%AND8)
18150PROCrescore(PX%)
18160ENDIF
18170ENDPROC
18180
18190DEF PROCput_tie
18200IF?N%(C%)AND4 THEN
18210PROCnote_tie(C%,FALSE)
18220PROCupdate_score(X%+24,Y%+12,X%+70,Y%+24)
18230ELSE
18240LOCALI%
18250PROCnote_tie(C%,TRUE)
18260PROCskip_notes(?GP%):GP%+=1
18270PROCarrange_stave(S%)
18280GP%=SCRIBE%(sgp%)
18290FORI%=0TO7:N%(I%)=SCRIBE%(I%):NEXT
18300IFN%(C%)+2>=FINE%(C%)OR(?N%(C%)EORN%(C%)?2)AND&F8 PROCnote_tie(C%,FALSE) ELSEPROCupdate_score(X%+24,Y%+12,X%+70,Y%+24)
18310ENDIF
18320ENDPROC
18330
18340DEF PROCput_accidental
18350LOCALA%,a%
18360a%=N%(C%)?1AND7
18370A%=s%AND7
18380IFA%=a% A%=0
18390PROCnote_accidental(C%,A%)
18400IFA%*a% PROCupdate_score(X%-34,Y%-24,X%+16,Y%+52) ELSEPROCrescore(PX%)
18410ENDPROC
18420
18430DEF PROCput_dot
18440LOCALD%,d%
18450d%=N%(C%)?1>>3AND3
18460D%=s%AND3
18470IFD%=d% D%=0
18480PROCnote_dots(C%,D%)
18490IFD%*d% THEN
18500s%=N%(C%)?1>>5OR?N%(C%)<<3AND8:IF?N%(C%)AND&F8 ELSEs%=s%ORrest%
18510X%+=x%(s%)
18520PROCupdate_score(X%+24,Y%-8,X%+50,Y%)
18530ELSE
18540PROCrescore(PX%)
18550ENDIF
18560ENDPROC
18570
18580DEF PROCput_clef
18590LOCALc%
18600c%=s%AND3
18610IFGP%=EP%OR?GP%OR(GP%?1AND%111)<>Clef% THEN
18620PROCinsert_gate(2)
18630PROCclef(S%,c%)
18640ELSE
18650WHILE ?GP%=0AND(GP%?1AND%111)=Clef%AND(GP%?1>>6)<>S%ANDGP%<EP%
18660PROCskip_gate
18670ENDWHILE
18680IFGP%<EP%AND?GP%=0AND(GP%?1AND%111)=Clef%AND(GP%?1>>6)=S% THEN
18690IF(GP%?1>>3AND3)<>c% PROCclef(S%,c%) ELSEPROCdelete_gate(2)
18700ELSE
18710PROCinsert_gate(2)
18720PROCclef(S%,c%)
18730ENDIF
18740ENDIF
18750PROCrescore(PX%)
18760ENDPROC
18770
18780DEF PROCput_key
18790IFGP%=EP%OR?GP%OR(GP%?1AND%11)<>Key% THEN
18800PROCinsert_gate(2)
18810PROCkey_sig(KEY_SIG%(0),KEY_SIG%(1))
18820ELSE
18830IFKEY_SIG%(1)>0AND(GP%?1>>2AND%1)<>KEY_SIG%(0)OR(GP%?1>>3)<>KEY_SIG%(1) PROCkey_sig(KEY_SIG%(0),KEY_SIG%(1)) ELSEPROCdelete_gate(2)
18840ENDIF
18850PROCrescore(PX%)
18860ENDPROC
18870
18880DEF PROCput_time
18890IFGP%=EP%OR?GP%OR(GP%?1AND%1)<>Time% THEN
18900PROCinsert_gate(2)
18910PROCtime_sig(TIME_SIG%(0),TIME_SIG%(1))
18920PROCrescore(PX%)
18930ELSE
18940IF(GP%?1>>1AND15)<>TIME_SIG%(0)OR(GP%?1>>5)<>TIME_SIG%(1) THEN
18950PROCtime_sig(TIME_SIG%(0),TIME_SIG%(1))
18960PROCupdate_score(X%,-Score_Height%,X%+48,0)
18970ELSE
18980PROCdelete_gate(2)
18990PROCrescore(PX%)
19000ENDIF
19010ENDIF
19020ENDPROC
19030
19040DEF PROCput_bar
19050IF(?GP%ORGP%?1<>Bar%)ANDX%>PX%(PX%+1)ANDGP%<EP% PROCskip_gate ELSEIFGP%?-2=0ANDGP%?-1=Bar% PROCback_gate
19060IFGP%>MUSIC% THEN
19070IFGP%<EP%AND?GP%=0ANDGP%?1=Bar% THEN
19080PROCdelete_gate(2)
19090NBars%-=1
19100WHILE?GP%=0ANDGP%<EP%
19110PROCdelete_gate(2)
19120ENDWHILE
19130PROCrescore(PX%)
19140ELSE
19150IFGP%?-2 PROCinsert_gate(2):PROCbar:NBars%+=1:PROCrescore(PX%)
19160ENDIF
19170ENDIF
19180ENDPROC
19190
19200DEF PROCrescore(px%)
19210IFpx% ELSEPROCstart_music:REM Bar position assumed set, but reset if px%=0
19220PROCset_score(px%)
19230IF px%=0 PROCSetupBarStarts(0) ELSE PROCSetupBarStarts(FNFindBar(px%))
19240PROCupdate_score(PX%(px%),-Score_Height%,Score_Width%,0)
19250ENDPROC
19260
19270DEF PROCupdate_note(S%,N%,X%,Y%)
19280LOCALlx0%,lx1%,ly0%,ly1%
19290PROCbound_note(N%):REM Get minimum bounding rectangle of note
19300IFN%AND4 lx1%=X%(tie%):IFY%(tie%)>ly1% ly1%=Y%(tie%):REM Tie sets suffix and may be higher (Tie bounds only apply to updating)
19310IFABS(Y%-Y_STAVE%(S%))>Li%*5 THEN
19320IFx%(dot%)>lx0% lx0%=x%(dot%)
19330IFX%(dot%)>lx1% lx1%=X%(dot%):REM Extend to ledger extent
19340ENDIF
19350ly1%+=Y%:Y%-=ly0%:ly0%=Y_STAVE%(S%):REM Stave position
19360IFly1%<ly0%-Li%*5 ly1%=ly0%-Li%*5 ELSEIFY%>ly0%+Li%*5 Y%=ly0%+Li%*5:REM If ledger lines will be drawn increase vertical update space
19370PROCupdate_score(X%-lx0%,Y%,X%+lx1%+Pgap%,ly1%):REM Update symbol space
19380ENDPROC
19390
19400DEF PROCattach(s%,V%)
19410SCRIBE%(sx%)=TRUE:REM New position (Maintain Y for orientation maintainance)
19420SCRIBE%(drawn%)=FALSE
19430IFs%<rest%ANDSCRIBE%(sprite%)<rest% s%=s%AND7ORSCRIBE%(sprite%)AND8:REM Maintain note orientation
19440SCRIBE%(sprite%)=s%
19450SCRIBE%(valid%)=V%
19460SCORING%=TRUE
19470ENDPROC
19480::
19490DEF PROCrelease
19500IFSCORING%ANDSCRIBE%(drawn%) THEN
19510LOCALWindow%:Window%=Icon%+100:REM Don't corrupt window workspace
19520PROCfloat(SCRIBE%(sprite%),SCRIBE%(sx%),SCRIBE%(sy%)):REM Undraw sprite
19530IFSCRIBE%(sprite%)<rest% IFABSSCRIBE%(line%)>5 PROCfloat(ledger%+SCRIBE%(line%)DIV2,SCRIBE%(sx%),Y_STAVE%(SCRIBE%(stave%))):REM Undraw ledger lines
19540SCRIBE%(sx%)=TRUE:REM Continue drawing of symbol
19550SCRIBE%(drawn%)=FALSE
19560ENDIF
19570ENDPROC
19580::
19590REM PROCEDURE: scribe(At X%,Y%)
19600REM
19610REM DESCRIPTION: Draw current music symbol at position over score window
19620:
19630DEF PROCscribe(X%,Y%)
19640LOCALS%,L%,C%
19650Window%!handle%=ScoreWind_h%
19660SYS GetWindowState%,,Window%+handle%
19670X%-=Window%!x0%-Window%!scx%
19680Y%-=Window%!y1%-Window%!scy%
19690PROCproximate(SCRIBE%(valid%))
19700IFX%<>SCRIBE%(sx%)ORY%<>SCRIBE%(sy%) THEN
19710LOCALA%
19720A%=TRUE
19730IF(SCRIBE%(valid%)AND112)=96 A%=FALSE:IFC%>=0 A%=SCRIBE%(valid%)AND8ORS%<=STAVE%AND?N%(C%)AND&F8
19740IFA% THEN
19750IFSCRIBE%(drawn%) THEN
19760PROCfloat(SCRIBE%(sprite%),SCRIBE%(sx%),SCRIBE%(sy%))
19770IFSCRIBE%(sprite%)<rest% IFABSSCRIBE%(line%)>5 PROCfloat(ledger%+SCRIBE%(line%)DIV2,SCRIBE%(sx%),Y_STAVE%(SCRIBE%(stave%)))
19780ELSE
19790SCRIBE%(drawn%)=TRUE
19800ENDIF
19810IFSCRIBE%(sprite%)<rest% THEN
19820IFABSL%>5 PROCfloat(ledger%+L%DIV2,X%,Y_STAVE%(S%))
19830IFY%<>SCRIBE%(sy%) SCRIBE%(sprite%)=SCRIBE%(sprite%)AND7OR8ANDY%>SCRIBE%(sy%)
19840ENDIF
19850SCRIBE%(sx%)=X%
19860SCRIBE%(sy%)=Y%
19870SCRIBE%(stave%)=S%
19880IFS%<=Max_Stave% SCRIBE%(sclef%)=CLEF%(S%) ELSESCRIBE%(sclef%)=CLEF%(Max_Stave%)
19890SCRIBE%(line%)=L%
19900SCRIBE%(posx%)=PX%
19910SCRIBE%(sgp%)=GP%
19920SCRIBE%(sc%)=C%
19930FORC%=0TO7:SCRIBE%(C%)=N%(C%):NEXT
19940PROCfloat(SCRIBE%(sprite%),X%,Y%)
19950ENDIF
19960ENDIF
19970ENDPROC
19980::
19990REM PROCEDURE: proximate(Positions valid mask V%)
20000REM
20010REM DESCRIPTION: Will set X%,Y% to be the nearest valid position to the point
20020REM X%,Y% supplied (all in stave coordinates) and return the
20030REM associated position index PX%.
20040REM
20050REM A mask is supplied to indicate the valid positions:
20060REM V% bit 0 -> Valid Time signature positions
20070REM 1 -> Valid Key signature positions
20080REM 2 -> Valid Clef positions
20090REM 3 -> At note positions
20100REM 4 -> In between notes
20110REM 5 -> To nearest stave line (else nearest stave)
20120REM 6 -> To nearest symbol present
20130REM 7 -> On bar line if near
20140REM
20150REM EFFECTS: Sets S%=Stave, L%=Line(-15..15), C%=Channel of note, GP%,N%()=Gate & note pointers, PX%=PX%() index, X%,Y% position
20160:
20170DEF PROCproximate(V%):REM (V%, var X%,Y%) = (S%,L%,PX%)
20180LOCALd%,D%:REM Distance previous¤t
20190LOCALpx%,x%:REM Previous PX%, Between note X position
20200LOCALgp%:REM Previous gate pointer, key sig
20210IF NBars%>4 THEN BAR%=LHBAR% ELSE BAR%=0
20220PROCGetBarInfo(BAR%) : REM Start from start of page
20230D%=4*S_Width%:REM A suitably excessive distance
20240IF V%AND7 THEN
20250LOCALT%:REM Type mask
20260X%-=X%(clef%)>>1:REM Pointer points to centre of Clef/Key/Time
20270T%=-V%<<1AND6:REM Valid Clef/Key/Time: Work out types to ignore after bars
20280REPEAT
20290PROCsavp:REM Save previous distance, pointers etc
20300REPEATPROCskip_gate:UNTILPTYPE%(PX%)ANDBar%ORGP%=EP%:REM Move to next bar PX%
20310IFPTYPE%(PX%)ANDBar% THEN
20320WHILEPTYPE%(PX%+1)ANDT%ANDGP%<EP%
20330REPEATPROCskip_gate:UNTILGP%=EP%ORGP%?1AND%111EOR%100:REM Skip ALL clefs
20340ENDWHILE:REM Skipping any T% types
20350D%=ABS(X%-PX%(PX%+1)):REM Calculate distance to this position
20360ELSE
20370D%=2*S_Width%:REM No more bars
20380ENDIF
20390UNTILD%>d%ORGP%=EP%:REM Until distance increases or no more positions
20400IFd%<D% PROCrstp:REM Previous position may be closer
20410X%=PX%(PX%+1):REM Set as position
20420ELSE
20430X%-=X%(2)>>1:REM Pointer points to centre of note
20440REPEAT
20450PROCsavp:REM Save previous distance, pointers etc
20460PROCskip_gate:REM Next symbol
20470WHILEPTYPE%(PX%+1)ANDGP%<EP%:PROCskip_gate:ENDWHILE:REM Skip any attribute types
20480D%=ABS(X%-PX%(PX%+1)):REM Calculate distance to this note position
20490UNTILD%>d%ORGP%=EP%:REM Until distance increases or no more notes
20500IFd%<D%ORGP%=EP%ANDV%AND64 PROCrstp:REM Previous position may be closer (or the last valid note position for an accidental/dot to appear)
20510IFV%AND16ANDGP%<EP% THEN
20520REM Valid between notes (if before last position)
20530IFX%<PX%(PX%+1)ORX%>PX%(EX%) THEN
20540IFPTYPE%(PX%) x%=PX%(PX%)+PW%(PX%)-X%(2) ELSEx%=PX%(PX%):REM Before current: between previous. (also if after last note)
20550x%=x%+PX%(PX%+1)>>1
20560IFV%AND128 x%+=Hi%*4:IFPTYPE%(PX%)=Bar% x%=PX%(PX%)
20570ELSE
20580x%=PX%(PX%+1)+PX%(PX%+2)>>1:REM After current: between next.
20590IFV%AND128 x%+=Hi%*4:IFPTYPE%(PX%+2)=Bar% x%=PX%(PX%+2)
20600ENDIF
20610IFV%AND8ANDABS(PX%(PX%+1)-X%)<ABS(x%-X%) X%=PX%(PX%+1) ELSEX%=x%:REM If on notes also valid then find closest position
20620ELSE
20630X%=PX%(PX%+1):REM Only at notes valid: closest position
20640ENDIF
20650ENDIF
20660C%=-1:REM Initially no channel
20670L%=0:REM Initially centre line
20680IFV%AND64AND?GP%>0 THEN
20690REM To nearest symbol at gate (just note at the moment !!)
20700LOCALG%,c%:REM Gate mask copy, Previous channel counter
20710c%=C%
20720d%=2*S_Height%:REM A suitably excessive distance
20730G%=?GP%
20740REPEAT
20750REPEATC%-=TRUE:UNTILG%AND%1<<C%
20760D%=Y%-Y_STAVE%(S_C%(C%))
20770IF?N%(C%)AND&F8 D%=ABS(D%-Li%*((?N%(C%)>>3)-16)) ELSED%=ABSD%:REM Distance to note/rest
20780IFD%<d% d%=D%:c%=C%
20790UNTIL(2<<C%)>G%
20800C%=c%:REM Nearest note
20810S%=S_C%(C%)
20820IF?N%(C%)AND&F8 L%=(?N%(C%)>>3)-16
20830ELSE
20840LOCALMS%:REM Highest stave number
20850MS%=STAVE%:IFV%AND6 ELSEMS%+=PERC%:REM Clef & Key not on percussion.
20860S%=-1:REM Before first stave
20870D%=2*S_Height%:REM A suitably excessive distance
20880REPEAT
20890d%=D%:REM Copy previous distance
20900S%+=1:REM Next stave
20910D%=ABS(Y%-Y_STAVE%(S%)):REM Distance between point and current stave
20920UNTILD%>d%ORS%=MS%:REM Until distance increases or no more staves
20930S%+=d%<D%:REM Previous stave was closest unless last¤t stave is nearer
20940IFV%AND32THEN
20950IFS%=STAVE%+1 S%+=Y%>=Y_STAVE%(STAVE%)-Li%*16:REM Ledger lines take priority over percussion lines
20960IFS%<=STAVE% THEN
20970L%=(Y%-Y_STAVE%(S%))/Li%+16.75:L%-=16:REM Y% is L% Lines away from centre stave
20980IFABSL%>15 L%=15*SGNL%:REM No further than 15 lines away
20990ENDIF
21000ENDIF
21010ENDIF
21020Y%=Y_STAVE%(S%)+L%*Li%:REM Centre line + offset
21030ENDPROC
21040::
21050
21060DEF PROCsavp:n%()=N%():d%=D%:px%=PX%:gp%=GP%:clef%()=CLEF%():sig%()=SIG%():ENDPROC
21070
21080DEF PROCrstp:N%()=n%():PX%=px%:GP%=gp%:CLEF%()=clef%():SIG%()=sig%():ENDPROC
21090
21100DEF PROCSetTempo(T%)
21110Tempo%=T%
21120SYS Sound_QTempo,Tempo%(T%)*128*4096DIV6000
21130ENDPROC
21140
21150REM given x position, return number of bar
21160DEF FNFindBar(xpos%)
21170LOCAL bar%
21180bar%=0
21190WHILE PX%(PXn%(bar%))<xpos% AND bar%<=NBars%:bar%+=1:ENDWHILE
21200IF bar%>0 bar%-=1
21210=bar%
21220
21230DEF PROCplay_start
21240LOCALC%,n%
21250SYS Sound_Configure,8
21260REM set play start bar PBAR% to start of currently displayed window (scx%)
21270Window%!handle%=ScoreWind_h%
21280SYS GetWindowState%,,Window%+handle%
21290PBAR%=FNFindBar(Window%!scx%)
21300PP%=GPn%(PBAR%)
21310FOR n%=0 TO 7
21320 P%(n%)=BPn%(n%,PBAR%)
21330 NEXT
21340FORC%=0TO3
21350PCLEF%(C%)=Clef%(CLEFn%(C%,PBAR%))
21360NEXT
21370PROCplay_key_sig(SIGn%(1,PBAR%))
21380PLAYING%=TRUE
21390SCROLLING%=TRUE
21400Beats%=((SIGn%(0,PBAR%)>>1AND&F)+1)*Length%(SIGn%(0,PBAR%)>>3AND%11100)
21410Q%()=Beats%
21420TIE%=&FF
21430B2%=&10000
21440SYS Sound_QBeat,Beats%
21450SYS Sound_QSchedule,Beats%,Sch%ORSound_QTempo,Tempo%(Tempo%)*128*4096DIV6000
21460C%=Beats%/50*&1000:IFC%>&7FFF C%=&7FFF
21470SYS Sound_QTempo,C%
21480MIDI_OFF%(0,0) = -1
21490MIDI_OFF%(1,0) = -1
21500ENDPROC
21510
21520REM flush sound queue and ensure note_offs are sent to midi channels
21530DEFPROCplay_stop
21540LOCAL note%, n%
21550PLAYING%=FALSE
21560SYS Sound_QInit
21570IF MIDIpresent% THEN
21580 FOR n%=0 TO 1
21590 note%=0
21600 WHILE (MIDI_OFF%(n%,note%) > 0)
21610 SYS MIDI_TxCommand%, MIDI_OFF%(n%,note%)
21620 note%+=1
21630 ENDWHILE
21640 NEXT
21650 REM reset end markers
21660 MIDI_OFF%(0,0) = -1
21670 MIDI_OFF%(1,0) = -1
21680 ENDIF
21690ENDPROC
21700
21710REM This should give a reasonable approximation to a smooth scroll
21720DEF PROCCheckScroll
21730LOCAL PosX%, WindowWidth%, BarPos%, LastScroll%,ThisScroll%
21740IF ScoreClosed% THEN
21750 IF NOT PLAYING% SCROLLING%=FALSE
21760 ENDPROC
21770 ENDIF
21780IF BADMODE% OR PBAR%<=2 ENDPROC
21790IF PLAYING% PROCCheckQ
21800IF PLAYING% SBAR%=PBAR%-2 ELSE B1%=B2%:B2%=BEAT:IFB2%<B1%:SBAR%+=1
21810IF SBAR%>=NBars% OR SBAR%>PBAR%+2 SCROLLING%=FALSE:ENDPROC
21820SYS GetWindowState%,,ScoreWBlk%+handle%
21830WindowWidth%=ScoreWBlk%!x1%-ScoreWBlk%!x0%
21840LastScroll%=ScoreWBlk%!scx%
21850PosX%=PX%(PXn%(SBAR%))
21860BarPos%=(PX%(PXn%(SBAR%+1))-PosX%)*BEAT DIV Beats%
21870IF BarPos%*2<Pgap% ENDPROC :REM if small amount into bar can scroll to wrong bar (sync with play)
21880PosX%+=BarPos%
21890ThisScroll% = PosX%-WindowWidth%DIV2
21900IF ThisScroll%<=0 ENDPROC
21910IF ThisScroll%<>LastScroll% THEN
21920 REM auto-scroll
21930 REM divide scroll into small bits to make it appear smooth
21940 ScoreWBlk%!scx%=(LastScroll%+ThisScroll%)/2
21950 SYS OpenWindow%,,ScoreWBlk%+handle%
21960 ScoreWBlk%!scx%=ThisScroll%
21970 SYS OpenWindow%,,ScoreWBlk%+handle%
21980 LHBAR%=FNFindBar(ThisScroll%)-1 :REM the bar number at the left of the window
21990 IF PLAYING%=FALSE THEN
22000REM stop scrolling at end of music
22010 IF ScoreWBlk%!scx%<=LastScroll% SCROLLING%=FALSE
22020 ENDIF
22030 ENDIF
22040ENDPROC
22050
22060DEF PROCplay_bar
22070LOCALC%,L%,I%,D%,S%,Q%,T%,B%,A%:REM Vars for play_bar & play_notes
22080LOCAL f
22090MIDI_Notenum%=0
22100Q%()=Beats%:REM Reset stave queue counters for next bar
22110B%=PBAR%
22120Accidental%()=0:REM No incidental accidentals initially
22130First%=TRUE
22140WHILEB%=PBAR%ANDPP%<GATE%
22150IF?PP% PROCplay_notes(?PP%):PP%+=1 ELSEPROCplay_attribute(PP%?1):PP%+=2
22160First%=FALSE
22170ENDWHILE
22180IFPP%>=GATE% PLAYING%=FALSE:SBAR%+=1
22190ENDPROC
22200
22210REM Notes for each gate start playing at Q%; the latest Q%() of the staves on
22220REM which notes are to play. This is because all notes in a gate play at the
22230REM same time and cannot start before the shortest of the notes preceding them
22240REM have finished.
22250REM Q% is the time at which the notes in the gate are played
22260REM Q%(S%) is the time at the end of the shortest note played on stave S%.
22270REM QI%(S%) is the current shortest note beats interval on stave S%
22280
22290DEF PROCplay_notes(G%)
22300LOCAL note%, packed%, Qoff%,xtraVol,tempo
22310tempo = 5 * 128 * Tempo%(Tempo%) / 6000
22320Q%=FALSE:C%=TRUE
22330REPEATREPEATC%-=TRUE:UNTILG%AND%1<<C%
22340IFQ%(S_C%(C%))>Q% Q%=Q%(S_C%(C%))
22350UNTIL(2<<C%)>G%
22360QI%()=&10000:C%=TRUE
22370REPEAT
22380 IF First% xtraVol=1.01 ELSE xtraVol=1.0 :REM a little dynamics. stress 1st beat in bar very slightly
22390 REPEATC%-=TRUE:UNTILG%AND%1<<C%
22400 IF INT(xtraVol*Volume%(Volumes%(C%)))>&7F xtraVol=1.0 :REM ensure no overflow
22410 T%=?P%(C%):D%=P%(C%)?1:I%=D%>>3:S%=S_C%(C%):L%=T%>>3:A%=0
22420 IFL%ANDS%<=STAVE% THEN
22430 IFD%AND7 Accidental%(S%,L%)=D%AND7
22440 A%=Accidental%(S%,L%):L%+=PCLEF%(S%):IFA% ELSEA%=Key%(L%MOD7):REM If no accidental then revert to key signature
22450 ENDIF
22460 IFTIE%AND%1<<C% THEN
22470 D%=Duration%(Tempo%)?I%:IFT%AND4 TIE%=TIE%ANDNOT(%1<<C%):T%=P%(C%)+1:REPEATT%+=2:D%+=Duration%(Tempo%)?(?T%>>3):UNTILT%>FINE%(C%)OR4ANDNOTT%?TRUE:IFD%>254 D%=254
22480 IFL% THEN
22490 SOUNDC%+1,INT(xtraVol*Volume%(Volumes%(C%)))OR&100,Line(L%)+Aoff(A%),D%,Q%
22500 IF MIDIpresent% THEN
22510 IF (MIDIChannel%(C%)>=1) AND (MIDIChannel%(C%)<=16) THEN
22520 note% = 12 + INT(((Line(L%)+Aoff(A%))/&1000) * 12)
22530 packed% = (MIDIChannel%(C%)-1) OR (note%<<8) OR INT(xtraVol*(Volume%(Volumes%(C%)))<<16)
22540 MIDI_OFF%(PBAR%MOD2,MIDI_Notenum%) = M_NoteOff% OR packed%
22550 MIDI_Notenum% += 1
22560 MIDI_OFF%(PBAR%MOD2,MIDI_Notenum%) = -1 :REM end marker
22570 REM D% = no. of 5 centisecs
22580 Qoff% = Q% + D% * tempo
22590 IF Qoff% > Q% + 15 Qoff% -= 10 :REM ensure note is off after start and before next note start
22600 SYS Sound_QSchedule,Q%, Sch% OR MIDI_TxCommand%, M_NoteOn% OR packed%
22610 SYS Sound_QSchedule,Qoff%, Sch% OR MIDI_TxCommand%, M_NoteOff% OR packed%
22620 ENDIF
22630 ENDIF
22640 ENDIF
22650 ELSE
22660 IF4ANDNOTT% TIE%=TIE%OR%1<<C%
22670 ENDIF
22680 P%(C%)+=2:IFLength%(I%)<QI%(S%) QI%(S%)=Length%(I%):Q%(S%)=Q%+QI%(S%)
22690 UNTIL(2<<C%)>G%
22700ENDPROC
22710
22720DEF PROCplay_attribute(A%)
22730C%=TRUE:REPEATC%-=TRUE:UNTILA%AND%1<<C%
22740ONC%+1 PROCplay_time_sig(A%),PROCplay_key_sig(A%),PROCplay_clef(A%),PROCplay_slur(A%),PROCplay_octave(A%),PROCplay_bar_line(A%)
22750ENDPROC
22760
22770DEF PROCplay_time_sig(A%)
22780A%=((A%>>1AND&F)+1)*Length%(A%>>3AND%11100)
22790SYS Sound_QSchedule,Beats%,Sch%ORSound_QBeat,A%
22800Beats%=A%
22810ENDPROC
22820
22830DEF PROCplay_key_sig(A%)
22840LOCALN%:A%=A%>>2
22850FORN%=0TO6:Key%(N%)=Key_Sig%(A%,N%):NEXT
22860ENDPROC
22870
22880DEF PROCplay_clef(A%)
22890PCLEF%(A%>>6)=Clef%(A%>>3AND3)
22900ENDPROC
22910
22920DEF PROCplay_slur(A%)
22930ENDPROC
22940
22950DEF PROCplay_octave(A%)
22960ENDPROC
22970
22980DEF PROCplay_bar_line(A%)
22990PBAR%+=1
23000ENDPROC
23010
23020DEF FNinitialise
23030LOCAL SoundEnable%
23040PROCEnumerateSWIs
23050PROCinitialise_miscellany
23060PROCinitialise_screen
23070PROCinitialise_sprites
23080PROCinitialise_wimp
23090SoundEnable%=FNinitialise_sound
23100PROCinitialise_music
23110PROCinitialise_menu
23120IF SoundEnable%=1 IF FNCheckOK("Sound is not enabled. Do you wish to quit?", 3) PROCterminate
23130=TRUE
23140
23150DEF PROCEnumerateSWIs
23160LOCALW%:W% = &400C0
23170SpriteOp% = &2E
23180Initialise% = W%+0
23190CreateWindow% = W%+1
23200CreateIcon% = W%+2
23210OpenWindow% = W%+5
23220CloseWindow% = W%+6
23230Poll% = W%+7
23240RedrawWindow% = W%+8
23250UpdateWindow% = W%+9
23260GetRectangle% = W%+10
23270GetWindowState% = W%+11
23280SetIconState% = W%+13
23290GetIconInfo% = W%+14
23300GetPointerInfo% = W%+15
23310DragBox = W%+16
23320ForceRedraw = W%+17
23330CreateMenu = W%+20
23340DecodeMenu = W%+21
23350SetExtent = W%+23
23360OpenTemplate = W%+25
23370CloseTemplate = W%+26
23380LoadTemplate = W%+27
23390CloseDown = W%+29
23400SYS "OS_SWINumberFromString",0,"Wimp_SendMessage" TO SendMessage
23410SYS "OS_SWINumberFromString",0,"Wimp_GetWindowOutline" TO GetWindowOutline
23420SYS "OS_SWINumberFromString",0,"Wimp_SetColour" TO SetColour
23430
23440LOCAL S0,S1,S2
23450SYS "OS_SWINumberFromString",0,"Sound_Configure" TO S0
23460SYS "OS_SWINumberFromString",0,"Sound_Volume" TO S1
23470SYS "OS_SWINumberFromString",0,"Sound_QInit" TO S2
23480Sound_Configure = S0
23490Sound_Enable = S0+1
23500Sound_Stereo = S0+2
23510Sound_Volume = S1
23520Sound_InstallVoice = S1+3
23530Sound_AttachVoice = S1+5
23540Sound_Control = S1+9
23550Sound_QInit = S2
23560Sound_QSchedule = S2+1
23570Sound_QRemove = S2+2
23580Sound_QFree = S2+3
23590Sound_QTempo = S2+5
23600Sound_QBeat = S2+6
23610Sch%=&F000000
23620LOCAL M%
23630LOCAL ERROR
23640ON ERROR LOCAL MIDIpresent%=FALSE : ENDPROC
23650SYS "OS_SWINumberFromString",0,"MIDI_SoundEnable" TO M%
23660M_NoteOff% = &80
23670M_NoteOn% = &90
23680MIDI_SoundEnable% = M%
23690MIDI_SetTxChannel% = M% + 2
23700MIDI_SetTxActiveSensing% = M% + 3
23710MIDI_TxCommand% = M% + 10
23720MIDI_TxLocalControl% = M% + 15
23730MIDI_TxOmniModeOff% = M% + 17
23740MIDI_TxOmniModeOn% = M% + 18
23750MIDI_TxMonoModeOn% = M% + 19
23760MIDI_TxPolyModeOn% = M% + 20
23770MIDI_TxProgramChange% = M% + 21
23780MIDI_TxSystemReset% = M% + 30
23790MIDIpresent%=TRUE : REM disable untested MIDI bits
23800ENDPROC
23810
23820DEF PROCinitialise_miscellany
23830DIM Resourcedir 255
23840LOCAL len
23850SYS "OS_ReadVarVal", "Maestro$dir", Resourcedir, 255, 0, 0 TO ,,len
23860Resourcedir?len=13 : REM It needs a cr termination
23870Resourcedir$=$Resourcedir
23880TIME=0
23890AwaitingAck%=FALSE
23900DRAGGING%=FALSE
23910SAVING%=FALSE
23920PLAYING%=FALSE
23930SCROLLING%=FALSE
23940TRANSCRIBE%=TRUE
23950DIM String_Space% 256
23960OS_File=&8
23970Sprite$=Resourcedir$+".Sprites"
23980CHANGED%=FALSE
23990FILE%=FALSE
24000laddr%=0
24010eaddr%=0
24020ENDPROC
24030
24040DEF PROCinitialise_screen
24050DIM modeblockin 40
24060DIM modeblockout 40
24070PROCgetmodeinfo(TRUE)
24080PROCset_colours
24090ENDPROC
24100
24110DEF PROCset_colours
24120LOCALC%
24130REM colours defined in memo from WStoye 21/7/88
24140C_MenuTitlefg = 7
24150C_MenuTitlebg = 2
24160C_Menufg = 7
24170C_Menubg = 0
24180C_MenuItemfg = 7
24190C_MenuItembg = 0
24200ENDPROC
24210
24220DEF PROCgetmodeinfo(new)
24230LOCAL S_Rows%, S_Columns%
24240modeblockin!0=0 : REM ModeFlags
24250modeblockin!4=1 : REM ScrRCol
24260modeblockin!8=2 : REM ScrBCol
24270modeblockin!12=3 : REM NColour
24280modeblockin!16=4 : REM XEigFactor
24290modeblockin!20=5 : REM YEigFactor
24300modeblockin!24=11 : REM XWindLimit
24310modeblockin!28=12 : REM YWindLimit
24320modeblockin!32=-1 : REM terminate list
24330SYS "OS_ReadVduVariables",modeblockin, modeblockout
24340BADMODE% = FALSE : REM ((modeblockout!12)>15) :REM doesn't work in 256-colour modes
24350REM no point in reading sizes since they are fixed by the wimp
24360Hi%=1<<(modeblockout!16)
24370Vi%=1<<(modeblockout!20)
24380S_Width%=Hi% * ((modeblockout!24)+1)
24390S_Height%=Vi% * ((modeblockout!28)+1)
24400Hi%=2: Vi%=4
24410C_Width%=8*Hi% : C_Height%=8*Vi%
24420IF NOT new THEN
24430 IF NOT BADMODE% SYS"Wimp_ReadPixTrans",&200,SprBlk%,S%(0),,,,factors%,pixtrans%
24440 ENDIF
24450ENDPROC
24460
24470DEF PROCinitialise_sprites
24480DIM factors% 16
24490DIM pixtrans% 16
24500SYS "Wimp_SpriteOp", 11,,Resourcedir$+".!Sprites" :REM merge music file type sprite into wimp sprite area
24510SpriteLen% = 10000
24520DIM SprBlk% SpriteLen%
24530!SprBlk% = SpriteLen% :REM initialise private sprite area
24540SprBlk%!4 = 0
24550SprBlk%!8 = 16
24560SprBlk%!12 = 16
24570SYS SpriteOp%, 512+10, SprBlk%, Sprite$ :REM *SLOAD equivalent
24580SprPlot% = &234 : REM sprite plot code
24590LOCAL I%,N%,x%,y%,W%,H%
24600RESTORE
24610N%=-1
24620READS$
24630WHILES$<>""
24640READx%,y%,W%,H%,S$
24650N%+=1
24660ENDWHILE
24670IFFNassert(N%>=0,"loading sprite data.") STOP
24680DIM S$(N%),x%(N%),y%(N%),X%(N%),Y%(N%), S%(N%) : REM S%() are sprite pointers
24690RESTORE
24700FORI%=0TON%
24710READ S$(I%),x%,y%,W%,H%
24720SYS SpriteOp%, 256+24, SprBlk%, S$(I%) TO ,,S%(I%) : REM get pointer to sprite
24730x%(I%)=x%*Hi%
24740y%(I%)=y%*Vi%
24750X%(I%)=(W%-x%)*Hi%
24760Y%(I%)=(H%-y%)*Vi%
24770NEXT
24780note%=0
24790rest%=16
24800accidental%=24
24810clef%=32
24820high%=36
24830dot%=44
24840ledger%=47
24850bar%=48
24860time%=55
24870key%=56
24880tie%=57
24890pointer%=58
24900DIM SL%(N%,1):REM List & item indexes for each sprite (set in list definitions)
24910SCORING%=FALSE:REM Flag indicating a sprite is to be drawn under the pointer
24920wasSCORING%=FALSE
24930stopSCORING%=TRUE
24940SCORING%=FALSE
24950DIM SCRIBE%(18):REM Details of symbol under pointer
24960REM 0-7 Channel pointers into N%()
24970sx%=8:REM Current sprite X position
24980sy%=9:REM Current sprite Y position
24990drawn%=10:REM Flag indicating sprite presence
25000sprite%=11:REM Sprite number
25010valid%=12:REM Valid stave positions
25020stave%=13:REM Current stave symbol is on
25030sclef%=14:REM Current clef applying to symbol (used for key sigs)
25040line%=15:REM Current stave line symbol is on
25050posx%=16:REM X music position
25060sgp%=17:REM Symbol gate pointer
25070sc%=18:REM Channel number if symbol specified close to note
25080PROCgetmodeinfo(FALSE)
25090ENDPROC
25100REM name, x, y, w, h
25110DATA B,7,3,26,7,SB,0,2,12,5,Mu,0,2,11,15,Cu,0,2,11,15,Qu,0,2,17,17,SQu,0,2,17,17,DSQu,0,2,17,17,SDSQu,0,2,17,17
25120DATA B,7,3,26,7,SB,0,2,12,5,Md,0,12,11,15,Cd,0,12,11,15,Qd,0,14,11,17,SQd,0,14,11,17,DSQd,0,14,11,17,SDSQd,0,14,11,17
25130DATA Rest,-1,-1,8,4,Rest,-1,-2,8,4,Rest,-1,0,8,4,Rest4,-2,5,7,12,Rest8,-1,4,9,8,Rest16,0,8,11,12,Rest32,1,8,13,16,Rest64,2,12,15,20
25140DATA M,0,2,11,5,Natural,8,6,7,13,Sharp,10,5,9,11,Flat,8,3,7,12,Sharp2,9,2,8,5,Flat2,14,3,13,12,NSharp,17,6,16,13,NFlat,15,6,14,15
25150DATA Treble,0,16,18,31,Alto,0,8,16,19,Alto,0,4,16,19,Bass,0,7,20,17
25160DATA Bh,7,3,26,7,SBh,0,2,12,5,Mh,0,2,11,5,Ch,0,2,11,5
25170DATA ldg5,2,28,15,17,ldg4,2,24,15,13,ldg3,2,20,15,9,ldg2,2,16,15,5,ldg1,2,12,15,1
25180DATA Dot1,-12,2,3,2,Dot2,-12,2,8,2,Dot3,-12,2,13,2,Bar,-1,8,2,17,C,0,2,11,5
25190DATA ldg1,2,-12,15,1,ldg2,2,-12,15,5,ldg3,2,-12,15,9,ldg4,2,-12,15,13,ldg5,2,-12,15,17
25200DATA Time,1,9,25,19,Key,0,13,0,30,Tie,-12,-3,23,3,""
25210
25220DEF PROCinitialise_wimp
25230PROCenumerate_wimp_offsets
25240task%=ASC("T")+(ASC("A")<<8)+(ASC("S")<<16)+(ASC("K")<<24)
25250SYS Initialise%, 200, task%, "Maestro" TO ,Task_h%
25260Mouse_X%=640:Mouse_Y%=560
25270PTIME%=0
25280DIM SpriteName% 14
25290$SpriteName%="!Maestro"
25300!Icon%=-1: REM iconbar utility
25310Icon%!4=0
25320Icon%!8=0
25330Icon%!12=64
25340Icon%!16=68
25350Icon%!20=(&311A OR (0<<24) OR (7<<28)) :REM indirected
25360Icon%!24=SpriteName% :REM indirected name
25370Icon%!28=1 :REM use common sprite area
25380Icon%!32=12 :REM name length
25390SYS CreateIcon%, ,Icon% TO Maestro_h%
25400PROCload_templates
25410ENDPROC
25420
25430DEF PROCenumerate_wimp_offsets
25440DIM Wimp_Space% &4000
25450Window%=Wimp_Space%+4
25460handle%=-4
25470x0%=0:y0%=4:x1%=8:y1%=12
25480scx%=16:scy%=20
25490under%=24
25500flags=28:status=30
25510tFrgd=32:tBkgd=33:Frgd=34:Bkgd=35
25520sbo=36:sbi=37
25530tHigh=38
25540title_flags=56
25550work_area_flags=60
25560sprite_area=64
25570title=72
25580icons=84
25590icon_defs=88
25600Icon%=Wimp_Space%+4
25610iflags=16
25620Colour=19
25630idata=20
25640Mouse%=Wimp_Space%
25650buttons=8
25660window=12
25670icon=16
25680old_buttons=20
25690colour=23
25700state=4
25710mask=8
25720Clip%=Window%+24
25730key=20
25740ENDPROC
25750
25760DEF PROCload_templates
25770LOCAL size, end
25780DIM windowname 12
25790DIM ScoreWBlk% 32
25800DIM NotePBlk% 32
25810DIM SharpPBlk% 32
25820DIM RestPBlk% 32
25830DIM ScoreTitle% 256
25840size = 1000
25850dialogsize = 3000
25860DIM InstrIndirect size : REM buffer for indirected text of instruments list
25870ScoreWBlk%-=handle%
25880DIM DialogIndirect dialogsize : REM buff for indirected text of dialog boxes
25890NotePBlk%-=handle%
25900SharpPBlk%-=handle%
25910RestPBlk%-=handle%
25920SYS OpenTemplate, ,Resourcedir$+".Templates"
25930$windowname="ScoreWind"
25940SYS LoadTemplate, ,Window%, ScoreTitle%, ScoreTitle%+255, -1, $windowname, 0
25950Window%!68=&10001 :REM reserved word sets min window size. to other than title size
25960SYS CreateWindow%, ,Window% TO ScoreWind_h%
25970ScoreWBlk%!handle%=ScoreWind_h%
25980SYS GetWindowState%, ScoreWind_h%, ScoreWBlk%+handle%
25990$windowname="SharpsPane"
26000SYS LoadTemplate, ,Window%, Window%+icons, Window%+200, -1, $windowname, 0
26010Window%!64=SprBlk% :REM use private sprite area
26020SYS CreateWindow%,, Window% TO SharpsPane_h%
26030SharpPBlk%!handle%=SharpsPane_h%
26040SYS GetWindowState%,, SharpPBlk%+handle%
26050SharpPBlk%!under%=-1
26060$windowname="RestsPane"
26070SYS LoadTemplate, ,Window%, Window%+icons, Window%+200, -1, $windowname, 0
26080Window%!64=SprBlk% :REM use private sprite area
26090SYS CreateWindow%, ,Window% TO RestsPane_h%
26100RestPBlk%!handle%=RestsPane_h%
26110SYS GetWindowState%,, RestPBlk%+handle%
26120RestPBlk%!under%=SharpsPane_h%
26130$windowname="NotesPane"
26140SYS LoadTemplate, ,Window%, Window%+icons, Window%+200, -1, $windowname, 0
26150Window%!64=SprBlk% :REM use private sprite area
26160SYS CreateWindow%, ,Window% TO NotesPane_h%
26170NotePBlk%!handle%=NotesPane_h%
26180SYS GetWindowState%,, NotePBlk%+handle%
26190NotePBlk%!under%=RestsPane_h%
26200PaneHeight%=Window%!y1%-Window%!y0%
26210SelW%=NotesPane_h%:SelI%=0
26220$windowname="InstrWind"
26230 SYS LoadTemplate, ,Window%, InstrIndirect, InstrIndirect+size, -1, $windowname, 0
26240IF NOT MIDIpresent% Window%!8-=100:Window%!48-=100:REM hide MIDI column in window
26250SYS CreateWindow%, ,Window% TO InstrWind_h%
26260LOCAL n%
26270DIM StaveStr%(7), VoiceStr%(7), VolumeStr%(7), StereoStr%(7), MIDIChStr%(7) :REM pointers to icon strings
26280VoiceSize%=20 : VolSize%=4 : SterSize%=14 : MIDIChSize%=3
26290FOR n%=0 TO 7
26300 StaveStr%(n%) = InstrIndirect + n%*14
26310 NEXT
26320InstrIndirect+=8*14
26330FOR n%=0 TO 7
26340 VoiceStr%(n%) = InstrIndirect + n%*VoiceSize%
26350 VolumeStr%(n%) = InstrIndirect + 8*VoiceSize% + n%*VolSize%
26360 StereoStr%(n%) = InstrIndirect + 8*VoiceSize% + 8*VolSize% + n%*SterSize%
26370 MIDIChStr%(n%) = InstrIndirect + 8*VoiceSize% + 8*VolSize% + 8*SterSize% +n%*MIDIChSize%
26380 IF NOT MIDIpresent% PROCUpdateIcon(InstrWind_h%, 32+n%, 1<<22, 0) :REM make icons unselectable
26390 NEXT
26400end=DialogIndirect+dialogsize
26410$windowname="progInfo"
26420SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26430SYS CreateWindow%, ,Window% TO ProgInfo_h%
26440DialogIndirect+=13 : REM skip indirected window title
26450$DialogIndirect = "Maestro" : DialogIndirect+=40
26460$DialogIndirect = "Music player" : DialogIndirect+=40
26470$DialogIndirect = "Acorn Computers" : DialogIndirect+=40
26480$DialogIndirect = VersionStr$ : DialogIndirect+=30
26490$windowname="query"
26500SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26510SYS CreateWindow%, ,Window% TO AbortQuery_h%
26520DialogIndirect+=100 :REM "music not saved etc."
26530DialogIndirect+=100
26540DIM FileSpriteName% 16
26550$FileSpriteName%="file_"+STR$~MusicFileType%
26560$windowname="xfer_send"
26570SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26580Window%!64=1 :REM use common sprite area
26590SpriteIcon%=Window%+88+32*2 :REM pointer to icon number 2 in save window
26600SpriteIcon%!16=SpriteIcon%!16 OR 1<<1 OR 1<<8 :REM indirected sprite
26610SpriteIcon%!16=SpriteIcon%!16 AND NOT 1 :REM not text
26620SpriteIcon%!24=1 :REM wimp area pointer
26630SpriteIcon%!28=12
26640SYS CreateWindow%, ,Window% TO Save_h%
26650SaveText=DialogIndirect
26660$SaveText="MusicFile"+CHR$(0) : DialogIndirect+=255
26670SaveSprite%=DialogIndirect :REM address of file type sprite in save window
26680DialogIndirect+=16
26690$SaveSprite%=$FileSpriteName%
26700$windowname="dboxfile_db"
26710SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26720SYS CreateWindow%, ,Window% TO Load_h%
26730DialogIndirect+=30 : LoadText=DialogIndirect
26740$LoadText=$SaveText : DialogIndirect+=255
26750$windowname="TimeSigW"
26760SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26770SYS CreateWindow%, ,Window% TO TimeSig_h%
26780BarLength%=DialogIndirect : DialogIndirect+=3
26790NoteValue%=DialogIndirect : DialogIndirect+=3
26800$windowname="BarW"
26810SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26820SYS CreateWindow%, ,Window% TO Bar_h%
26830BarNum%=DialogIndirect : DialogIndirect+=15 :REM allow for validation str
26840$windowname="FileInfo"
26850SYS LoadTemplate, ,Window%, DialogIndirect, end, -1, $windowname, 0
26860Window%!64=1 :REM use common sprite area
26870SpriteIcon%=Window%+88+32*6 :REM pointer to icon number 6 in save window
26880SpriteIcon%!16=SpriteIcon%!16 OR 1<<1 OR 1<<8 :REM indirected sprite
26890SpriteIcon%!16=SpriteIcon%!16 AND NOT 1 :REM not text
26900SpriteIcon%!24=1 :REM wimp area pointer
26910SpriteIcon%!28=12
26920SYS CreateWindow%, ,Window% TO FileInfo_h%
26930ThisFile%=DialogIndirect : DialogIndirect+=255
26940Updated%=DialogIndirect : DialogIndirect+=12
26950FileType%=DialogIndirect : DialogIndirect+=20
26960FileSize%=DialogIndirect : DialogIndirect+=20
26970FileDate%=DialogIndirect : DialogIndirect+=100
26980FileTypeSprite%=DialogIndirect :REM address of file type sprite in window
26990$FileTypeSprite%=$FileSpriteName% : DialogIndirect+=16
27000$ThisFile%="<untitled>"
27010$Updated%="NO"
27020F$="<untitled>"
27030$ThisFile%=F$+CHR$(0)
27040$ScoreTitle%=F$+CHR$(0)
27050SYS CloseTemplate
27060IF FNassert(DialogIndirect<end-10, "Not enough memory to load window templates.") : STOP
27070ScoreClosed%=FALSE
27080ENDPROC
27090
27100DEF PROCinitialise_menu
27110LOCAL item$, sub%, MenuBlock, SubMenuBlock, MenuEnd, MenuItemsList
27120LOCAL nextsub%, flags%, m%, width%
27130MenuSize% = 28+(20*24)
27140DIM MenuStart 15*MenuSize% :REM allow 15 menus max
27150MenuEnd=MenuStart+15*MenuSize%
27160DIM MenuString 14*4
27170DIM StaveNum% 1 : REM buffers for writeable icons
27180DIM ValstrS 10
27190RESTORE +0
27200MenuBlock=MenuStart
27210READ item$
27220WHILE item$>""
27230 CASE LEFT$(item$,3) OF
27240 WHEN "Mae" : IconMenu%=MenuBlock
27250REM note there are 2 "Maestro" menus. IconMenu% is the 2nd
27260 WHEN "Sta" : StaveMenu%=MenuBlock
27270 WHEN "Vol" : VolumeMenu%=MenuBlock
27280 WHEN "Tem" : TempoMenu%=MenuBlock
27290 WHEN "Maj" : MajorMenu%=MenuBlock
27300 WHEN "Min" : MinorMenu%=MenuBlock
27310 ENDCASE
27320 $MenuBlock = item$
27330 MenuBlock?12 = C_MenuTitlefg
27340 MenuBlock?13 = C_MenuTitlebg
27350 MenuBlock?14 = C_Menufg
27360 MenuBlock?15 = C_Menubg
27370 READ width%
27380REM adjust menu width
27390 MenuBlock!16 = width% * C_Width%
27400 MenuBlock!20 = 44
27410 MenuBlock!24 = 0
27420 MenuItemsList=MenuBlock+28
27430 SubMenuBlock=MenuBlock+MenuSize%
27440 nextsub%=SubMenuBlock
27450 READ item$
27460 WHILE item$>""
27470 READ sub%
27480 IF sub%=0 sub%=-1 ELSE sub%=nextsub% : nextsub%+=MenuSize%
27490 IF FNassert(sub%<MenuEnd, "Menu too big.") STOP
27500REM pointers to windows
27510 CASE LEFT$(item$,4) OF
27520 WHEN "Inst" : sub%=InstrWind_h%
27530 WHEN "Info" : sub%=ProgInfo_h%
27540 WHEN "Save" : sub%=Save_h%
27550 WHEN "File" : sub%=FileInfo_h%
27560 WHEN "Time" : sub%=TimeSig_h%
27570 WHEN "Goto" : sub%=Bar_h%
27580 ENDCASE
27590 $(MenuItemsList+12) = item$+CHR$(0)
27600 MenuItemsList!4 = sub%
27610 MenuItemsList!8 = &21 OR (C_MenuItemfg<<24) OR (C_MenuItembg<<28)
27620 READ flags%
27630 !MenuItemsList=flags% AND %111
27640 REM indirect writeable icons
27650 IF flags%AND4 THEN
27660 MenuItemsList!8 = (MenuItemsList!8) OR 1<<8 :REM set indirect bit
27670 CASE LEFT$(item$,4) OF
27680 WHEN "Stav"
27690 MenuItemsList!12=StaveNum% :REM buffer pointer
27700 $ValstrS="a1-4"
27710 MenuItemsList!16 = ValstrS :REM validation string
27720 MenuItemsList!20 = 2 :REM buffer length
27730 ENDCASE
27740 ENDIF
27750 MenuItemsList+=24
27760 READ item$
27770 ENDWHILE
27780 REM last item
27790 MenuItemsList-=24
27800 !MenuItemsList = !MenuItemsList OR &80 : REM last item
27810 READ item$
27820 MenuBlock=SubMenuBlock
27830ENDWHILE
27840$BarLength%="4"
27850PROCSetVolume(6) : REM ff
27860PROCSetTempo(8) : REM moderato
27870$StaveNum%=LEFT$(STR$(STAVE%+1),1)
27880item% = FNFindMenuItem("+percussion", StaveMenu%)
27890IF (item%>=0) !item% = FNSetBit(PERC%=1, !item%, 0):REM set or clear tick
27900REM abstracted from set key sig
27910LOCAL n
27920IF KEY_SIG%(1) n=accidental%+2+KEY_SIG%(0) : X%(key%)=(x%(n)+X%(n))*KEY_SIG%(1) ELSE X%(key%)=x%(accidental%+2)+X%(accidental%+2)
27930CONFIRM%=FALSE
27940ENDPROC
27950REM [title, width, [item, 1 if sub_menu pointer, flags] ]
27960DATA "Maestro", 12
27970DATA "Save",0,0, "File",0,0, "Clear",0,2
27980DATA "Staves",1,0, "Instruments",0,0, "Volume",1,0, "Tempo",1,2
27990DATA "Time sig.",0,0, "Key sig.",1,2
28000DATA "Goto",0,0, "Play",0,0,""
28010DATA "Staves",12
28020DATA "StaveNum",0,4, "+percussion",0,0,""
28030REM 4 indicates writeable
28040DATA "Volume",4
28050DATA "ppp",0,0, "pp",0,0, "p",0,0, "mp",0,0, "mf", 0,0
28060DATA "f",0,0, "ff",0,1, "fff",0,0,""
28070DATA "Tempo",12
28080DATA "Largissimo",0,0, "Largo",0,0, "Larghetto",0,0, "Grave",0,0
28090DATA "Adagio",0,0, "Adagietto",0,0, "Andante",0,0, "Andantino",0,0
28100DATA "Moderato",0,1, "Allegretto",0,0, "Allegro",0,0, "Vivace",0,0
28110DATA "Veloce",0,0, "Presto",0,0, "Prestissimo",0,0,""
28120DATA "Key sig.",6
28130DATA "Major",1,0, "Minor",1,0,""
28140DATA "Major",3
28150DATA "Cb",0,0, "Gb",0,0, "Db",0,0, "Ab",0,0, "Eb",0,0, "Bb",0,0, "F",0,2
28160DATA "C", 0,3 : REM dotted line and tick
28170DATA "G", 0,0, "D", 0,0, "A", 0,0, "E", 0,0, "B", 0,0, "F#",0,0, "C#",0,0,""
28180DATA "Minor",3
28190DATA "Ab",0,0, "Eb",0,0, "Bb",0,0, "F", 0,0, "C", 0,0, "G", 0,0, "D",0,2
28200DATA "A", 0,3 : REM dotted line and tick
28210DATA "E", 0,0, "B", 0,0, "F#",0,0, "C#",0,0, "G#",0,0, "D#",0,0, "A#",0,0,""
28220DATA "Maestro",5
28230DATA "Info",0,0, "Quit",0,0,""
28240DATA ""
28250
28260DEF FNFindMenuItem(item$, menu%) : REM return pointer to menu item
28270LOCAL item%,this$
28280item$=LEFT$(item$,3)
28290item%=menu%+28
28300this$=LEFT$($(item%+12),3)
28310IF (this$ <> item$) THEN
28320 REPEAT
28330 item%+=24
28340 this$=LEFT$($(item%+12),3)
28350 UNTIL ( (this$ = item$) OR (!item% AND &80)>0 )
28360 ENDIF
28370IF (this$ <> item$) THEN =-1 : REM not found
28380=item%
28390
28400DEF FNinitialise_sound
28410LOCAL SoundEnable%
28420SYS Sound_Configure,8 TO PrevConfigure
28430SYS Sound_Enable,0 TO SoundEnable%
28440REM disconnect midi interpreter
28450IF MIDIpresent% SYS MIDI_SoundEnable%,0
28460=SoundEnable%
28470
28480DEF PROCClearAllMusic
28490FINE%()=MUSIC%()
28500PP%=MUSIC%:P%()=MUSIC%()
28510NBars%=0 :REM number of bars
28520BAR%=0:REM Current bar number
28530PBAR%=0:REM Playing bar counter
28540SBAR%=0:REM scrolling bar number
28550PROCstart_music:REM Set pointers to start of music store
28560EP%=GP%
28570PROCbar:REM Music always starts off with a bar
28580GATE%=MUSIC%+2:REM End of minimum score
28590PX%(0)=0:REM Score starts at left window edge
28600PXn%(BAR%)=0
28610PW%(0)=4*Hi%:REM Bar width
28620PTYPE%(0)=Bar%:REM Score starts with a bar
28630GP%=MUSIC%:REM Reset gate pointer for set score
28640GPn%(BAR%)=GP%
28650PROCrescore(0)
28660PROCSetExtent(S_Width%)
28670PROCrelease
28680wasSCORING%=FALSE
28690stopSCORING%=TRUE
28700SCORING%=FALSE
28710CHANGED%=FALSE
28720$Updated%="NO"
28730PROCUpdateTitle("<untitled>")
28740$SaveText="MusicFile"+CHR$(0)
28750$LoadText="MusicFile"+CHR$(0)
28760n=FNGetFileInfo("") :REM clear file info stuff
28770$FileDate%=""
28780ENDPROC
28790
28800REM PROCEDURE: initialise_music
28810REM
28820REM STRUCTURES: Queue of Gate;
28830REM Queue of Music;
28840REM
28850REM TYPES: Gate= byte0>0 -> Gate_Mask
28860REM byte0=0 -> Music_Attribute
28870REM
28880REM Gate_Mask= byte; bitn=1 -> gate 1 note/rest from music queue n (n=0-7)
28890REM
28900REM Music_Attribute= word; bit0_7=0
28910REM bit8=1 -> Time signature bit12_9 No. beats-1
28920REM bit15_13 Beat type
28930REM bit9=1 -> Key signature bit10 0-#, 1-b
28940REM bit13_11 0-7
28950REM bit10=1 -> Clef bit12_11 Treble,Alto,Tenor,Bass
28960REM bit15_14 Stave 1-4
28970REM bit11=1 -> Slur switch bit12 on/off
28980REM bit15_14 Stave 1-4
28990REM bit12=1 -> Octave shift bit13 0-up,1-down
29000REM bit15_14 Stave 1-4
29010REM bit13=1 -> bar
29020REM (bit13_0=0 -> Reserved)
29030REM
29040REM Music= word; bit7_3>0 -> Note
29050REM bit7_3=0 -> Rest
29060REM
29070REM Note= word; bit0= Stem orientation: 0-up,1-down
29080REM bit1=1 -> Join barbs to next note
29090REM bit2=1 -> Tie with next note
29100REM bit7_3>0 Note stave line position 1 to 31 (16=centre line)
29110REM bit10_8>0 -> Accidental N,#,b,X,bb,N#,Nb
29120REM bit12_11= Number of dots 0 to 3
29130REM bit15_13= Type: Breve to Semi-demi-semiquaver
29140REM
29150REM Rest= word; bit0=0 NB If a rest coincides with a note, its
29160REM bit1=0 position is determined by the
29170REM bit2=0 following note on the same channel.
29180REM bit7_3=0
29190REM bit10_8=0
29200REM bit12_11= Number of dots 0 to 3
29210REM bit15_13= Type: Breve rest to Semi-demi-semiquaver rest
29220REM
29230REM Position_type= 0 -> Note
29240REM 1 -> Time Signature
29250REM 2 -> Key Signature
29260REM 3 -> Clef
29270REM 4 -> Slur
29280REM 5 -> Octave shift
29290REM 6 -> Bar
29300:
29310DEF PROCinitialise_music
29320LOCALN%,C%:REM Note,channel
29330PROCinitialise_options
29340Note%=0:Time%=1:Key%=2:Clef%=4:Slur%=8:Octave%=16:Bar%=32:REM Stave attribute enumerators
29350DIM Ninc%(6),Line(42),Aoff(7),Clef%(3),Key%(6),Key_Sig%(15,6),Length%(31),Duration%(NTempos%),Accidental%(3,31)
29360FORN%=0TO6:Ninc%(N%)=ASCMID$("024579;",N%+1)AND15:NEXT:REM Note increment
29370LOCALST:ST=&1000/12:REM Semitone increment
29380FORN%=0TO42
29390Line(N%)=(1+N%DIV7<<12)+Ninc%(N%MOD7)*ST+.49
29400NEXT:REM Notes corresponding to stave lines (C octave 1 TO C octave 7)
29410Aoff(2)=ST:Aoff(3)=-ST:Aoff(4)=ST*2:Aoff(5)=-ST*2:Aoff(6)=ST:Aoff(7)=-ST:REM Accidental offsets
29420Clef%(0)=11:Clef%(1)=5:Clef%(2)=3:Clef%(3)=-1:REM Line offsets for each clef
29430FORC%=2TO15
29440FORN%=0TO(C%>>1)-1
29450Key_Sig%(C%,(7+Key_Y%(1,C%AND%1,N%))MOD7)=C%MOD2+2
29460NEXT
29470NEXT:REM Set up note offsets for each key signature
29480FORC%=0TO31
29490Length%(C%)=(%1<<7-(C%>>2))*(%1111000>>(C%AND3)AND%1111):REM Length of each possible note/rest (dotted) in tempo beats
29500NEXT
29510LOCALD%:REM Duration
29520FORN%=0TONTempos%
29530DIM C% 32:Duration%(N%)=C%:REM Reserve space for each tempo
29540FORC%=0TO31
29550D%=75/Tempo%(N%)*Length%(C%)/8+.5:REM Durations of note+dot combinations in 20ths of a second for each tempo (Max 22.5 Seconds)
29560IFD%>254 D%=254:REM Limit to maximum possible duration (12.7 Seconds)
29570Duration%(N%)?C%=D%
29580NEXT
29590NEXT
29600TIE%=&FF:REM Tie state of channels - Each bit corresponds to a channel, 0 if tied
29610SPACE%=HIMEM-END :REM find available space to tailor allocation
29620IFFNassert(SPACE%>1024,"Not enough memory available for music storage.") STOP
29630Max_Gate%=SPACE%/100 :REM max no. of gates (events, chords)
29640Max_Bar%=Max_Gate%/4 :REM maximum number of bars
29650PLAYING%=FALSE:REM Flag indicating play in process
29660SCROLLING%=FALSE:REM Flag indicating auto-scrolling while playing
29670DIM Q%(Max_Stave%+2):REM Time positions of next notes on each stave
29680DIM QI%(Max_Stave%+2):REM Time increments of Q%() for each stave
29690B1%=0:B2%=0:REM Alternate beat counters used to detect zero wrap
29700::
29710DIM GPn%(Max_Bar%) :REM GP% at the start of each bar
29720DIM PX%(Max_Gate%),PW%(Max_Gate%),PTYPE%(Max_Gate%):REM Notation screen positions, widths & types
29730REM PX% is PX%() index to screen positions & types - Always refers to the note/attribute just passed
29740DIM PXn%(Max_Bar%) :REM PX% at start of each bar
29750DIM BPn%(7,Max_Bar%):REM Indices to (8) note queues at the start of each bar
29760DIM MUSIC%(7),FINE%(7):REM Pointers delimiting music storage
29770DIM N%(7),n%(7):REM Pointers to current notes (n%()=copy, cf PROCproximate)
29780DIM C%(7),c%(7):REM Indexes of gate channels used in sorting
29790DIM CLEF%(Max_Stave%),clef%(Max_Stave%):REM Current clef on each stave (& clef copy)
29800DIM CLEFn%(Max_Stave%,Max_Bar%) :REM CLEF% at start of each bar
29810DIM SIG%(1),sig%(1):REM Base bar key & time sigs, current and copy
29820DIM SIGn%(1,Max_Bar%) :REM SIG% at the start of each bar
29830DIM P%(7),PCLEF%(3):REM Playing note pointers, clef
29840MaxNotesInBar%=128
29850REM while one half of draw q is being filled, the other half is being used for scrolling.
29860LOCAL n%
29870n%=MaxNotesInBar%*4
29880DIM MIDI_OFF%(1,n%) :REM an array of MIDI noteoffs if playing is stopped in the middle of a bar
29890MIDI_OFF%(0,0)=-1
29900MIDI_OFF%(1,0)=-1
29910MIDI_Notenum%=0
29920SPACE%=HIMEM-END
29930SPACE%-=&4000:REM Leave a few K for the program and Basic stack
29940IFFNassert(SPACE%>1024,"Not enough memory available for music storage.") STOP
29950DIM MUSIC% SPACE%+8:REM Allocate main music space (+8: Extra bytes in case of overrun)
29960FINE%=MUSIC%+SPACE%:REM End of music memory
29970GATE%=MUSIC%:REM No gate space used as yet
29980FORC%=0TO7
29990MUSIC%(C%)=MUSIC%+(C%+1)*SPACE%/9
30000NEXT:REM Share out storage (NB No bounds checking ever occurs!)
30010FINE%()=MUSIC%():REM No music defined yet
30020Pgap%=X%(2)DIV2+1:REM Symbol spacing (half note blob width)
30030:
30040REM Now set up basic score
30050PROCClearAllMusic
30060ENDPROC
30070
30080DEF PROCinitialise_options
30090LOCALN%,C%
30100NTempos%=14
30110DIM Tempo%(NTempos%)
30120Tempo%(0)=40
30130Tempo%(1)=50
30140Tempo%(2)=60
30150Tempo%(3)=65
30160Tempo%(4)=70
30170Tempo%(5)=80
30180Tempo%(6)=90
30190Tempo%(7)=100
30200Tempo%(8)=115
30210Tempo%(9)=130
30220Tempo%(10)=145
30230Tempo%(11)=160
30240Tempo%(12)=175
30250Tempo%(13)=190
30260Tempo%(14)=210
30270
30280DIM TIME_SIG%(1):REM Current time signature numerator(0) and denominator(1)
30290TIME_SIG%()=3:REM 4/4 time (= (n+1)/2^(d-1))
30300
30310DIM KEY_SIG%(1):REM Current key signature
30320DIM Key_Y%(3,1,6):REM Key signature 0-sharp/1-flat stave line positions
30330LOCAL C%,A%,P%:REM Indices
30340FOR C%=0 TO 3:REM For each clef
30350FOR A%=0 TO 1:REM For each accidental type
30360FOR P%=0 TO 6:REM For each accidental position
30370Key_Y%(C%,1-A%,P%)=3*(P%AND%1)-P%DIV2+(P%-3)*A%+(A%ANDC%<>2AND(P%AND5)=0)*7-1-(C%-1>>1)-2*(C%=2)
30380REM Position offsets of key signature accidentals from centre stave line
30390NEXT
30400NEXT
30410NEXT
30420KEY_SIG%(0)=1
30430KEY_SIG%(1)=0
30440LOCAL item%
30450Max_Stave%=3
30460Li%=2*Vi%
30470Stave_Height%=Li%*8
30480DIM Y_STAVE%(Max_Stave%+2)
30490STAVE%=0 :REM means 1 stave
30500PERC%=0 :REM means no percussion
30510Score_Width%=S_Width%
30520PROCposition_staves
30530
30540NVolumes%=7
30550DIM Volume%(NVolumes%),Volumes%(7)
30560FORR%=0TONVolumes%
30570Volume%(R%)=(R%+1)*120/(NVolumes%+1)-1 :REM don't permit full volume so that dynamics are possible
30580NEXT
30590SYS Sound_Volume TO R%
30600RestoreVolume=R%
30610Volume%=R%*(NVolumes%+1)/120-.5:IFVolume%<0 Volume%=0 :REM don't permit full volume. This is reserved for dynamics
30620SYS Sound_Volume,Volume%(Volume%)
30630FORR%=0TO7:Volumes%(R%)=6:NEXT
30640
30650LOCALS%
30660DIM Stave_Channels%(Max_Stave%+2,7):REM Primary stave allocation of channels 0-7 for each stave structure
30670FOR S%=0 TO Max_Stave%
30680FOR R%=0 TO 7
30690Stave_Channels%(S%,R%)=(S%+1)*R%DIV8:REM Close formula to desired data
30700NEXT
30710NEXT
30720Stave_Channels%(2,1)=1
30730Stave_Channels%(2,2)=1
30740Stave_Channels%(2,5)=2:REM Correct the exceptions to formula
30750DIM S_C%(7):REM Current stave allocation
30760FORR%=0TO7
30770S_C%(R%)=Stave_Channels%(STAVE%,R%):REM Initialise channel allocation (Also reset each time stave structure is changed)
30780NEXT
30790:REM Now get names and channel allocation of instruments
30800LOCALI$,L%,M%:REM Instrument name, Number of instruments, Instrument name length, Max
30810MAX_Voices%=20
30820DIM Voice$(20):REM Instrument voice descriptions
30830DIM RestoreVoice%(7)
30840DIM Instrument%(7,1):REM Instrument information 0=Stave, 1=Voice
30850DIM Nth$(6):REM First,Second stave etc
30860Nth$(1)="1st stave":Nth$(2)="2nd stave":Nth$(3)="3rd stave":Nth$(4)="4th stave":Nth$(5)="1st perc":Nth$(6)="2nd perc"
30870SYS Sound_InstallVoice TO I$,NVoices%:NVoices%-=1:REM NVoices% is now number of voices/instruments available
30880IF FNassert(NVoices%>0,"No sound voices are installed.") STOP
30890FORR%=1TONVoices%
30900SYS Sound_InstallVoice,0,R% TO Voice$(R%)
30910NEXT:REM Get names of voices/instruments
30920FORR%=0TO7
30930SYS Sound_AttachVoice,R%+1,0 TO L%,S%
30940RestoreVoice%(R%)=S%
30950IFS%<1ORS%>NVoices% S%=1:REM Make sure a voice is attached to all channels
30960SYS Sound_AttachVoice,L%,S%
30970Instrument%(R%,0)=S_C%(R%)+1:REM Instrument stave
30980Instrument%(R%,1)=S%:REM Instrument voice
30990$(VoiceStr%(R%)) = LEFT$(Voice$(S%), VoiceSize%)
31000NEXT:REM Get details of each channel
31010PROCSetDefaultChannels
31020DIM Volume$(NVolumes%)
31030Volume$(0)="ppp" : Volume$(1)="pp" : Volume$(2)="p" : Volume$(3)="mp" : Volume$(4)="mf" : Volume$(5)="f" : Volume$(6)="ff" : Volume$(7)="fff"
31040FORR%=0TO7
31050$(VolumeStr%(R%)) = LEFT$(Volume$(Volumes%(R%)), VolSize%)
31060NEXT
31070NStereos%=6
31080DIM Stereo%(NStereos%),Stereo$(NStereos%,1),Stereo_Position%(7)
31090Stereo$(0,0)="Full L.":Stereo$(1,0)="Left":Stereo$(2,0)="Centre L.":Stereo$(3,0)="Centre":Stereo$(4,0)="Centre R.":Stereo$(5,0)="Right":Stereo$(6,0)="Full R."
31100FORR%=0TONStereos%
31110Stereo%(R%)=(2*R%/NStereos%-1)*127
31120Stereo$(R%,1)="Stereo position ("+STR$Stereo%(R%)+")"
31130NEXT
31140FORR%=0TO7
31150Stereo_Position%(R%)=NStereos%DIV2
31160SYS Sound_Stereo,R%+1,Stereo%(Stereo_Position%(R%))
31170$(StereoStr%(R%)) = LEFT$(Stereo$(Stereo_Position%(R%), 0), SterSize%)
31180NEXT
31190
31200NMIDIChannels%=16
31210DIM MIDIChannel%(7)
31220FORR%=0TO7
31230MIDIChannel%(R%)=1
31240IF MIDIpresent% THEN
31250 $(MIDIChStr%(R%))="1"
31260ELSE
31270 $(MIDIChStr%(R%))=" "
31280 ENDIF
31290NEXT
31300
31310ENDPROC
31320
31330DEF PROCrestore
31340REMOSCLI("audio off")
31350FORR%=0TO7 :REM restore channel/voice allocation
31360SYS Sound_AttachVoice,R%+1,RestoreVoice%(R%)
31370NEXT
31380SYS Sound_Volume, RestoreVolume :REM restore volume
31390SYS Sound_Configure,PrevConfigure
31400ENDPROC
31410
31420DEF PROCexit
31430PROCremove_panes
31440PROCCloseWindow(ScoreWind_h%)
31450PROCrestore
31460ENDPROC
31470
31480DEF PROCterminate
31490IF INITIALISED% IF PLAYING% PROCplay_stop
31500IF Task_h%>0 THEN
31510 ON ERROR SYS CloseDown, task%, Task_h% : END
31520ELSE
31530 ON ERROR END
31540 ENDIF
31550IF INITIALISED% PROCexit :PROCCloseWindow(AbortQuery_h%)
31560REMCLOSE#debug
31570ON ERROR END
31580IF Task_h%>0 THEN SYS CloseDown, task%, Task_h%
31590END
31600ENDPROC
31610
31620DEF FNassert(E%,A$)
31630LOCAL e%
31640IFE% THEN=FALSE ELSE e%=FNCheckOK("ERROR: "+A$+" Program will exit.",1)
31650PROCterminate
31660=TRUE
31670
31680DEF PROCerror
31690LOCAL e%
31700LOCAL ERROR
31710ON ERROR LOCAL PROCterminate: STOP
31720SYS "Hourglass_Smash"
31730E$=REPORT$+" at line "+STR$ERL+". Exit program?"
31740FILE%=FILE%:IFFILE% CLOSE#FILE%:FILE%=FALSE
31750IF INITIALISED% THEN
31760 IF FNCheckOK(E$,3) PROCterminate
31770ELSE
31780 e%=FNCheckOK(E$,1) : PROCterminate
31790 ENDIF
31800ENDPROC
31810
31820DEF PROCOpenAbortQuery(x, y)
31830LOCAL xsize%, ysize%
31840Window%!handle%=AbortQuery_h%
31850SYS GetWindowState%, ,Window%+handle%
31860xsize% = Window%!x1%-Window%!x0%
31870ysize% = Window%!y1%-Window%!y0%
31880Window%!x0% = x - xsize%/2
31890Window%!x1% = x + xsize%/2
31900Window%!y0% = y - ysize%/2
31910Window%!y1% = y + ysize%/2
31920SYS OpenWindow%, ,Window%+handle%
31930ENDPROC
31940
31950DEF FNCheckOK(E$, boxes%)
31960LOCAL E%
31970!ERRBLOCK%=0
31980$(ERRBLOCK%+4)=LEFT$(E$,100)+CHR$0
31990SYS "Wimp_ReportError", ERRBLOCK%, boxes%, "Maestro" TO ,E%
32000=E%=1 : REM return TRUE if OK pressed
� > Music.Maestxt
� (c) Acorn Computers 1988
(!VersionStr$="1.65 (4-Jan-89)"
2
<
Task_h%=0
FMusicFileType%=&AF1
PINITIALISED%=�
Z� ERRBLOCK% 200
d� space for an error window
n.SPACE%=�-� :� check enough space available
x� SPACE%<100000 �
��debug=OPENOUT "debugfile"
�� � �error:�
�� get name of file to load
�ș "OS_GetEnv" � EnvStr$
�� �EnvStr$," -quit ") �
� I%=�EnvStr$,"""")
� I%=�EnvStr$,"""",I%+1)
�! �I%+=1:��EnvStr$,I%,1)<>" "
� f$=�EnvStr$,I%)
� �
�INITIALISED%=�initialise
�.mask%=0 : � mask out unwanted reason codes
�� � �error
�
� PLAYING% �CheckQ
) ș Poll%,mask%,Window%+handle% � R%
"Q � PLAYING% �CheckQ: � recheck since poll might have stayed away for a while
,
Ȏ R% �
6 � 0
@l � AwaitingAck% �CheckOK("Bad Data Transfer, Receiver Dead",3) :AwaitingAck%=� : CHANGED%=wasCHANGED%
J! � SCORING% �symbol_pointer
T' � f$<>"" �load_music(f$) : f$=""
^2 � SCROLLING% �CheckScroll :� auto-scrolling
h � 1:�redraw_window_request
r � 2:�open_window_request
| � 3:�close_window_request
�, � 4 :� pointer leaving window
�' � Window%!handle%=ScoreWind_h% �
� �release
� wasSCORING%=SCORING%
� SCORING%=�
� �
�D � Window%!handle%=AbortQuery_h% �CloseWindow(AbortQuery_h%)
�
�
�U � 5: � Window%!handle%=ScoreWind_h% SCORING% = (wasSCORING% � � stopSCORING%)=�
� � 6:�mouse_button_click
� � 7:�UserDragBox
� � 8:�KeyPressed
� � 9:�MenuSelect
B � 10: �ScrollReq(Window%!(handle%+32), Window%!(handle%+36))
� 17,18:�receive
�
& � �
0�
:
D
� �CheckQ
NB1%=B2%: B2%=Ə
X�B2%<B1% �play_bar
b�
l
v� �receive
�� task%, ref%, block,F$
�%block=Window% :� temporary buffer
�ref%=!(Window%+handle%+8)
�task%=!(Window%+handle%+4)
�8� task%=Task_h% � : � ignore messages from this task
�Ȏ Window%!(handle%+16) �
� � 0
� � � CHANGED% �
� �terminate
� �
� !block=20
� block!12=ref%
�# block!16=0 :� quit block
G ș SendMessage,19,block :� acknowledge, and refuse quit request
D � �CheckOK("Unsaved music. Do you really want to quit?",3) �
�terminate
�
*# ș GetPointerInfo%,,Mouse%
45 �OpenWindow(Save_h%, Mouse%!x0%, Mouse%!y0%)
>
�
H �
R � 2 : � save file
\ � SAVING% �
f wasCHANGED%=CHANGED%
p0 �save_music(�GetStr(Window%+handle%+44))
z SAVING%=�
�" ș GetPointerInfo%, ,block
� block!20=block!12
� block!24=block!16
� block!28=block!0
� block!32=block!4
� block!0=44
� block!12=ref%
�3 block!16=3 :� DataLoad application to filer
� block!40=MusicFileType%
� ș SendMessage,17,block
� AwaitingAck%=�
� �CloseMenu
� �
� 3 : � load file
. �load_music(�GetStr(Window%+handle%+44))
�DataLoadAck(ref%)
$B � 4 :AwaitingAck%=� : � DataLoadAck. End of DataSave protocol
.% � 5 : � open double-clicked file
80 � !(Window%+handle%+40) = MusicFileType% �
B0 �load_music(�GetStr(Window%+handle%+44))
L �DataLoadAck(ref%)
V �
` � &502:�Help(ref%)
j � &400C1:�getmodeinfo(�)
t �
~�
�
�� �Help(ref%)
�� block%,text$
�block%=Window%+handle%
�block%!12 = ref%
�)block%!16 = &503 :� Send help message
�text$=""
�Ȏ block%!32 �
�J � -2 : text$="This is the Maestro icon.|MClick SELECT to open score."
� � ScoreWind_h%
�! �SCORING%�SCRIBE%(drawn%) �
�2 text$="Click SELECT to place item in score"
�
= text$="Select an item in a pane to place on the score"
�
� NotesPane_h%
(' text$="Click SELECT to select a "
2 Ȏ block%!36 �
<" � 0 : text$=text$+"breve."
F& � 1 : text$=text$+"semibreve."
P" � 2 : text$=text$+"minim."
Z$ � 3 : text$=text$+"crochet."
d# � 4 : text$=text$+"quaver."
n' � 5 : text$=text$+"semiquaver."
x+ � 6 : text$=text$+"demisemiquaver."
�/ � 7 : text$=text$+"hemidemisemiquaver."
� text$=""
� �
� � SharpsPane_h%
�' text$="Click SELECT to select a "
� Ȏ block%!36 �
�$ � 0 : text$=text$+"natural."
�" � 1 : text$=text$+"sharp."
�! � 2 : text$=text$+"flat."
�) � 3 : text$=text$+"double-sharp."
�( � 4 : text$=text$+"double-flat."
�. � 5 : text$=text$+"naturalised sharp."
�- � 6 : text$=text$+"naturalised flat."
� 7 : text$=text$+"dot."
' � 8 : text$=text$+"double-dot."
' � 9 : text$=text$+"triple-dot."
"! � 10 : text$=text$+"tie."
,& � 11 : text$=text$+"bar line."
6) � 12 : text$=text$+"treble clef."
@' � 13 : text$=text$+"bass clef."
J+ � 14 : text$=text$+"key signature."
T, � 15 : text$=text$+"time signature."
^ text$=""
h �
r � RestsPane_h%
|' text$="Click SELECT to select a "
� Ȏ block%!36 �
�' � 0 : text$=text$+"breve rest."
�+ � 1 : text$=text$+"semibreve rest."
�' � 2 : text$=text$+"minim rest."
�) � 3 : text$=text$+"crochet rest."
�( � 4 : text$=text$+"quaver rest."
�, � 5 : text$=text$+"semiquaver rest."
�0 � 6 : text$=text$+"demisemiquaver rest."
�4 � 7 : text$=text$+"hemidemisemiquaver rest."
� text$=""
� �
� � InstrWind_h%
� � (block%!36>7) �
� (block%!36<16) �
M text$="Click SELECT or ADJUST to select an instrument for this stave"
�
& � (block%!36<24) �
0I text$="Click SELECT or ADJUST to set the volume of this channel"
:e � � (block%!36<32) text$="Click SELECT or ADJUST to set the stereo position of this channel"
D
�
N �
X �
b � Save_h%
l� � block%!36=2 text$="Drag this icon into a directory window to save it in that directory" � text$="Fill in the file name and then drag the icon into a filer directory window to save it"
v � TimeSig_h%
�I text$="use SELECT and ADJUST on each field to set a time signature"
� � AbortQuery_h%
�, text$="click YES to terminate Maestro"
� �
�$(block%+20)=text$
�(block%!0 = (((20+�(text$)+1)�4)*4)+4
�2$(block%+21+�(text$)) = �(0) :� null-terminate
�3ș SendMessage,17,block% :� acknowledge message
��
�
�� �DataLoadAck(ref%)
�� block
�block=Window%
block!0 = 20
block!12 = ref%
block!16 = 4 :� DataLoadAck
2ș SendMessage,17,block :� acknowledge message
*�
4
>� �redraw_window_request
H� R%
R*ș RedrawWindow%,,Window%+handle% � R%
\ ȕ R%
fF � Window%!handle% = ScoreWind_h% �draw_staves:� PLAYING% �CheckQ
p-� try to avoid interruptions in the music
z, ș GetRectangle%,,Window%+handle% � R%
� �
��
�
�� �place_top_panes(behind)
�� xsize%
�"xsize%=Window%!x1%-Window%!x0%
�RestPBlk%!x1%=Window%!x1%
�&RestPBlk%!x0%=Window%!x0%+xsize%/2
�RestPBlk%!y1%=Window%!y1%
�+RestPBlk%!y0%=RestPBlk%!y1%-PaneHeight%
�RestPBlk%!under%=behind
�3ș OpenWindow%, RestsPane_h%, RestPBlk%+handle%
�NotePBlk%!x0%=Window%!x0%
NotePBlk%!x1%=RestPBlk%!x0%
NotePBlk%!y1%=Window%!y1%
+NotePBlk%!y0%=NotePBlk%!y1%-PaneHeight%
$!NotePBlk%!under%=RestsPane_h%
.3ș OpenWindow%, NotesPane_h%, NotePBlk%+handle%
8�
B
L!� �place_bottom_panes(behind)
V� xsize%
`"xsize%=Window%!x0%+Window%!x1%
jSharpPBlk%!x0%=Window%!x0%
t(SharpPBlk%!x1%=SharpPBlk%!x0%+xsize%
~SharpPBlk%!x1%=Window%!x1%
�SharpPBlk%!y0%=Window%!y0%
�-SharpPBlk%!y1%=SharpPBlk%!y0%+PaneHeight%
�SharpPBlk%!under%=behind
�5ș OpenWindow%, SharpsPane_h%, SharpPBlk%+handle%
��
�
�� �remove_panes
��CloseWindow(NotesPane_h%)
��CloseWindow(SharpsPane_h%)
��CloseWindow(RestsPane_h%)
��
�
� �open_window_request
� bhandle%
bhandle%=Window%!under%
�release
($� Window%!handle%=ScoreWind_h% �
2 � TRANSCRIBE% �
<P� define window stack as top->bottom sharpspane->restspane->notespane->score
FJ� open top or bottom panes before score depending on direction of drag
P, ș GetWindowState%,,SharpPBlk%+handle%
Z> � SharpPBlk%!under%=bhandle% Window%!under%=NotesPane_h%
d, ș GetWindowState%,,ScoreWBlk%+handle%
n$ � Window%!y1%>ScoreWBlk%!y1% �
x! �place_top_panes(bhandle%)
� �
�$ �place_bottom_panes(bhandle%)
� �
� �
�S � PLAYING% � Window%!scx%<>ScoreWBlk%!scx% �:� no user-scrolling while playing
�$ ș OpenWindow%,,Window%+handle%
� ScoreClosed%=�
� � TRANSCRIBE% �
� � bhandle%=-2 �
�2 � find behind handle% if pushed to the back
�- ș GetWindowState%,,ScoreWBlk%+handle%
�# bhandle% = ScoreWBlk%!under%
� �
� re-open all panes
# �place_bottom_panes(bhandle%)
% �place_top_panes(SharpsPane_h%)
"o � bhandle%=ScoreWind_h% ș OpenWindow%,,Window%+handle% :� ensure window is behind panes in unusual case
, �
6Q LHBAR%=�FindBar(ScoreWBlk%!scx%) :� the bar number at the left of the window
@ �
J$ ș OpenWindow%,,Window%+handle%
T �
^�
h
r� �close_window_request
|$ș CloseWindow%,,Window%+handle%
�A� Window%!handle%=ScoreWind_h% �remove_panes : ScoreClosed%=�
��
�
�� �mouse_button_click
��B%,C%,W%,I%,inc%
�#W%=Mouse%!window:I%=Mouse%!icon
�B%=%111�Mouse%!buttons
�+Mouse_X%=Mouse%!x0%:Mouse_Y%=Mouse%!y0%
�� B%=%010 �
� �CheckInstalledVoices
� �StopScoring
�2 � W%=-2 �OpenMenu(IconMenu%) � �OpenMainMenu
��
1� B%=%100 inc%=-1 � � B%=%001 inc%=1 � inc%=0
Ȏ W% �
A� -2 : � ScoreClosed% � � PLAYING% �setup_staves:� :� iconbar
&� AbortQuery_h%
0 � I%=2 �
: �terminate
D �
N �CloseMenu
X% �CloseWindow(AbortQuery_h%)
b �
l� NotesPane_h%
v � I%>=0 �
� �release
� wasSCORING%=�
� stopSCORING%=�
�( �UpdateIcon(SelW%, SelI%, 0, 1<<2)
�" �UpdateIcon(W%, I%, 1<<2, 0)
� SelW%=W%
� SelI%=I%
� �attach(note%+I%,%111000)
� �
�� RestsPane_h%
� � I%>=0 �
� �release
� wasSCORING%=�
stopSCORING%=�
( �UpdateIcon(SelW%, SelI%, 0, 1<<2)
" �UpdateIcon(W%, I%, 1<<2, 0)
SelW%=W%
* SelI%=I%
4 �attach(rest%+I%,%11000)
> �
H� SharpsPane_h%
R � I%>=0 �
\ �release
f wasSCORING%=�
p stopSCORING%=�
z( �UpdateIcon(SelW%, SelI%, 0, 1<<2)
�" �UpdateIcon(W%, I%, 1<<2, 0)
� SelW%=W%
� SelI%=I%
�U � I%<7 �attach(accidental%+I%+1,%1100000) �� I%<11 �attach(dot%+I%-6,%1101000)
�$ � I%=10 �attach(tie%,%1101000)
�% � I%=11 �attach(bar%,%10010000)
�1 � I%=12 �attach(clef%,%100) :� Treble clef
�/ � I%=13 �attach(clef%+3,%100):� Bass clef
�3 � I%=14 �attach(key%,%10) :� key signature
�4 � I%=15 �attach(time%,%1) :� time signature
� �
�� ScoreWind_h%
�3 � B%=%100 �SCORING%�SCRIBE%(drawn%) �put_down
� InstrWind_h%
� chan%, v%
�CheckInstalledVoices
$ �StopScoring
. � I%>=8 �
8 chan%=I%-8
B � chan%<8 �
L& v%=�AttachVoice(chan%, inc%)
V+ ș Sound_AttachVoice, chan%+1, v%
`6 $(VoiceStr%(chan%))=�Voice$(v%), VoiceSize%)
j# Instrument%(chan%,1) = v%
t" �UpdateIcon(W%, I%, 0,0)
~ �
� chan%-=8
� � chan%<8 �
� v%=Volumes%(chan%)
�= v%+=inc% : � v%>NVolumes% v%=NVolumes% �� v%<0 v%=0
� Volumes%(chan%)=v%
�C $(VolumeStr%(chan%))=�Volume$(Volumes%(chan%)), VolSize%)
�" �UpdateIcon(W%, I%, 0,0)
� �
� chan%-=8
� � chan%<8 �
�$ v%=Stereo_Position%(chan%)
�H v%+=inc% : � v%>NStereos% v%=NStereos% �� v%<0 v%=0: � no wrap
$ Stereo_Position%(chan%)=v%
B ș Sound_Stereo,chan%+1,Stereo%(Stereo_Position%(chan%))
Q $(StereoStr%(chan%)) = �Stereo$(Stereo_Position%(chan%), 0), SterSize%)
" �UpdateIcon(W%, I%, 0,0)
( �
2 � MIDIpresent% �
< chan%-=8
F � chan%<8 �
P" v%=MIDIChannel%(chan%)
ZI v%+=inc% : � v%>NMIDIChannels% v%=1 �� v%<1 v%=NMIDIChannels%
d" MIDIChannel%(chan%)=v%
n9 $(MIDIChStr%(chan%)) = �(MIDIChannel%(chan%))
x$ �UpdateIcon(W%, I%, 0,0)
�
�
� �
� �
� �
� �
� �
�
� Save_h%
� �StopScoring
�@ � I%=0 �save_music(�GetStr(SaveText)) : � 0 is OK.
� � I%=2 �
�; � x%, y% : � dragging icon
� Window%!handle%=W%
�+ ș GetWindowState%,,Window%+handle%
& ysize%=Window%!y1%-Window%!y0%
x%=Window%!x0%
y%=Window%!y0%
" !Window% = W%
, Window%!4 = I%
6R ș GetIconInfo%, ,Window% : � returns icon box in right place for drag box
@ Window%!8 += x%
J! Window%!12 += y% + ysize%
T Window%!16 += x%
^! Window%!20 += y% + ysize%
hG� get size in appropriate part of block: parent box=screen boundary
r Window%!24 = 0
| Window%!28 = 0
� Window%!32 = S_Width%
� Window%!36 = S_Height%
� !Window%=0
�* Window%!4=5: � fixed size drag box
� SAVING%=�
� DRAGGING%=�
� ș DragBox, ,Window%
� �
� �CloseWindow(Save_h%)
� �CloseMenu
� �
�
� Load_h%
� �StopScoring
@ � I%=0 �load_music(�GetStr(LoadText)) : � 0 is OK.
�CloseWindow(Load_h%)
�CloseMenu
&� TimeSig_h%
0
Ȏ I% �
:
� 0
D0 TIME_SIG%(0)=1+(TIME_SIG%(0)+inc%+14)�15
N% $BarLength%=�(TIME_SIG%(0)+1)
X
� 1
b, TIME_SIG%(1)=(TIME_SIG%(1)-inc%)�4+2
l* $NoteValue%=�(1<<(TIME_SIG%(1)-1))
v �
�* � I%=0 � I%=1 �UpdateIcon(W%,I%,0,0)
��
��
��
�
�� �CheckInstalledVoices
�,� NewVoice$, NewNVoices%, changedVoices%
�changedVoices%=�
�9ș Sound_InstallVoice � I$,NewNVoices%:NewNVoices%-=1
�<� �assert(NVoices%>0,"No sound voices are installed.") �
�q� MIDIpresent% NewNVoices%+=1:Voice$(NewNVoices%)="":� allow a NULL voice to permit just MIDI on this channel
�� NewNVoices%<>NVoices% �
� changedVoices%=�
NVoices%=NewNVoices%
�
/� NVoices%>MAX_Voices% NVoices%=MAX_Voices%
�R%=1�NVoices%
*$� (MIDIpresent% � R%=NVoices%) �
4 Voice$(R%)=""
>�
H+ ș Sound_InstallVoice,0,R% � NewVoice$
R � NewVoice$<>Voice$(R%) �
\ changedVoices%=�
f Voice$(R%)=NewVoice$
p �
z �
�(�:� find if instruments have changed
�� � changedVoices% �
��R%=0�7
�'ș Sound_AttachVoice,R%+1,0 � L%,S%
�J�S%<1�S%>NVoices% S%=1:� Make sure a voice is attached to all channels
�ș Sound_AttachVoice,L%,S%
�3Instrument%(R%,0)=S_C%(R%)+1:� Instrument stave
�+Instrument%(R%,1)=S%:� Instrument voice
�/$(VoiceStr%(R%)) = �Voice$(S%), VoiceSize%)
�(�UpdateIcon(InstrWind_h%, R%+8, 0,0)
�#�:� Get details of each channel
��SetDefaultChannels
��
G� �AttachVoice(chan%,inc%) :� inc% is +1 or -1 or 0 to find nearest
� stop%,v%,perc%
$�CheckInstalledVoices
.6� NVoices%<2 ș Sound_AttachVoice, chan%+1, 1 : =1
8e� attach next voice to channel. Attach percussion voice only on perc line, and not on stave lines
BGperc%=(�$StaveStr%(chan%),"perc")>0) :� is it a percussion channel?
L(ș Sound_AttachVoice, chan%+1 � , v%
V� v%=0 v%=1
`4ș Sound_AttachVoice, chan%+1, v% :� restore it
j6� check if current voice is valid for this channel
t8� inc%=0 � � (perc% = (�Voice$(v%),"Perc")>0)) � =v%
~� inc or dec until found
�� �(inc%)<>1 � inc%=1
�stop%=v%
��
�6 v%+=inc% : � v%>NVoices% v%=1 �� v%<1 v%=NVoices%
�b � MIDIpresent% � Voice$(v%)="" stop%=v% :� permit NULL voice on any channel if MIDI installed
�3 � (perc% = (�Voice$(v%),"Perc")>0)) � stop%=v%
�=v%
�
�$� �UpdateIcon(W%, I%, st%, msk%)
� Window%!handle%=W%:!Icon%=I%
�#Icon%!state=st%:Icon%!mask=msk%
�:ș SetIconState%,,Window%+handle% : � update icon text
�
� �UserDragBox
� block
(DRAGGING%=�
2%block=Window% :� temporary buffer
<� SAVING% �
F ș GetPointerInfo%, ,block
P block!32=block!4
Z block!28=!block
d block!24=block!16
nB block!20=block!12 : � this is the destination window handle%
x$ block!16=1 : � DataSave
� block!12=0
�) block!36=0 :� don't know file size
� block!40=MusicFileType%
�( $(block+44)=�GetLeafName(SaveText)
� !block = 60
�) ș SendMessage, 17, block, block!20
� �
��
�
�H� �SetBit(test, word, bitnum) : � set or clear bit depending on test
�� test �
� = word � (1<<bitnum)
��
= word � � (1<<bitnum)
�
"� �MenuSelect
,*� item%, n, selection, SoundEnable%,F$
6selection=Window%+handle%
@7ș DecodeMenu, ,CurrentMenu%, selection, MenuString
J+Ȏ �$MenuString,�$MenuString, ".")-1) �
T � "Save"
^ F$=�GetStr(SaveText)
h& � ( �F$,".")=0 � �F$,":")=0 ) �
r0 �OpenWindow(Save_h%, Mouse_X%, Mouse_Y%)
| �
� �save_music(F$)
� �
� � "Staves"
� ș "Hourglass_On"
�) � �$StaveNum%,1)="" $StaveNum%="1"
�! STAVE%=�(�$StaveNum%,1))-1
�" � �$MenuString,"+perc",6) �
�# � PERC%=1 PERC%=0 � PERC%=1
� �
�7 item% = �FindMenuItem("+percussion", StaveMenu%)
�L � (item%>=0) !item% = �SetBit(PERC%=1, !item%, 0):� set or clear tick
� �setup_score
� �start_music
�rescore(0)
ș "Hourglass_Off"
C � "Instruments" : �OpenWindow(InstrWind_h%,Mouse_X%,Mouse_Y%)
& � "Volume"
0 � selection!4>=0 �
:! �SetVolume(selection!4)
D0 �SetMenuTick(VolumeMenu%, selection!4)
N �
X � "Tempo"
b � selection!4>=0 �
l �SetTempo(selection!4)
v/ �SetMenuTick(TempoMenu%, selection!4)
� �
�2 � "Time sig" : TIME_SIG%(0)=�($BarLength%)-1
� � "Key sig"
� � selection!4>=0 �
�+ KEY_SIG%(0) = ((selection!8)>=7)+1
�) KEY_SIG%(1) = �((selection!8)-7)
�. �SetMenuTick(MajorMenu%, selection!8)
�. �SetMenuTick(MinorMenu%, selection!8)
І � KEY_SIG%(1) n=accidental%+2+KEY_SIG%(0) : X%(key%)=(x%(n)+X%(n))*KEY_SIG%(1) � X%(key%)=x%(accidental%+2)+X%(accidental%+2)
�
�
� � "Play"
�j ș Sound_Enable,0 � SoundEnable%:� dont pretend to be able to play if the sound system is disabled
� � SoundEnable%=2 �
SCORING%=�
PLAYING%=� PLAYING%
SCROLLING%=PLAYING%
, � PLAYING% �play_start � �play_stop
*@ � n=�CheckOK("Sound is not enabled. You cannot play", 1)
4
�
> � "Clear"
HM � CHANGED% � � �CheckOK("Are you sure? Current music is unsaved",3) �
R �ClearAllMusic
\$ Window%!handle%=ScoreWind_h%
f+ ș GetWindowState%,,Window%+handle%
p Window%!scx%=0
z LHBAR%=0
�' ș OpenWindow%,,Window%+handle%
�. ș GetWindowState%,,ScoreWBlk%+handle%
�= � "Info" : �OpenWindow(ProgInfo_h%, Mouse_X%, Mouse_Y%)
�L � "Quit" : � CHANGED% �OpenAbortQuery(Mouse_X%, Mouse_Y%) � �terminate
� � "Goto"
�$ Window%!handle%=ScoreWind_h%
�+ ș GetWindowState%,,Window%+handle%
� Window%!scx%=0
�; ș OpenWindow%,,Window%+handle% : � set scroll to 0
�. ș GetWindowState%,,ScoreWBlk%+handle%
�< LHBAR%=0 :� the bar number at the left of the window
� �
�" � (CurrentMenu%=MenuStart) �
! ș GetPointerInfo%,,Mouse%
? � %001 � Mouse%!buttons �OpenMainMenu :� persistent menu
�
$�
.
8 � �SetMenuTick(menu%, this%)
B-� set 1 tick in menu and clear all others
L� n, item%
V n = 0
`item%=menu%+28
j>!item% = �SetBit(n=this%, !item%, 0) :� set or clear tick
t�
~ item% += 24
� n += 1
�? !item% = �SetBit(n=this%, !item%, 0) :� set or clear tick
� � (!item% � &80)
��
�
�� �KeyPressed
�� ThisWindow%
� � !(Window%+handle%+24)=13 �
�! ThisWindow%=Window%!handle%
� Ȏ (ThisWindow%) �
�I � Save_h% : �save_music(�GetStr(SaveText)) : � c/r in save window
�I � Load_h% : �load_music(�GetStr(LoadText)) : � c/r in load window
� Bar_h%
BAR%=�($BarNum%)
3 � BAR%>NBars% BAR%=NBars% �� BAR%<0 BAR%=0
% Window%!handle%=ScoreWind_h%
(, ș GetWindowState%,,Window%+handle%
2% Window%!scx%=PX%(PXn%(BAR%))
<O ș OpenWindow%,,Window%+handle% : � set scroll to requested bar number
F/ ș GetWindowState%,,ScoreWBlk%+handle%
PU LHBAR%=�FindBar(ScoreWBlk%!scx%) :� the bar number at the left of the window
ZU : ș "Wimp_ProcessKey",!(Window%+handle%+24) : � ie. pass on key code in R0
d �
n �CloseWindow(ThisWindow%)
x �CloseMenu
� �
�O ș "Wimp_ProcessKey",!(Window%+handle%+24) : � ie. pass on key code in R0
� �
��
�
�&� �ScrollReq(x_scroll%, y_scroll%)
�G� if bad mode disallow scroll to prevent mode warning being garbled
�� BADMODE% �
�$� Window%!handle%=ScoreWind_h% �
�! � PLAYING%�(x_scroll%<>0) �
� Ȏ �(x_scroll%) �
�+ � 1:Window%!scx%+=x_scroll%*4*Pgap%
�A � 2:Window%!scx%+=(x_scroll%/2)*(Window%!x1%-Window%!x0%)
�
Ȏ �(y_scroll%) �
- � 1:Window%!scy%+=y_scroll%*C_Height%
"A � 2:Window%!scy%+=(y_scroll%/2)*(Window%!y1%-Window%!y0%)
, �
6& ș OpenWindow%, ,Window%+handle%
@, ș GetWindowState%,,ScoreWBlk%+handle%
J ScoreClosed%=�
TR LHBAR%=�FindBar(ScoreWBlk%!scx%) :� the bar number at the left of the window
^ �
h�
r
|� �OpenMainMenu
�� item%
�,item% = �FindMenuItem("Play", MenuStart)
�6� (item%>=0) !item% = �SetBit(PLAYING%, !item%, 0)
�� set or clear tick
�,item% = �FindMenuItem("Goto", MenuStart)
�9� (item%>=0) item%!8 = �SetBit(PLAYING%, item%!8, 22)
�+� shaded bit. disable Goto when playing
�Hș CreateMenu, ,MenuStart, Mouse_X%-C_Width%*4, Mouse_Y%+C_Height%*4
�CurrentMenu%=MenuStart
��CloseWindow(Save_h%)
��CloseWindow(Load_h%)
��
�
� �OpenMenu(Menu%)
� (Menu%=IconMenu%) �
] ș CreateMenu, ,IconMenu%, Mouse_X%-C_Width%*4, 112+C_Height%*2:� y adjusted to line up
&�
0D ș CreateMenu, ,Menu%, Mouse_X%-C_Width%*4, Mouse_Y%+C_Height%
:�
DCurrentMenu%=Menu%
N�CloseWindow(Save_h%)
X�CloseWindow(Load_h%)
b�
l
v� �CloseMenu
�ș CreateMenu, ,-1
��CloseWindow(Save_h%)
��CloseWindow(Load_h%)
��
�
�� �symbol_pointer
�ș GetPointerInfo%,,Mouse%
�A� (Mouse%!window=ScoreWind_h%) �scribe(Mouse%!x0%,Mouse%!y0%)
��
�
�� �StopScoring
�stopSCORING%=�
��release
J�UpdateIcon(SelW%, SelI%, 0, 1<<2) :� remove highlight on icon in pane
�
� �UpdateTitle(T$)
*
� a,b,c,d
4$ScoreTitle%=T$+�(0)
> Window%!handle%=ScoreWind_h%
H'ș GetWindowState%,,Window%+handle%
RTa=Window%!x0% : b=Window%!y1% : c=Window%!x1% :� get title area and force redraw
\(ș GetWindowOutline,,Window%+handle%
fd=Window%!y1%
p-ș ForceRedraw,-1,a+Hi%,b+Vi%,c-Hi%,d+Vi%
z�
�
�� �OpenWindow(h%, x%, y%)
�� xsize%, ysize%
�Window%!handle% = h%
�(ș GetWindowState%, ,Window%+handle%
�"xsize%=Window%!x1%-Window%!x0%
�"ysize%=Window%!y1%-Window%!y0%
�Window%!under%=-1
�Window%!x0%=x%
�"Window%!x1%=Window%!x0%+xsize%
�Window%!y0%=y%
�"Window%!y1%=Window%!y0%+ysize%
�$ș OpenWindow%, ,Window%+handle%
�
� �CloseWindow(h%)
$Window%!handle%=h%
.%ș CloseWindow%, ,Window%+handle%
8�
B
L� �SetExtent(W%)
VScore_Width%=W%
`Window%!x0%=0
j*Window%!y0%=-Score_Height%-PaneHeight%
tWindow%!x1%=Score_Width%
~Window%!y1%=PaneHeight%
�%ș SetExtent,ScoreWind_h%,Window%
��
�
�.� �GetLeafName(name%) :� returns leaf name
�� ch$,n%,name$
�name$=�GetStr(name%)
�3� ( (�name$,".")=0) � (�name$,":")=0) ) �=name$
�n%=�(name$)
�+� scan string to find leaf name of file
��
� ch$= �name$, n%, 1)
� n%-=1
# � (n%<=0 � ch$="." � ch$=":")
$� n%>0 � =�name$, �(name$)-n%-1)
� �GetStr(s%) : � get string
(� n$
2ȕ?s%:n$+=�?s%:s%+=1:�
<=n$
F
P� �SetVolume(R%)
Zș Sound_Volume,Volume%(R%)
d�
n
x� �SetDefaultChannels
�
�S%,P%
�-� only 1 percussion channel permitted now
�_�S%=0�7:S_C%(S%)=Stave_Channels%(STAVE%,S%):�:� Channel assignment changes if staves change
�A� PERC% S_C%(7)=STAVE%+1:� Steal channel for percussion lines
��S%=0�7
� Instrument%(S%,0)=S_C%(S%)+1
��$StaveStr%(S%)=Nth$(Instrument%(S%,0)+(STAVE%-3)*((Instrument%(S%,0)-1)>STAVE%))+";" :� set up stave names in instrument wimdow
�<�UpdateIcon(InstrWind_h%,S%,0,0) :� update text in icons
�v%=�AttachVoice(S%,0)
� ș Sound_AttachVoice,S%+1,v%
�-$(VoiceStr%(S%))=�Voice$(v%), VoiceSize%)
�Instrument%(S%,1) = v%
�'�UpdateIcon(InstrWind_h%,S%+8, 0,0)
�:� Instrument allocation
G� PERC% $StaveStr%(7)=Nth$(5)+";" :� steal 1 channel for percussion
�
"
,� �setup_score
6
�S%,P%
@-� only 1 percussion channel permitted now
J1�setup_staves:� Calculate new stave positions
T�SetDefaultChannels
^�
h
r� �GetFileInfo(F$)
|&�T%,L%,A%,M$,time%,FileType$,r2,r3
�5� F$<>"" șOS_File,5,F$ � T%,,laddr%,eaddr%,L%,A%
�2� (F$="") � ((T%=1) � (A% � 1) � (L%>8)) = 0 �
� $ThisFile%="<untitled>"+�(0)
�$FileSize%=""
�$FileType%=""
�$FileDate%=""
�=�
��
�$ThisFile%=F$
�$FileSize%=�(L%)
� � ((laddr%>>20)�&FFF)=&FFF �
�- � (laddr%>>8 � &FFF) = MusicFileType% �
� FileType$= "Music "
�
8 ș "OS_FSControl",18,,laddr%>>8 � &FFF � ,,r2,r3
X FileType$=�(r2 � &FF) + �((r2>>8) � &FF) + �((r2>>16) � &FF) + �((r2>>24) � &FF)
&[ FileType$ += �(r3 � &FF) + �((r3>>8) � &FF) + �((r3>>16) � &FF) + �((r3>>24) � &FF)
0 �
:/ $FileType%=FileType$+�~(laddr%>>8 � &FFF)
D* time%=Window% :� a convenient buffer
N;� load and execution addresses are, in fact, time stamp
X time%?4=laddr% � &FF
b time%?3=eaddr%>>24 � &FF
l time%?2=eaddr%>>16 � &FF
v time%?1=eaddr%>>8 � &FF
� time%?0=eaddr% � &FF
�> ș "OS_ConvertStandardDateAndTime", time%, FileDate%, 28
�3 � put timestamp of file into file info window
� �
��
�=�
�
�� �load_music(F$)
�� F%,M$
�� �
�I� CHANGED% � � �CheckOK("Are you sure? Current music is unsaved",3) �
�SCORING%=�
�� PLAYING% �play_stop
!SCROLLING%=�
!8� �F$=0 � �7 : T%=�CheckOK("Invalid filename",1) : �
!ș "Hourglass_On"
! Y� � � �7: �("FX 229,1") : �ClearAllMusic : ș "Hourglass_Off" : T%=�CheckOK(�$,1) : �
!*&�("FX 229,0") :� enable escape key
!4
FILE%=�F$
!> M$="":�R%=1�7:M$+=��#FILE%:�
!HB%=�#FILE%
!R�M$="Maestro" �
!\$LoadText=F$+�(0)
!f$SaveText=F$+�(0)
!pn� � �GetFileInfo(F$) � �7 :�("FX 229,1") : ș "Hourglass_Off" :T%=�CheckOK("Invalid or locked file",1) : �
!zT%=�
!�NBars%=0
!�Ȏ �#FILE% �
!�� 0:T%=�
!�� 1
!�*�lTempo:�lInstruments:�lStaves:�lMusic
!�"� File id version 2 and above
!�A%=�
!��
!�N� �#FILE% �lMusic,�lStaves,�lInstruments,�lVolumes,�lStereos,�lTempo �A%=�
!���#FILE%�A%
!��
!��#FILE%:FILE%=�
!��("FX 229,1")
"$Updated%="NO"
"�UpdateTitle(F$)
"ș "Hourglass_Off"
"$ș "Hourglass_On"
". �T% �
"8 �position_staves
"B �start_music
"L �set_score(0)
"V �SetupBarStarts(0)
"` �setup_score
"j3 �update_score(0,-Score_Height%,Score_Width%,0)
"t �StopScoring
"~ CHANGED%=�
"� �
"��
"��("FX 229,1")
"�T%=�
"��
"�5� � T% � �7 : T%=�CheckOK("Invalid music file",1)
"�ș "Hourglass_Off"
"��
"�
"�
� �lMusic
"�
�C%,B%
"��#FILE%,GATE%:GATE%+=MUSIC%
# �C%=0�7
#
+�#FILE%,FINE%(C%):FINE%(C%)+=MUSIC%(C%)
#�
#,B%=MUSIC%:ȕB%<GATE%:?B%=�#FILE%:B%+=1:�
#(�C%=0�7
#24B%=MUSIC%(C%):ȕB%<FINE%(C%):?B%=�#FILE%:B%+=1:�
#<�
#FPP%=MUSIC%:P%()=MUSIC%()
#P�
#Z
#d� �lStaves
#n� item%
#xSTAVE%=�#FILE%
#�$StaveNum%=��(STAVE%+1),1)
#�PERC%=�#FILE%
#�4item% = �FindMenuItem("+percussion", StaveMenu%)
#�E� item%>0 !item%=�SetBit(PERC%=1, !item%, 0) :� set or clear tick
#��
#�
#�� �lInstruments
#��C%, chan%,v%
#��C%=0�7
#�chan%=�#FILE%:� Channel
#�Kv%=�#FILE%�(NVoices%+1):� Write voice numbers allocated to each channel
#�� v%=0 v%=1
#�%ș Sound_AttachVoice, chan%+1, v%
$=v%=�AttachVoice(chan%, 0) :� check if it is a valid voice
$%ș Sound_AttachVoice, chan%+1, v%
$Instrument%(chan%,1) = v%
$"0$(VoiceStr%(chan%))=�Voice$(v%), VoiceSize%)
$,+�UpdateIcon(InstrWind_h%, chan%+8, 0,0)
$6�
$@�
$J
$T� �lVolumes
$^�C%
$h�C%=0�7
$rVolumes%(C%)=�#FILE%
$|"�Volumes%(C%)>7 Volumes%(C%)=7
$�"�Volumes%(C%)<0 Volumes%(C%)=0
$�7$(VolumeStr%(C%))=�Volume$(Volumes%(C%)), VolSize%)
$�O�UpdateIcon(InstrWind_h%, C%+16, 0,0) :� update icon in instrument window
$��
$��
$�
$�� �lStereos
$��C%
$��C%=0�7
$� Stereo_Position%(C%)=�#FILE%
$�6ș Sound_Stereo,C%+1,Stereo%(Stereo_Position%(C%))
$�E$(StereoStr%(C%)) = �Stereo$(Stereo_Position%(C%), 0), SterSize%)
$�)�UpdateIcon(InstrWind_h%, C%+24, 0,0)
%�
%�
%
%&
� �lTempo
%0� t
%:
t=�#FILE%
%D�SetTempo(t)
%N�SetMenuTick(TempoMenu%, t)
%X�
%b
%l� �save_music(F$)
%v
� block,n
%�� �
%�block=Window%
%�$SaveText=F$+�(0)
%�$LoadText=F$+�(0)
%�6� simple check for pathname rather than local name
%�#� ( �F$,".")=0 � �F$,":")=0 ) �
%�B n=�CheckOK("To save, drag the icon to a directory viewer.",1)
%� �
%� �
%�ș "Hourglass_On"
%�D� � � �("FX 229,1") : ș "Hourglass_Off" : T%=�CheckOK(�$,1) : �
%�&�("FX 229,0") :� enable escape key
%�1� CHANGED% � (((laddr%>>20)�&FFF) <> &FFF) �
&* � file changed or wasn't timestamped
& � get current time
&& block?0=3:ș "OS_Word",&0E,block
& laddr%=block?4
&* eaddr%=block!0
&4 �
&>3� force music file type, and preserve timestamp
&H<laddr%=(laddr% � &FF) � (&FFF<<20) � (MusicFileType%<<8)
&R7� I don't know what the length will be, so use zero
&\/ș "OS_File", &07, F$, laddr%, eaddr%, 0, 0
&f-� OPENUP, error if directory or not found
&p!ș "OS_Find", &CC, F$ � FILE%
&z:� timestamp is automatically updated on 1st byte write
&��#FILE%,"Maestro"
&�
�#FILE%,2
&��sMusic
&��sStaves
&��sInstruments
&�
�sVolumes
&�
�sStereos
&��sTempo
&��#FILE%:FILE%=�
&�6� (� CHANGED%) � (((laddr%>>20) � &FFF) = &FFF) �
&�, � file not changed and was timestamped
&�# � preserve original timestamp
&�7 ș OS_File,2,F$,laddr% :� re-stamp with old stamp
'0 ș OS_File,3,F$,,eaddr% :� nb eaddr% in r3
' �
'�("FX 229,1")
'$F%=�GetFileInfo(F$)
'.CHANGED%=�
'8$Updated%="NO"
'B�UpdateTitle(F$)
'L5� � T% � �7 : T%=�CheckOK("Invalid music file",1)
'Vș "Hourglass_Off"
'`�
'j
't
� �sMusic
'~
�C%,B%
'�
�#FILE%,1
'��#FILE%,GATE%-MUSIC%
'��C%=0�7
'� �#FILE%,FINE%(C%)-MUSIC%(C%)
'��
'�,B%=MUSIC%:ȕB%<GATE%:�#FILE%,?B%:B%+=1:�
'��C%=0�7
'�4B%=MUSIC%(C%):ȕB%<FINE%(C%):�#FILE%,?B%:B%+=1:�
'��
'��
'�
'�� �sStaves
(
�#FILE%,2
(
�#FILE%,STAVE%
(�#FILE%,PERC%
(�
((
(2� �sInstruments
(<�C%
(F
�#FILE%,3
(P�C%=0�7
(Z�#FILE%,C%
(d�#FILE%,Instrument%(C%,1)
(n�
(x�
(�
(�� �sVolumes
(��C%
(�
�#FILE%,4
(��C%=0�7
(��#FILE%,Volumes%(C%)
(��
(��
(�
(�� �sStereos
(��C%
(�
�#FILE%,5
(��C%=0�7
) �#FILE%,Stereo_Position%(C%)
)�
)�
)"
),
� �sTempo
)6
�#FILE%,6
)@�#FILE%,Tempo%
)J�
)T
)^7� �sprite(s%,X%,Y%) : � plot sprite S%(s%) at X%,Y%
)hXș SpriteOp%, SprPlot%, SprBlk%, S%(s%), X%-x%(s%),Y%-y%(s%), 8, factors%, pixtrans%
)r4� overwrite screen colour, but using sprite mask
)|�
)�
)�� �float(s%,X%,Y%)
)��R%
)� Window%!handle%=ScoreWind_h%
)�Window%!x1%=X%+X%(s%)
)�X%-=x%(s%)
)�Window%!x0%=X%
)�Window%!y1%=Y%+Y%(s%)
)�Y%-=y%(s%)
)�Window%!y0%=Y%
)�*ș UpdateWindow%,,Window%+handle% � R%
)� X%+=Window%!x0%-Window%!scx%
)� Y%+=Window%!y1%-Window%!scy%
*ChSprite%=S%(s%)
* ȕ R%
* Ȏ s% �
*&. � key% : �float_key_sig(X%,Y%+y%(s%))
*0( � time% : �float_time_sig(X%,Y%)
*:4 : pixtrans%?1 = pixtrans%?0 � pixtrans%?1
*D_ ș SpriteOp%, SprPlot%, SprBlk%, ChSprite%, X%,Y%, 11, factors%, pixtrans%
*N< pixtrans%?1 = pixtrans%?0 � pixtrans%?1
*X �
*b, ș GetRectangle%,,Window%+handle% � R%
*l �
*v�
*�
*�� �float_key_sig(X%,Y%)
*��I%,A%,C%,W%
*��KEY_SIG%(1) �
*�C%=SCRIBE%(sclef%)
*�A%=KEY_SIG%(0)
*�I%=accidental%+2+A%
*�W%=x%(I%)+X%(I%):Y%-=y%(I%)
*�ChSprite%=S%(I%)
*�� I%=0 � KEY_SIG%(1)-1
*�cș SpriteOp%, SprPlot%, SprBlk%, ChSprite%, X%, Y%+Li%*Key_Y%(C%,A%,I%), 3, factors%, pixtrans%
*�
X%+=W%
*��
+�
+Uș SpriteOp%, SprPlot%, SprBlk%, ChSprite%, X%, Y%-y%(s%), 3, factors%, pixtrans%
+�
+ �
+*
+4� �float_time_sig(X%,Y%)
+>ș SetColour, 7 � 3<<4
+HR� �($NoteValue%)>1 � X%+C_Width%�2, Y%+C_Height% � � X%+C_Width%, Y%+C_Height%
+R� $NoteValue%
+\V� �($BarLength%)>1 � X%+C_Width%�2, Y%+2*C_Height% � � X%+C_Width%, Y%+2*C_Height%
+f� $BarLength%
+p�
+z
+�Q� *********************************************************************** REM
+�Q� REM
+�Q� M U S I C T R A N S C R I P T I O N R O U T I N E S REM
+�Q� REM
+�Q� *********************************************************************** REM
+�::
+�� PROCEDURE: start_music
+��
+�/� DESCRIPTION: Initialise to start of music
+�:
+�� �start_music
+�� n%
+�BAR%=0 :� current bar
,EGP%=MUSIC%:� Set current gate pointer to start of displayed music
,N%()=MUSIC%()
,1CLEF%()=0:� Start with base clefs (NOT BASS!)
,$6SIG%(0)=%01100111:� (Default time sig of 4/4 time)
,.VSIG%(1)=%00000010:� (Default of C Major) First displayed signatures attribute byte
,8 PX%=0
,B�PutBarInfo(0)
,L�
,V
,`1� PROCEDURE: note_type(Channel C%, Type T%)
,j
,t� �note_type(C%,T%)
,~ N%(C%)?1=N%(C%)?1�&1F �T%<<5
,��
,�
,�� �note_dots(C%,D%)
,� N%(C%)?1=N%(C%)?1�&E7 �D%<<3
,��
,�
,�� �note_accidental(C%,A%)
,�N%(C%)?1=N%(C%)?1�&F8 �A%
,��
,�
,�7� PROCEDURE: note_line(Channel C%, Stave line L%)
,��
- c� NOTE: If a note's position is not defined or specified as -16, it is treated as a rest
-
-� �note_line(C%,L%)
-!?N%(C%)=?N%(C%)�7 �(16+L%)<<3
-(�
-2
-<� �note_tie(C%,T%)
-F"?N%(C%)=?N%(C%)�&FB �(T%<>0)�4
-P�
-Z
-d� �note_join(C%,J%)
-n"?N%(C%)=?N%(C%)�&FD �(J%<>0)�2
-x�
-�
-�� �note_stem(C%,D%)
-�#?N%(C%)=?N%(C%)�&FE �(D%<>0)�%1
-��
-�
-�� �note_clear(C%)
-�?N%(C%)=0:N%(C%)?1=0
-��
-�
-�>� PROCEDURE: time_sig(Number of beats-1 N%,Beat type B%)
-�
-�� �time_sig(N%,B%)
-�"?GP%=0:GP%?1=Time%�N%<<1�B%<<5
.�
.
."� PROCEDURE: key_sig(Key K%)
."
.,� �key_sig(A%,N%)
.6!?GP%=0:GP%?1=Key%�A%<<2�N%<<3
.@�
.J
.T*� PROCEDURE: clef(Stave S%, Clef C%)
.^
.h� �clef(S%,C%)
.r"?GP%=0:GP%?1=Clef%�C%<<3�S%<<6
.|�
.�
.�
� �bar
.�?GP%=0:GP%?1=Bar%
.��
.�
.�� �insert_gate(W%)
.��G%
.��GP%<GATE% �
.�C�G%=GATE%-W%�GP%�-4:G%!W%=!G%:�:� Shift up by W% from insertion
.�#G%+=3:� Byte before last copied
.�6ȕG%>=GP%:G%?W%=?G%:G%+=�:�:� Clear up in odd word
.��
.�6GATE%+=W%:� Insert gate of size W% into gate queue
/@EP%+=W%:� This assumes gates will only be inserted below EP%
//?GP%=0:GP%?(W%-1)=0:� Zeroise inserted gate
/�
/&
/0� �insert_note(C%)
/:�N%
/D�N%(C%)<FINE%(C%) �
/NB�N%=FINE%(C%)-2�N%(C%)�-4:N%!2=!N%:�:� Shift up from insertion
/XN%+=3:� Last copied word
/b8ȕN%>=N%(C%):N%?2=?N%:N%+=�:�:� Clear up in odd word
/l�
/v5FINE%(C%)+=2:� Insert a note word into this queue
/�#?GP%=?GP%�%1<<C%:� Mark in gate
/��note_clear(C%)
/��
/�::
/�� �delete_gate(W%)
/��G%
/�
GATE%-=W%
/�*�GP%<GATE% �G%=GP%�GATE%�4:!G%=G%!W%:�
/�?EP%-=W%:� This assumes gates will only be deleted below EP%
/��
/�::
/�� �delete_note(C%)
/��N%
0FINE%(C%)-=2
07�N%(C%)<FINE%(C%) �N%=N%(C%)�FINE%(C%)�4:!N%=N%!2:�
0?GP%=?GP%��(%1<<C%)
0 �
0*
04� �allocate_channel(S%)
0>
�C%,c%,G%
0HG%=?GP%
0RC%=-1:c%=7
0\�
0fȕc%>=0�G%�%1<<c%:c%+=�:�
0p�c%>=0 �S_C%(c%)=S% C%=c%
0z c%+=�
0� �c%<0
0�=C%
0�
0�� �arrange_stave(S%)
0�
�C%,B%
0�
B%=�:C%=�
0�#�?GP% ��GP%+=2:�?GP%�GP%>=GATE%
0�ȕB%�GP%<GATE%
0�C%=C%��sort_gate
0��skip_notes(?GP%):GP%+=1
0�.�?GP% �B%=C%:C%=�:�GP%+=2:�?GP%�GP%>=GATE%
0��
0��
1
1� �sort_gate
10�C%,NC%,NN%,G%,g%,pg%,d%,shortest%,Gchanged%
1$shortest%=255
1.G%=?GP%
18g%=�previous_gate(GP%)
1Bpg%=�preceding_gate(GP%)
1L
NC%=-1
1V
NN%=-1
1`�C%=0�7
1j�S_C%(C%)=S% �
1t2NC%+=1:n%(NC%)=C%:�G%�%1<<C% NN%+=1:C%(NN%)=C%
1~A�pg%�%1<<C% d%=N%(C%)?-1>>3�%11100:�d%<shortest% shortest%=d%
1��
1��
1�shortest%=shortest%�%11100
1��g%�NC%>0�NN%>=0 �sort
1�=Gchanged%
1�
1�� �sort
1�
�N%,M%
1�c%()=-1
1�
�M%=0�NC%
1�
�N%=0�NN%
1�-�same_pitch(n%(M%),C%(N%)) c%(N%)=n%(M%)
2 �
2
�
2
�N%=0�NN%
2�c%(N%)<0 c%(N%)=�best
2(�
22
�N%=0�NN%
2<]�C%(N%)=c%(N%) �Gchanged%=�:M%=�in(c%(N%),C%()):�M%>N% �swap_notes(N%,M%) ��move_note(N%)
2F�
2P�
2Z
2d� �swap_notes(N%,M%)
2n
�s%,d%
2xs%=C%(N%):d%=c%(N%)
2�ȔC%(N%),C%(M%)
2�Ȕ?N%(s%),?N%(d%)
2�ȔN%(s%)?1,N%(d%)?1
2��
2�
2�� �move_note(N%)
2�
�s%,d%
2�s%=C%(N%):d%=c%(N%)
2��insert_note(d%)
2�?N%(d%)=?N%(s%)
2�N%(d%)?1=N%(s%)?1
2��delete_note(s%)
2��
3
3� �best
3
�N%,C%
3"#� short%,free%,rest%,any%,tied%
3,� N%=NC%�0�-1
36
C%=n%(N%)
3@�in(C%,c%())<0 �
3J�N%(C%)?-2�4 �
3Ttied%=C%+1
3^�
3h�pg%�%1<<C% �
3r)�(N%(C%)?-1>>3)=shortest% short%=C%+1
3|�N%(C%)?-2�&F8 �rest%=C%+1
3��
3�free%=C%+1
3��
3�
any%=C%+1
3��
3��
3��
3�P�short% C%=short% ��free% C%=free% ��rest% C%=rest% ��any% C%=any% �C%=tied%
3� =C%-1
3�
3�� �in(U%,U%())
3��I%:I%=NN%
3�ȕI%�U%<>U%(I%):I%-=1:�
4=I%+(U%<>U%(I%))
4
4�� �same_pitch(c%,C%):�R%,r%:R%=?N%(C%)�&F8:r%=N%(c%)?-2�&F8:=N%(c%)-2>=MUSIC%(c%)�(g%�(N%(c%)?-2�4)=4)�%1<<c%�(R%�r%)=� �(�<>R%�r%=�)
4&
40� �preceding_gate(gp%)
4:�C%,gm%
4D'�C%=0�7:�S_C%(C%)=S% gm%=gm%�%1<<C%
4N�
4X�
4b
gp%-=1
4l#�gm%�?gp%�gp%<MUSIC%+2�gp%?-1=�
4v =?gp%�gp%>MUSIC%+1�gp%?-1<>�
4�
4�� �previous_gate(gp%)
4��C%,gm%
4�'�C%=0�7:�S_C%(C%)=S% gm%=gm%�%1<<C%
4��
4��
4�
gp%-=1
4�"ȕgp%>MUSIC%�gp%?-1=�:gp%-=2:�
4��gm%�?gp%�gp%<MUSIC%+2
4�=?gp%�gp%>MUSIC%+1
4�
4�� �conflict(T%,S%,L%)
4��C%,co%
5
L%+=16
5C%=7
5�
5 /co%=?GP%�%1<<C%�S_C%(C%)=S%�(?N%(C%)>>3)=L%
5* C%+=�
54
�co%�C%<�
5>=C%-(co%<>0)
5H
5RQ� *********************************************************************** REM
5\Q� REM
5fQ� M U S I C T Y P E S E T T I N G R O U T I N E S REM
5pQ� REM
5zQ� *********************************************************************** REM
5�::
5�3� PROCEDURE: set_score(Starting position PX%)
5��
5�N� DESCRIPTION: Typeset music score from current bar to end of music/screen
5�K� No drawing is done as only the positions are calculated.
5��
5�F� EFFECTS: Stores gate X positions & types in PX%() & PTYPE%()
5�:
5�� �set_score(PX%)
5�ȕ GP%<GATE%
5�?�?GP% �set_notes(?GP%):GP%+=1 ��set_attribute(GP%?1):GP%+=2
5�,�:� Until edge of screen or end of music
5�5EP%=GP%:� Last GP%, pointer to first undrawn gate
6.EX%=PX%:� Last PX%, index to last position
6OPX%(PX%+1)=PX%(PX%)+PW%(PX%)+Pgap%:� Appendation position after last symbol
6O� PX%(PX%+1)>S_Width% �SetExtent(PX%(PX%+1)+100*Hi%) � �SetExtent(S_Width%)
6$PXn%(NBars%)=PX%
6..PTYPE%(PX%+1)=Note%:� Note type by default
68�
6B::
6Lr� NOTE: The key signature is the only attribute that must be kept track of in order to ensure correct setting.
6V�� This is because a naturalising signature consists of the same number of naturals as accidentals in the previous key signature
6`:
6j� �set_attribute(A%)
6t(�A%�PTYPE%(PX%)+� ��A%�PTYPE%(PX%) �
6~&�T%:T%=%1:�A%�%1 ��T%=T%<<1:�A%�T%
6�&PX%(PX%+1)=PX%(PX%)+PW%(PX%)+Pgap%
6�
PX%+=1
6�PTYPE%(PX%)=T%
6�Ȏ T% �
6��Time%:PW%(PX%)=20*Hi%
6� �Key%
6�c�A%�56 T%=accidental%+(A%>>2�%1)+2:SIG%(1)=A% �T%=accidental%+1:ȔA%,SIG%(1):�A%�56 �A%=8:T%+=1
6�&PW%(PX%)=(A%>>3�7)*(x%(T%)+X%(T%))
6�+�Clef%:PW%(PX%)=x%(clef%+3)+X%(clef%+3)
6��Bar%:PW%(PX%)=Hi%*4
6��
6��
7
7
� �set_notes(G%)
7�lx0%,lx1%,ly0%,ly1%
7B�C%,P%,R%,s%:� Channel, Note prefix & remainder widths, Sprite
7( C%=-1
72�
7<�C%-=�:�G%�%1<<C%
7F�bound_note(!N%(C%))
7P%�lx0%>P% P%=lx0%:� Maximum prefix
7Z(�lx1%>R% R%=lx1%:� Maximum remainder
7d$N%(C%)+=2:� Pull from note queue
7n.�(2<<C%)>G%:� Until no more notes (1 bits)
7xnPX%(PX%+1)=PX%(PX%)+PW%(PX%)+Pgap%+P%:� Next position is previous position + width+gap+prefix of next note
7�%PX%+=1:� next note position index
7�#PW%(PX%)=R%:� Width of new note
7�!PTYPE%(PX%)=Note%:� Note type
7��
7�
7�� �bound_note(L%)
7�
�H%,S%,s%
7�H%=L%>>8�&FF
7�?�L%�&F8 S%=H%>>5�L%<<3�8 �S%=rest%�H%>>5:� Note/rest sprite
7�lx0%=x%(S%):� Prefix width
7�lx1%=X%(S%):� Suffix width
7�!ly0%=y%(S%):� Decender height
7�!ly1%=Y%(S%):� Ascender height
8�H%�7 �
8s%=accidental%�H%�7
8'lx0%+=x%(s%):� Add accidental width
8"&�y%(s%)>ly0% ly0%=y%(s%):� Lower ?
8,'�Y%(s%)>ly1% ly1%=Y%(s%):� Higher ?
86�
8@k�H%�24 s%=dot%+(H%>>3�3):lx1%=x%(S%)+X%(s%):�y%(s%)>ly0% ly0%=y%(s%):� Dot adds suffix and may be lower
8J�
8T::
8^� �position_staves
8h� Y%,S%
8r8Score_Height%=(PERC%+1+3*(STAVE%+1)+1)*Stave_Height%
8|%Y%=-Score_Height%-Stave_Height%�2
8�D�PERC% �S%=PERC%�1�-1:Y%+=Stave_Height%:Y_STAVE%(STAVE%+S%)=Y%:�
8�:� S%=STAVE%�0�-1:Y%+=3*Stave_Height%:Y_STAVE%(S%)=Y%:�
8��
8�
8�� �setup_staves
8�� O%
8��position_staves
8�#ScoreWBlk%!handle%=ScoreWind_h%
8�*ș GetWindowState%,,ScoreWBlk%+handle%
8�;ScoreWBlk%!y0%=ScoreWBlk%!y1%-Score_Height%-PaneHeight%
8�3ScoreWBlk%!scx%=0:ScoreWBlk%!scy%=PaneHeight%/2
8�ScoreWBlk%!under%=-1
8�&ș OpenWindow%,,ScoreWBlk%+handle%
9LHBAR%=0
9ScoreClosed%=�
9 Window%!handle%=ScoreWind_h%
9&'ș GetWindowState%,,Window%+handle%
90?� TRANSCRIBE% �place_top_panes(-1): �place_bottom_panes(-1)
9:� PLAYING% �CheckScroll
9D�
9N
9XD� �update_score(Window%!x0%,Window%!y0%,Window%!x1%,Window%!y1%)
9b Window%!handle%=ScoreWind_h%
9l*ș UpdateWindow%,,Window%+handle% � R%
9v ȕ R%
9��:�draw_staves
9�*ș GetRectangle%,,Window%+handle% � R%
9��
9��
9�
9�� PROCEDURE: draw_staves
9��
9�/� DESCRIPTION: Draw current stave structure
9�&� 1 stave for 1 voice
9�(� 2 staves for keyboard
9�4� 3 staves for 1 voice and keyboard
9�.� 4 staves for 4 voice chorus
9�:
:� �draw_staves
:c� Y%,T%,B%,S%,L%:� Y%,T%,B%=Position & Y bounds of each stave, S%=Stave index, L%=Line position
:Q� x%,y%,lx1%:� Virtual coordinates of bottom left & top right of score window
: 6� c%,t%,b%:� Left edge of clip window and Y bounds
:*y%=Window%!y1%-Window%!scy%
:4x%=Window%!x0%-Window%!scx%
:>8lx1%=x%+Score_Width%:� lx1%>Clip%!x1% lx1%=Clip%!x1%
:H%c%=Clip%!x0%:�c%<x%+Hi% c%=x%+Hi%
:Rb%=Clip%!y0%:t%=Clip%!y1%
:\�PERC% �
:f� S%=STAVE%+1�STAVE%+PERC%
:pY%=y%+Y_STAVE%(S%)
:z!�b%<=Y%�t%>=Y% �c%,Y%,lx1%,Y%
:��
:��
:�� S%=0 � STAVE%
:�@Y%=y%+Y_STAVE%(S%):T%=Y%+Stave_Height%�2:B%=T%-Stave_Height%
:��b%<=T%�t%>=B% �
:�)�c%,Y%,lx1%,Y%:� Plot centre bar line
:��L%=Li%*2�L%*2�L%
:�*�c%,Y%+L%,lx1%,Y%+L%:� Plot upper line
:�*�c%,Y%-L%,lx1%,Y%-L%:� Plot lower line
:��
:��
:��
:�B%=Score_Width%
;.T%=Clip%!x1%-x%:�T%<B% B%=T%:� Right bound
;+T%=Clip%!x0%-x%:�T%<0 T%=0:� Left bound
;� � BADMODE% �
;$6 �draw_score(x%,y%,T%,B%):� Draw symbols on stave
;.�
;8$ � Window%!x0%, Window%!y1%-100
;B8 � "Cannot display score in this (256-colour) mode"
;L �
;V�
;`::
;j;� PROCEDURE: draw_score(Based X%,Y%, PX bounds A%,B%)
;t�
;~O� DESCRIPTION: Write current section of music score that appears between A%
;�� and B%
;�:
;�� �draw_score(X%,Y%,A%,B%)
;��PX%
;�
BAR%=0
;�t� must be subtle about redrawing last note (or other item) of score to prevent picking up rubbish data after end
;�� A%>PX%(PXn%(NBars%)) �
;�= � A%>PX%(PXn%(NBars%))+2*Pgap% � � A%=PX%(PXn%(NBars%))
;� �
;�T� ensure bar numbers are always completely updated; but don't lose 1st-note draw
;�� NBars%>2 �
;�,ȕ PX%(PXn%(BAR%+2))<A% � BAR%<=NBars%-1
<