Commit d69459a4 authored by Chris Wraight's avatar Chris Wraight Committed by ROOL
Browse files

Add beaming

Dynamically beam barbed notes (quavers, etc). Beams are added to score on loading a file, and updated as notes are edited. Only notes of the same orientation are beamed and notes in the same chord must be placed in the same column on the score.

Version 2.18. Tagged as 'Maestro-2_18'
parent e7ba1c44
......@@ -13,7 +13,7 @@ https://www.riscosopen.org/wiki/documentation/show/File%20formats:%20MusicFile
Note blocks in memory are 1 word in size. The first two
bytes are as in the file specification. The remaining
two bytes hold additional typestting information.
two bytes hold additional typesetting information.
Byte 2
------
......@@ -21,7 +21,10 @@ Byte 2
1 Note is top of chord
2 Note is bottom of chord
3 Note is staggered
4-7 Reserved
4 Note is first member of a beaming group
5 Note is middle member of beaming group
6 Note is last member of beaming group
7 Reserved
Byte 3
------
......
/* (2.17)
/* (2.18)
*
* This file is automatically maintained by srccommit, do not edit manually.
*
*/
#define Module_MajorVersion_CMHG 2.17
#define Module_MajorVersion_CMHG 2.18
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 27 Mar 2021
#define Module_Date_CMHG 27 Oct 2021
#define Module_MajorVersion "2.17"
#define Module_Version 217
#define Module_MajorVersion "2.18"
#define Module_Version 218
#define Module_MinorVersion ""
#define Module_Date "27 Mar 2021"
#define Module_Date "27 Oct 2021"
#define Module_ApplicationDate "27-Mar-21"
#define Module_ApplicationDate "27-Oct-21"
#define Module_ComponentName "Maestro"
#define Module_FullVersion "2.17"
#define Module_HelpVersion "2.17 (27 Mar 2021)"
#define Module_LibraryVersionInfo "2:17"
#define Module_FullVersion "2.18"
#define Module_HelpVersion "2.18 (27 Oct 2021)"
#define Module_LibraryVersionInfo "2:18"
......@@ -759,11 +759,12 @@ CASE icon% OF
SYS "Hourglass_On"
PROCposition_staves
PROCSetExtent(S_Width%)
PROCscore_init
PROCSetDefaultChannels
PROCstart_music
PROCSetDefaultChannels
PROCset_score(0)
PROCSetupBarStarts(0)
PROCbeam_score
PROCscore_init
SYS "Hourglass_Off"
PROCChangedScore
IF button%=4 THEN PROCwimp_closedialogue(StaveWind_h%,ScoreWind_h%)
......@@ -1361,6 +1362,7 @@ IFM$="Maestro" THEN
PROCSetDefaultChannels
PROCset_score(0)
PROCSetupBarStarts(0)
PROCbeam_score
PROCscore_init
PROCscore_update(0,-Score_Height%,Score_Width%,0)
PROCStopScoring
......@@ -1840,6 +1842,171 @@ FOR n%=0 TO 7
NEXT
ENDPROC
REM // Beam //
DEF PROCbeam_score
REM Add beaming data to a typeset score
LOCAL b%
IF NBars%>1 THEN
FOR b%=1 TO NBars%-1
PROCGetBarInfo(b%)
PROCbeam_bar
NEXT
ENDIF
ENDPROC
DEF PROCbeam_bar
REM Set beaming data for all Notes in a bar
LOCAL type%,c%,orient%,stave%,blen%,len%,shortest%,tptr%,res%
blen%=FNbeam_measure(SIG%(0))
BeamPos%()=0 :REM For each channel, position within beaming measure
BeamIn%()=0 :REM Per stave/orientation: are we in a beaming group?
WHILE GP%<GateBlock%+GateSize% AND type%<>Bar%
REM Notes: Step through and add beaming data
IF GP%?0 THEN
REM Add length values for notes in Gate
shortest%=0
FOR c%=0 TO 7
IF (GP%?0 AND 1<<c%) THEN
len%=Length%(N%(c%)?1>>3)
BeamPos%(c%)+=len%
IF shortest%=0 OR len%<shortest% THEN shortest%=len%
REM Clear any beaming data that might already exist
N%(c%)?2=N%(c%)?2 AND NOT &70
ELSE
res%=BeamPos%(c%)-tptr%
IF res%>0 AND (shortest%=0 OR res%<shortest%) THEN shortest%=res%
ENDIF
NEXT
REM Advance position and check for measure wrap
tptr%+=shortest%
IF tptr%>blen% THEN
tptr%=tptr%-blen%
FOR c%=0 TO 7
BeamPos%(c%)-=blen%
IF BeamPos%(c%)<0 THEN BeamPos%(c%)=0
NEXT
ENDIF
REM Add length values for empty note slots
FOR c%=0 TO 7
IF (GP%?0 AND 1<<c%)=0 AND BeamPos%(c%)<=tptr%-shortest% THEN BeamPos%(c%)+=shortest%
NEXT
REM Add beaming data
FOR c%=0 TO 7
IF (GP%?0 AND 1<<c%) THEN
orient%=(N%(c%)?0 AND &1)
stave%=S_C%(c%)
REM Is Note is a candidate for beaming?
IF FNbeam_check(N%(c%)) THEN
REM If so, and still within group, scan ahead for Notes to match
IF BeamPos%(c%)<blen% AND FNbeam_match(stave%,orient%,blen%,tptr%) THEN
REM We have a match: set data for first Note
IF BeamIn%(stave%,orient%)=FALSE THEN
REM Set as first member of beaming group
BeamIn%(stave%,orient%)=TRUE
N%(c%)?2=N%(c%)?2 OR &10
ELSE
REM Set as middle member of beaming group
N%(c%)?2=N%(c%)?2 OR &20
ENDIF
ELSE
REM No match ahead: set as final member of beaming group
IF BeamIn%(stave%,orient%) THEN
N%(c%)?2=N%(c%)?2 OR &40
BeamIn%(stave%,orient%)=0
ENDIF
ENDIF
ENDIF
ENDIF
NEXT
REM Increment Note pointers
FOR c%=0 TO 7
IF (GP%?0 AND 1<<c%) THEN N%(c%)+=NSIZE%
NEXT
GP%+=1
ELSE
REM Attributes: Look out for Barlines and Time signatures
type%=FNgate_type(GP%)
IF type%=Time% THEN blen%=FNbeam_measure(GP%?1)
GP%+=2
ENDIF
ENDWHILE
ENDPROC
DEF FNbeam_measure (sig%)
REM Return beaming measure for a time signature
LOCAL note%,beats%
beats%=((sig% AND &0E)>>1)+1
note%=(sig% AND &E0)>>5
IF (beats% MOD 3)=0 AND beats%>3 THEN
REM Compound time
=(1024>>note%)*3
ELSE
REM All other times
=1024>>note%
ENDIF
DEF FNbeam_check (n%)
REM Returns TRUE if a note is a candidate for beaming:
REM - is not a rest
REM - is a quaver length or shorter
REM - is either standalone, or the 'barb-end' of a chord
LOCAL cand%,type%,length%,barb%,be%
REM Get note type and length
type%=(n%?0 AND &F8)
length%=(n%?1 AND &E0)>>5
REM In a chord? If so, check if at the 'barb-end'
IF (n%?2 AND &1) THEN
be%=(n%?0 AND &1)+1
IF (n%?2 AND 1<<be%) THEN barb%=TRUE
ELSE
barb%=TRUE
ENDIF
IF type%>0 AND length%>3 AND barb%=TRUE THEN cand%=TRUE
=cand%
DEF FNbeam_match (stave%,orient%,blen%,tp%)
REM Returns TRUE if a second note matches a first
LOCAL match%,i%,break%,len%,shortest%,res%
REM Store Gate and Note pointers
PROCsavp
REM Scan forward until a match is hit, or end of beam group/barline
REPEAT
IF GP%+1<GateBlock%+GateSize% THEN
PROCskip_gate
IF GP%?0 THEN
shortest%=0
FOR i%=0 TO 7
IF (GP%?0 AND 1<<i%) THEN
len%=Length%(N%(i%)?1>>3)
IF shortest%=0 OR len%<shortest% THEN shortest%=len%
REM Is next object a note, with same orientation and stave?
IF (N%(i%)?0 AND &F8) AND (N%(i%)?0 AND &1)=orient% AND S_C%(i%)=stave% THEN
REM Is it a candidate for beaming and within a beaming group?
IF FNbeam_check(N%(i%)) AND tp%+len%<=blen% THEN
REM Match found: notes can be beamed
match%=TRUE
break%=TRUE
ENDIF
ENDIF
ELSE
res%=BeamPos%(i%)-tptr%
IF res%>0 AND (shortest%=0 OR res%<shortest%) THEN shortest%=res%
ENDIF
NEXT
IF match%=0 THEN tp%+=shortest%
IF tp%>=blen% THEN break%=TRUE
ELSE
IF FNgate_type(GP%)=Bar% THEN break%=TRUE
ENDIF
ELSE
break%=TRUE
ENDIF
UNTIL break%
REM Restore pointers
PROCrstp
=match%
DEF PROCtime_sig(N%,B%)
?GP%=0:GP%?1=Time%ORN%<<1ORB%<<5
ENDPROC
......@@ -1873,14 +2040,11 @@ IF GateSize%>bsize%-4 THEN
ENDIF
GP%=GateBlock%+goff%
EP%=GateBlock%+GateSize%
PROCsavp
PROCSetupBarStarts(0)
PROCrstp
SCRIBE%(sgp%)=GP%
ENDIF
IF GP%<EP% THEN
REM Shift data up by gsize% from insertion point
FOR ptr%=GateBlock%+GateSize% TO GP% STEP -4
FOR ptr%=GateBlock%+GateSize%-4 TO GP% STEP -4
ptr%!gsize%=!ptr%
NEXT
REM Clear up odd word
......@@ -1910,9 +2074,6 @@ IF NoteSize%(c%)>bsize%-NSIZE% THEN
=FALSE
ENDIF
N%(c%)=NoteBlock%(c%)+noff%
PROCsavp
PROCSetupBarStarts(0)
PROCrstp
SCRIBE%(c%)=N%(c%)
ENDIF
end%=NoteBlock%(c%)+NoteSize%(c%)
......@@ -1954,6 +2115,22 @@ ENDIF
GP%?0=GP%?0 AND NOT (1<<c%)
ENDPROC
DEF FNgate_type (g%)
REM Returns a Gate's type
REM 0=Note, 1=TimeSig, 2=KeySig, 4=Clef, 8=Slur, 16=Octave, 32=Bar
LOCAL t%
IF g%?0 THEN
ELSE
t%=Time%
IF (g%?1 AND t%) THEN
ELSE
REPEAT
t%=t%<<1
UNTIL (g%?1 AND t%)
ENDIF
ENDIF
=t%
DEF FNallocate_channel (s%)
LOCAL channel%,c%
channel%=-1
......@@ -2113,13 +2290,6 @@ REM REM
REM 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
REM REM
REM *********************************************************************** REM
::
REM PROCEDURE: set_score(Starting position PX%)
REM
REM DESCRIPTION: Typeset music score from current bar to end of music/screen
REM No drawing is done as only the positions are calculated.
REM
REM EFFECTS: Stores gate X positions & types in PX%() & PTYPE%()
:
DEF PROCset_score(PX%)
WHILE GP%<GateBlock%+GateSize%
......@@ -2548,15 +2718,10 @@ REPEAT
line%=(n0%>>3)-16
type%=n1%>>5
s%=type% OR n0%<<3 AND &8
offset%=0
barb%=0
offset%=FNdraw_getoffset
REM Is note in a chord?
IF (n2% AND &1) THEN
REM Note is in a chord
REM Check stagger position
IF (n2% AND &8) THEN
IF (n0% AND &1)=0 THEN offset%=X%(3)-4
IF (n0% AND &1) THEN offset%=-X%(3)+4
ENDIF
REM If a stemmed note:
REM - only draw barbs if at the 'barb-end' of a chord
REM - use a reverse sprite if barbed note is staggered
......@@ -2569,9 +2734,11 @@ REPEAT
IF type%=2 THEN s%=SPR_MHEAD% ELSE s%=SPR_CHEAD%
ENDIF
ENDIF
ELSE
REM Note is on its own: check stagger position
IF (n2% AND &8) THEN offset%=X%(s%)-4
ENDIF
REM Is note in a beaming group? If so, draw beaming lines
IF (n2% AND &70) THEN
s%=SPR_CHEAD%
IF (n2% AND &10) THEN PROCdraw_beams(c%)
ENDIF
REM Draw note
PROCsprite(s%,x%+offset%,y%+(line%*Li%))
......@@ -2603,6 +2770,102 @@ REPEAT
UNTIL (2<<c%)>g%
ENDPROC
DEF FNdraw_getoffset
REM Returns the x offset for a staggered note
LOCAL o%
IF (N%(c%)?2 AND &1) THEN
REM Note is in a chord
IF (N%(c%)?2 AND &8) THEN
IF (N%(c%)?0 AND &1)=0 THEN o%=X%(3)-4
IF (N%(c%)?0 AND &1) THEN o%=-X%(3)+4
ENDIF
ELSE
REM Note is on its own
IF (N%(c%)?2 AND &8) THEN o%=X%(3)-4
ENDIF
=o%
DEF PROCdraw_beams (c%)
REM Draw lines for a beamed group
LOCAL orient%,stave%,side%,i%,k%,n%,ingroup%,edge%
LOCAL y%,ystem%,yo%,width%,barbs1%,barbs2%,break%
REM Store Gate and Note pointers
PROCsavp
REM Store x/y positions of first note
y%=Y%+Y_STAVE%(S_C%(c%))
BeamData%()=0
BeamData%(0,0)=X%+PX%(PX%)+FNdraw_getoffset
BeamData%(0,1)=y%+((N%(c%)?0>>3)-16)*Li%
BeamData%(0,2)=(N%(c%)?1>>5)-3
orient%=(N%(c%)?0 AND &1)
stave%=S_C%(c%)
edge%=BeamData%(0,1)
REM Scan rest of beaming group and store x/y positions
REPEAT
PROCskip_gate
FOR c%=0 TO 7
IF GP%?0 AND (1<<c%) THEN
ingroup%=(N%(c%)?2 AND &70)>>4
IF (N%(c%)?0 AND &1)=orient% AND S_C%(c%)=stave% AND ingroup%>0 THEN
i%+=1
BeamData%(i%,0)=X%+PX%(PX%)+FNdraw_getoffset
BeamData%(i%,1)=y%+((N%(c%)?0>>3)-16)*Li%
BeamData%(i%,2)=(N%(c%)?1>>5)-3
IF orient%=0 THEN
IF BeamData%(i%,1)>edge% THEN edge%=BeamData%(i%,1)
ELSE
IF BeamData%(i%,1)<edge% THEN edge%=BeamData%(i%,1)
ENDIF
IF ingroup%=&4 THEN break%=TRUE
ENDIF
ENDIF
NEXT
UNTIL break%
REM Draw beams
IF orient%=0 THEN ystem%=52:side%=22:yo%=12 ELSE ystem%=-52:yo%=-12
FOR n%=0 TO i%
MOVE BeamData%(n%,0)+side%,BeamData%(n%,1)
DRAW BeamData%(n%,0)+side%,edge%+ystem%
IF n%<i% THEN
width%=BeamData%(n%+1,0)-BeamData%(n%,0)
barbs1%=BeamData%(n%,2)
barbs2%=BeamData%(n%+1,2)
REM Notes in pair are of equal length
IF barbs1%=barbs2% THEN
FOR k%=0 TO barbs1%-1
RECTANGLE FILL BeamData%(n%,0)+side%,edge%+ystem%-(k%*yo%),width%,4
NEXT
ENDIF
REM First note is longer
IF barbs1%<barbs2% THEN
FOR k%=0 TO barbs1%-1
RECTANGLE FILL BeamData%(n%,0)+side%,edge%+ystem%-(k%*yo%),width%,4
NEXT
IF n%<i%-1 THEN comp%=BeamData%(n%+2,2) ELSE comp%=0
IF comp%<barbs2% THEN
FOR k%=0 TO barbs2%-1
RECTANGLE FILL BeamData%(n%+1,0)+side%,edge%+ystem%-(k%*yo%),-20,4
NEXT
ENDIF
ENDIF
REM Second note is longer
IF barbs1%>barbs2% THEN
FOR k%=0 TO barbs2%-1
RECTANGLE FILL BeamData%(n%,0)+side%,edge%+ystem%-(k%*yo%),width%,4
NEXT
IF n%>0 THEN comp%=BeamData%(n%-1,2) ELSE comp%=0
IF comp%<barbs2% THEN
FOR k%=0 TO barbs1%-1
RECTANGLE FILL BeamData%(n%,0)+side%,edge%+ystem%-(k%*yo%),20,4
NEXT
ENDIF
ENDIF
ENDIF
NEXT
REM Restore pointers
PROCrstp
ENDPROC
DEF PROCdraw_cstem (g%,x%,y%,c%,type%,orient%)
REM Draw the stem section of a chord
LOCAL nc%,norient%,ntype%,nl%,ny%,b%
......@@ -3061,17 +3324,25 @@ IF GP%>GateBlock% THEN
ENDIF
ENDPROC
DEF PROCrescore(px%)
IFpx% ELSEPROCstart_music:REM Bar position assumed set, but reset if px%=0
PROCset_score(px%)
IF px%=0 PROCSetupBarStarts(0) ELSE PROCSetupBarStarts(FNFindBar(px%))
PROCscore_update(PX%(px%),-Score_Height%,Score_Width%,0)
DEF PROCrescore (p%)
LOCAL b%
b%=FNFindBar(PX%(p%))
IF p% ELSEPROCstart_music:REM Bar position assumed set, but reset if px%=0
PROCset_score(p%)
REM Cache bar starts
IF p%=0 PROCSetupBarStarts(0) ELSE PROCSetupBarStarts(b%)
REM Update beaming data for bar
PROCGetBarInfo(b%)
PROCbeam_bar
REM Update Score window
PROCscore_update(PX%(PXn%(b%)),-Score_Height%,Score_Width%,0)
ENDPROC
DEF PROCupdate_note
REM Re-typeset score when note width changes
LOCAL oldx%,newx%,shift%,px%
px%=PX%
LOCAL oldx%,newx%,shift%,p%,b%
p%=PX%
b%=FNFindBar(PX%(p%))
REM Update current note width
oldx%=PX%(PX%+1)+PW%(PX%+1)+Pgap%
PROCset_notes(GP%?0)
......@@ -3093,13 +3364,17 @@ IF shift% THEN
PXn%(NBars%)=PX%
PTYPE%(PX%+1)=Note%
ENDIF
REM Cache bar starts and update Score window
IF px%=0 THEN
REM Cache bar starts
IF p%=0 THEN
PROCSetupBarStarts(0)
ELSE
PROCSetupBarStarts(FNFindBar(px%))
PROCSetupBarStarts(b%)
ENDIF
PROCscore_update(PX%(px%),-Score_Height%,Score_Width%,0)
REM Update beaming data for bar
PROCGetBarInfo(b%)
PROCbeam_bar
REM Update Score window
PROCscore_update(PX%(PXn%(b%)),-Score_Height%,Score_Width%,0)
ENDPROC
DEF PROCattach(s%,V%)
......@@ -4163,7 +4438,11 @@ MIDI_OFF%(0,0)=-1
MIDI_OFF%(1,0)=-1
MIDI_Notenum%=0
Pgap%=X%(2)DIV2:REM Symbol spacing (half note blob width)
REM Typesetting
Pgap%=X%(2) DIV 2 :REM Symbol spacing (half note blob width)
DIM BeamPos%(7) :REM Position within a bar's beaming measure
DIM BeamIn%(Max_Stave%,1) :REM Flag if note is within a beaming group
DIM BeamData%(255,2) :REM Store note x/y positions for a beaming group
ENDPROC
DEF PROCinitialise_options
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment