Skip to content

Commit d725901

Browse files
committed
update to latest xml2abc and abc2xml from wim.vree.org + fixes #89 and #78 for the rcsizer not yet included
1 parent d98cde5 commit d725901

7 files changed

+205
-143
lines changed

abc2xml.py

+49-25
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
except: import xml.etree.ElementTree as E
2323
import types, sys, os, re, datetime
2424

25-
VERSION = 233
25+
VERSION = 245
2626

2727
python3 = sys.version_info[0] > 2
2828
lmap = lambda f, xs: list (map (f, xs)) # eager map for python 3
@@ -31,16 +31,27 @@
3131
list_type = list
3232
str_type = str
3333
uni_type = str
34+
stdin = sys.stdin.buffer if sys.stdin else None # read binary if stdin available!
3435
else:
3536
int_type = types.IntType
3637
list_type = types.ListType
3738
str_type = types.StringTypes
3839
uni_type = types.UnicodeType
40+
stdin = sys.stdin
3941

42+
info_list = [] # diagnostic messages
4043
def info (s, warn=1):
4144
x = (warn and '-- ' or '') + s
42-
try: sys.stderr.write (x + '\n')
43-
except: sys.stderr.write (repr (x) + '\n')
45+
info_list.append (x + '\n') # collect messages
46+
if __name__ == '__main__': # only write to stdout when called as main progeam
47+
try: sys.stderr.write (x + '\n')
48+
except: sys.stderr.write (repr (x) + '\n')
49+
50+
def getInfo (): # get string of diagnostic messages, then clear messages
51+
global info_list
52+
xs = ''.join (info_list)
53+
info_list = []
54+
return xs
4455

4556
def abc_grammar (): # header, voice and lyrics grammar for ABC
4657
#-----------------------------------------------------------------
@@ -86,13 +97,13 @@ def abc_grammar (): # header, voice and lyrics grammar for ABC
8697
# ABC lyric lines (white space sensitive)
8798
#----------------------------------------
8899

89-
skip_note = oneOf ('* - ~')
100+
skip_note = oneOf ('* -')
90101
extend_note = Literal ('_')
91102
measure_end = Literal ('|')
92-
syl_str = CharsNotIn ('*~-_| \t\n\\]')
103+
syl_str = CharsNotIn ('*-_| \t\n\\]')
93104
syl_chars = Combine (OneOrMore (syl_str | Regex (r'\\.')))
94105
white = Word (' \t')
95-
syllable = Combine (Optional ('~') + syl_chars + ZeroOrMore (Literal ('~') + syl_chars)) + Optional ('-')
106+
syllable = syl_chars + Optional ('-')
96107
lyr_elem = (syllable | skip_note | extend_note | measure_end) + Optional (white).suppress ()
97108
lyr_line = Optional (white).suppress () + ZeroOrMore (lyr_elem)
98109

@@ -589,7 +600,7 @@ def splitHeaderVoices (abctext):
589600
voices.append ((id, voice))
590601
return header, voices
591602

592-
def mergeMeasure (m1, m2, slur_offset, voice_offset, rOpt, is_grand=0):
603+
def mergeMeasure (m1, m2, slur_offset, voice_offset, rOpt, is_grand=0, is_overlay=0):
593604
slurs = m2.findall ('note/notations/slur')
594605
for slr in slurs:
595606
slrnum = int (slr.get ('number')) + slur_offset
@@ -606,20 +617,22 @@ def mergeMeasure (m1, m2, slur_offset, voice_offset, rOpt, is_grand=0):
606617
dur1 = sum (int (n.find ('duration').text) for n in ns
607618
if n.find ('grace') == None and n.find ('chord') == None)
608619
dur1 -= sum (int (b.text) for b in m1.findall ('backup/duration'))
609-
nns, es = 0, [] # nns = number of real notes in m2
620+
repbar, nns, es = 0, 0, [] # nns = number of real notes in m2
610621
for e in list (m2): # scan all elements of m2
611622
if e.tag == 'attributes':
612623
if not is_grand: continue # no attribute merging for normal voices
613-
else: nns += 1 # but we do merge (clef) attributes for a grand staff
624+
else: nns += 1 # but we do merge (clef) attributes for a grand staff
614625
if e.tag == 'print': continue
615626
if e.tag == 'note' and (rOpt or e.find ('rest') == None): nns += 1
627+
if e.tag == 'barline' and e.find ('repeat') != None: repbar = e;
616628
es.append (e) # buffer elements to be merged
617629
if nns > 0: # only merge if m2 contains any real notes
618630
if dur1 > 0: # only insert backup if duration of m1 > 0
619631
b = E.Element ('backup')
620632
addElem (m1, b, level=3)
621633
addElemT (b, 'duration', str (dur1), level=4)
622634
for e in es: addElem (m1, e, level=3) # merge buffered elements of m2
635+
elif is_overlay and repbar: addElem (m1, repbar, level=3) # merge repeat in empty overlay
623636

624637
def mergePartList (parts, rOpt, is_grand=0): # merge parts, make grand staff when is_grand true
625638

@@ -662,11 +675,16 @@ def mergeParts (parts, vids, staves, rOpt, is_grand=0):
662675
vidsnew.append (vids [pixs[0]])
663676
return partsnew, vidsnew
664677

665-
def mergePartMeasure (part, msre, ovrlaynum, rOpt): # merge msre into last measure of part, only for overlays
666-
slurs = part.findall ('measure/note/notations/slur') # find highest slur num in part
667-
slur_max = max ([int (slr.get ('number')) for slr in slurs] + [0])
678+
def mergePartMeasure (part, msre, ovrlaynum, rOpt): # merge msre into last measure of part, only for overlays
679+
slur_offset = 0; # slur numbers determined by the slurstack size (as in a single voice)
668680
last_msre = list (part)[-1] # last measure in part
669-
mergeMeasure (last_msre, msre, slur_max, ovrlaynum, rOpt) # voice offset = s.overlayVNum
681+
mergeMeasure (last_msre, msre, slur_offset, ovrlaynum, rOpt, is_overlay=1) # voice offset = s.overlayVNum
682+
683+
def pushSlur (boogStapel, stem):
684+
if stem not in boogStapel: boogStapel [stem] = [] # initialize slurstack for stem
685+
boognum = sum (map (len, boogStapel.values ())) + 1 # number of open slurs in all (overlay) voices
686+
boogStapel [stem].append (boognum)
687+
return boognum
670688

671689
def setFristVoiceNameFromGroup (vids, vdefs): # vids = [vid], vdef = {vid -> (name, subname, voicedef)}
672690
vids = [v for v in vids if v in vdefs] # only consider defined voices
@@ -826,7 +844,7 @@ def __init__ (s):
826844
def reset (s, fOpt=False):
827845
s.divisions = 2520 # xml duration of 1/4 note, 2^3 * 3^2 * 5 * 7 => 5,7,9 tuplets
828846
s.ties = {} # {abc pitch tuple -> alteration} for all open ties
829-
s.slurstack = [] # stack of open slur numbers
847+
s.slurstack = {} # stack of open slur numbers per (overlay) voice
830848
s.slurbeg = [] # type of slurs to start (when slurs are detected at element-level)
831849
s.tmnum = 0 # time modification, numerator
832850
s.tmden = 0 # time modification, denominator
@@ -1059,6 +1077,7 @@ def cmpNormType (s, rdvs, lev): # compute the normal-type of a tuplet (only need
10591077
def doNotations (s, n, decos, ptup, alter, tupnotation, tstop, nt, lev):
10601078
slurs = getattr (n, 'slurs', 0) # slur ends
10611079
pts = getattr (n, 'pitches', []) # all chord notes available in the first note
1080+
ov = s.overlayVnum # current overlay voice number (0 for the main voice)
10621081
if pts: # make list of pitches in chord: [(pitch, octave), ..]
10631082
if type (pts.pitch) == pObj: pts = [pts.pitch] # chord with one note
10641083
else: pts = [tuple (p.t[-2:]) for p in pts.pitch] # normal chord
@@ -1074,8 +1093,7 @@ def doNotations (s, n, decos, ptup, alter, tupnotation, tstop, nt, lev):
10741093
ntelm.remove (e) # delete start tie element
10751094
e = [t for t in nts.findall ('tied') if t.get ('type') == 'start'][0] # get the tied start element
10761095
e.tag = 'slur' # convert tie into slur
1077-
slurnum = len (s.slurstack) + 1
1078-
s.slurstack.append (slurnum)
1096+
slurnum = pushSlur (s.slurstack, ov)
10791097
e.set ('number', str (slurnum))
10801098
if slurs: slurs.t.append (')') # close slur on this note
10811099
else: slurs = pObj ('slurs', [')'])
@@ -1130,14 +1148,13 @@ def doNotations (s, n, decos, ptup, alter, tupnotation, tstop, nt, lev):
11301148
if rest: info ('unhandled note decorations: %s' % rest)
11311149
if slurs: # these are only slur endings
11321150
for d in slurs.t: # slurs to be closed on this note
1133-
if not s.slurstack: break # no more open old slurs
1134-
slurnum = s.slurstack.pop ()
1151+
if not s.slurstack.get (ov, 0): break # no more open old slurs for this (overlay) voice
1152+
slurnum = s.slurstack [ov].pop ()
11351153
slur = E.Element ('slur', number='%d' % slurnum, type='stop')
11361154
addElem (nots, slur, lev + 1)
11371155
while s.slurbeg: # create slurs beginning on this note
11381156
stp = s.slurbeg.pop (0)
1139-
slurnum = len (s.slurstack) + 1
1140-
s.slurstack.append (slurnum)
1157+
slurnum = pushSlur (s.slurstack, ov)
11411158
ntn = E.Element ('slur', number='%d' % slurnum, type='start')
11421159
if '.' in stp: ntn.set ('line-type', 'dotted')
11431160
if ',' in stp: ntn.set ('placement', 'below')
@@ -1297,6 +1314,7 @@ def staffDecos (s, decos, maat, lev):
12971314
elif d in ['/-','//-','///-','////-']: # duplet tremolo sequence
12981315
s.tmnum, s.tmden, s.ntup, s.trem, s.intrem = 2, 1, 2, len (d) - 1, 1
12991316
elif d in ['/','//','///']: s.trem = - len (d) # single note tremolo
1317+
elif d == 'rbstop': s.rbStop = 1; # sluit een open volta aan het eind van de maat
13001318
else: s.nextdecos.append (d) # keep annotation for the next note
13011319

13021320
def doFields (s, maat, fieldmap, lev):
@@ -1578,6 +1596,7 @@ def mkMeasure (s, i, t, lev, fieldmap={}):
15781596
s.msreAlts = {}
15791597
s.ntup, s.trem, s.intrem = -1, 0, 0
15801598
s.acciatura = 0 # next grace element gets acciatura attribute
1599+
s.rbStop = 0 # sluit een open volta aan het eind van de maat
15811600
overlay = 0
15821601
maat = E.Element ('measure', number = str(i))
15831602
if fieldmap: s.doFields (maat, fieldmap, lev + 1)
@@ -1612,6 +1631,8 @@ def mkMeasure (s, i, t, lev, fieldmap={}):
16121631
s.mkBarline (maat, 'right', lev + 1, style='none')
16131632
elif '[' in bar or ']' in bar:
16141633
s.mkBarline (maat, 'right', lev + 1, style='light-heavy')
1634+
elif bar == '|' and s.rbStop: # normale barline hoeft niet, behalve om een volta te stoppen
1635+
s.mkBarline (maat, 'right', lev + 1, style='regular')
16151636
elif bar[0] == '&': overlay = 1
16161637
elif x.name == 'tup':
16171638
if len (x.t) == 3: n, into, nts = x.t
@@ -1648,7 +1669,7 @@ def mkMeasure (s, i, t, lev, fieldmap={}):
16481669
return maat, overlay
16491670

16501671
def mkPart (s, maten, id, lev, attrs, nstaves, rOpt):
1651-
s.slurstack = []
1672+
s.slurstack = {}
16521673
s.glisnum = 0; # xml number attribute for glissandos
16531674
s.slidenum = 0; # xml number attribute for slides
16541675
s.unitLcur = s.unitL # set the default unit length at begin of each voice
@@ -2121,7 +2142,8 @@ def writefile (pad, fnm, fnmNum, xmldoc, mxlOpt, tOpt=False):
21212142

21222143
def readfile (fnmext, errmsg='read error: '):
21232144
try:
2124-
fobj = open (fnmext, 'rb')
2145+
if fnmext == '-.abc': fobj = stdin # see python2/3 differences
2146+
else: fobj = open (fnmext, 'rb')
21252147
encoded_data = fobj.read ()
21262148
fobj.close ()
21272149
return encoded_data if type (encoded_data) == uni_type else decodeInput (encoded_data)
@@ -2147,7 +2169,7 @@ def getXmlScores (abc_string, skip=0, num=1, rOpt=False, bOpt=False, fOpt=False)
21472169
def getXmlDocs (abc_string, skip=0, num=1, rOpt=False, bOpt=False, fOpt=False): # added by David Randolph
21482170
xml_docs = []
21492171
abctext = expand_abc_include (abc_string)
2150-
fragments = re.split ('^\s*X:', abctext, flags=re.M)
2172+
fragments = re.split (r'^\s*X:', abctext, flags=re.M)
21512173
preamble = fragments [0] # tunes can be preceeded by formatting instructions
21522174
tunes = fragments[1:]
21532175
if not tunes and preamble: tunes, preamble = ['1\n' + preamble], '' # tune without X:
@@ -2208,10 +2230,12 @@ def getXmlDocs (abc_string, skip=0, num=1, rOpt=False, bOpt=False, fOpt=False):
22082230
if tag not in mxm.metaTypes: parser.error ('--meta: tag %s is no valid XML creator type' % tag)
22092231
mxm.metaMap [field] = tag
22102232
fnmext_list = []
2211-
for i in args: fnmext_list += glob (i)
2233+
for i in args:
2234+
if i == '-': fnmext_list.append ('-.abc') # represents standard input
2235+
else: fnmext_list += glob (i)
22122236
if not fnmext_list: parser.error ('none of the input files exist')
22132237
t_start = time.time ()
2214-
for X, fnmext in enumerate (fnmext_list):
2238+
for fnmext in fnmext_list:
22152239
fnm, ext = os.path.splitext (fnmext)
22162240
if ext.lower () not in ('.abc'):
22172241
info ('skipped input file %s, it should have extension .abc' % fnmext)

abc_tune.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
meter_re = re.compile(meter_pattern)
1919
unitlength_re = re.compile(unitlength_pattern)
20-
inline_meter_re = re.compile('\[{0}\]'.format(meter_pattern))
21-
inline_unitlength_re = re.compile('\[{0}\]'.format(unitlength_pattern))
20+
inline_meter_re = re.compile(r'\[{0}\]'.format(meter_pattern))
21+
inline_unitlength_re = re.compile(r'\[{0}\]'.format(unitlength_pattern))
2222
abc_field_re = re.compile(field_pattern)
2323
voice_re = re.compile(voice_pattern)
2424
comment_re = re.compile(comment_pattern)

0 commit comments

Comments
 (0)