|
3 | 3 | '''
|
4 | 4 | Copyright (C) 2012-2018: Willem G. Vree
|
5 | 5 | Contributions: Nils Liberg, Nicolas Froment, Norman Schmidt, Reinier Maliepaard, Martin Tarenskeen,
|
6 |
| - Paul Villiger, Alexander Scheutzow, Herbert Schneider, David Randolph |
| 6 | + Paul Villiger, Alexander Scheutzow, Herbert Schneider, David Randolph, Michael Strasser |
7 | 7 |
|
8 | 8 | This program is free software; you can redistribute it and/or modify it under the terms of the
|
9 | 9 | Lesser GNU General Public License as published by the Free Software Foundation;
|
|
22 | 22 | except: import xml.etree.ElementTree as E
|
23 | 23 | import types, sys, os, re, datetime
|
24 | 24 |
|
25 |
| -VERSION = 228 |
| 25 | +VERSION = 231 |
26 | 26 |
|
27 | 27 | python3 = sys.version_info[0] > 2
|
28 | 28 | lmap = lambda f, xs: list (map (f, xs)) # eager map for python 3
|
@@ -152,13 +152,14 @@ def abc_grammar (): # header, voice and lyrics grammar for ABC
|
152 | 152 | text_expression = Optional (oneOf ('^ _ < > @'), '^') + Optional (CharsNotIn ('"'), "")
|
153 | 153 | chord_accidental = oneOf ('# b =')
|
154 | 154 | triad = oneOf ('ma Maj maj M mi min m aug dim o + -')
|
155 |
| - seventh = oneOf ('7 ma7 Maj7 M7 maj7 mi7 m7 dim7 o7 -7 aug7 +7 m7b5 mi7b5') |
156 |
| - sixth = oneOf ('6 ma6 M6 m6 mi6') |
157 |
| - ninth = oneOf ('9 ma9 M9 maj9 Maj9 mi9 m9') |
158 |
| - elevn = oneOf ('11 ma11 M11 maj11 Maj11 mi11 m11') |
| 155 | + seventh = oneOf ('7 ma7 Maj7 M7 maj7 mi7 min7 m7 dim7 o7 -7 aug7 +7 m7b5 mi7b5') |
| 156 | + sixth = oneOf ('6 ma6 M6 mi6 min6 m6') |
| 157 | + ninth = oneOf ('9 ma9 M9 maj9 Maj9 mi9 min9 m9') |
| 158 | + elevn = oneOf ('11 ma11 M11 maj11 Maj11 mi11 min11 m11') |
| 159 | + thirt = oneOf ('13 ma13 M13 maj13 Maj13 mi13 min13 m13') |
159 | 160 | suspended = oneOf ('sus sus2 sus4')
|
160 | 161 | chord_degree = Combine (Optional (chord_accidental) + oneOf ('2 4 5 6 7 9 11 13'))
|
161 |
| - chord_kind = Optional (seventh | sixth | ninth | elevn | triad, '_') + Optional (suspended) |
| 162 | + chord_kind = Optional (seventh | sixth | ninth | elevn | thirt | triad) + Optional (suspended) |
162 | 163 | chord_root = oneOf ('C D E F G A B') + Optional (chord_accidental)
|
163 | 164 | chord_bass = oneOf ('C D E F G A B') + Optional (chord_accidental) # needs a different parse action
|
164 | 165 | chordsym = chord_root + chord_kind + ZeroOrMore (chord_degree) + Optional (Suppress ('/') + chord_bass)
|
@@ -413,14 +414,16 @@ def doGrace (t): # t is a Group() result -> the grace sequence is in t[0]
|
413 | 414 | #----------------------------------
|
414 | 415 |
|
415 | 416 | def compChordTab (): # avoid some typing work: returns mapping constant {ABC chordsyms -> musicXML kind}
|
416 |
| - maj, min, aug, dim, dom, ch7, ch6, ch9, ch11, hd = 'major minor augmented diminished dominant -seventh -sixth -ninth -11th half-diminished'.split () |
| 417 | + maj, min, aug, dim, dom, ch7, ch6, ch9, ch11, ch13, hd = 'major minor augmented diminished dominant -seventh -sixth -ninth -11th -13th half-diminished'.split () |
417 | 418 | triad = zip ('ma Maj maj M mi min m aug dim o + -'.split (), [maj, maj, maj, maj, min, min, min, aug, dim, dim, aug, min])
|
418 |
| - seventh = zip ('7 ma7 Maj7 M7 maj7 mi7 m7 dim7 o7 -7 aug7 +7 m7b5 mi7b5'.split (), |
419 |
| - [dom, maj+ch7, maj+ch7, maj+ch7, maj+ch7, min+ch7, min+ch7, dim+ch7, dim+ch7, min+ch7, aug+ch7, aug+ch7, hd, hd]) |
420 |
| - sixth = zip ('6 ma6 M6 mi6 m6'.split (), [maj+ch6, maj+ch6, maj+ch6, min+ch6, min+ch6]) |
421 |
| - ninth = zip ('9 ma9 M9 maj9 Maj9 mi9 m9'.split (), [dom+ch9, maj+ch9, maj+ch9, maj+ch9, maj+ch9, min+ch9, min+ch9]) |
422 |
| - elevn = zip ('11 ma11 M11 maj11 Maj11 mi11 m11'.split (), [dom+ch11, maj+ch11, maj+ch11, maj+ch11, maj+ch11, min+ch11, min+ch11]) |
423 |
| - return dict (list (triad) + list (seventh) + list (sixth) + list (ninth) + list (elevn)) |
| 419 | + seventh = zip ('7 ma7 Maj7 M7 maj7 mi7 min7 m7 dim7 o7 -7 aug7 +7 m7b5 mi7b5'.split (), |
| 420 | + [dom, maj+ch7, maj+ch7, maj+ch7, maj+ch7, min+ch7, min+ch7, min+ch7, dim+ch7, dim+ch7, min+ch7, aug+ch7, aug+ch7, hd, hd]) |
| 421 | + sixth = zip ('6 ma6 M6 mi6 min6 m6'.split (), [maj+ch6, maj+ch6, maj+ch6, min+ch6, min+ch6, min+ch6]) |
| 422 | + ninth = zip ('9 ma9 M9 maj9 Maj9 mi9 min9 m9'.split (), [dom+ch9, maj+ch9, maj+ch9, maj+ch9, maj+ch9, min+ch9, min+ch9, min+ch9]) |
| 423 | + elevn = zip ('11 ma11 M11 maj11 Maj11 mi11 min11 m11'.split (), [dom+ch11, maj+ch11, maj+ch11, maj+ch11, maj+ch11, min+ch11, min+ch11, min+ch11]) |
| 424 | + thirt = zip ('13 ma13 M13 maj13 Maj13 mi13 min13 m13'.split (), [dom+ch13, maj+ch13, maj+ch13, maj+ch13, maj+ch13, min+ch13, min+ch13, min+ch13]) |
| 425 | + sus = zip ('sus sus4 sus2'.split (), ['suspended-fourth', 'suspended-fourth', 'suspended-second']) |
| 426 | + return dict (list (triad) + list (seventh) + list (sixth) + list (ninth) + list (elevn) + list (thirt) + list (sus)) |
424 | 427 |
|
425 | 428 | def addElem (parent, child, level):
|
426 | 429 | indent = 2
|
@@ -844,6 +847,7 @@ def reset (s, fOpt=False):
|
844 | 847 | s.lyrdash = {} # {lyric number -> 1 if dash between syllables}
|
845 | 848 | s.usrSyms = s.uSyms # user defined symbols
|
846 | 849 | s.prevNote = None # xml element of previous beamed note to correct beams (start, continue)
|
| 850 | + s.prevLyric = {} # xml element of previous lyric to add/correct extend type (start, continue) |
847 | 851 | s.grcbbrk = False # remember any bbrk in a grace sequence
|
848 | 852 | s.linebrk = 0 # 1 if next measure should start with a line break
|
849 | 853 | s.nextdecos = [] # decorations for the next note
|
@@ -1035,6 +1039,7 @@ def mkNote (s, n, lev):
|
1035 | 1039 | if hasStem: s.doBeams (n, nt, den, lev + 1) # no stems -> no beams in a tab staff
|
1036 | 1040 | s.doNotations (n, decos, ptup, alter, tupnotation, tstop, nt, lev + 1)
|
1037 | 1041 | if n.objs: s.doLyr (n, nt, lev + 1)
|
| 1042 | + else: s.prevLyric = {} # clear on note without lyrics |
1038 | 1043 | return nt
|
1039 | 1044 |
|
1040 | 1045 | def cmpNormType (s, rdvs, lev): # compute the normal-type of a tuplet (only needed for Finale)
|
@@ -1198,21 +1203,33 @@ def doArticulations (s, nt, nots, arts, lev):
|
1198 | 1203 |
|
1199 | 1204 | def doLyr (s, n, nt, lev):
|
1200 | 1205 | for i, lyrobj in enumerate (n.objs):
|
1201 |
| - if lyrobj.name != 'syl': continue |
1202 |
| - dash = len (lyrobj.t) == 2 |
1203 |
| - if dash: |
1204 |
| - if i in s.lyrdash: type = 'middle' |
1205 |
| - else: type = 'begin'; s.lyrdash [i] = 1 |
1206 |
| - else: |
1207 |
| - if i in s.lyrdash: type = 'end'; del s.lyrdash [i] |
1208 |
| - else: type = 'single' |
1209 | 1206 | lyrel = E.Element ('lyric', number = str (i + 1))
|
| 1207 | + if lyrobj.name == 'syl': |
| 1208 | + dash = len (lyrobj.t) == 2 |
| 1209 | + if dash: |
| 1210 | + if i in s.lyrdash: type = 'middle' |
| 1211 | + else: type = 'begin'; s.lyrdash [i] = 1 |
| 1212 | + else: |
| 1213 | + if i in s.lyrdash: type = 'end'; del s.lyrdash [i] |
| 1214 | + else: type = 'single' |
| 1215 | + addElemT (lyrel, 'syllabic', type, lev + 1) |
| 1216 | + txt = lyrobj.t[0] # the syllabe |
| 1217 | + txt = re.sub (r'(?<!\\)~', ' ', txt) # replace ~ by space when not escaped (preceded by \) |
| 1218 | + txt = re.sub (r'\\(.)', r'\1', txt) # replace all escaped characters by themselves (for the time being) |
| 1219 | + addElemT (lyrel, 'text', txt, lev + 1) |
| 1220 | + elif lyrobj.name == 'ext' and i in s.prevLyric: |
| 1221 | + pext = s.prevLyric [i].find ('extend') # identify previous extend |
| 1222 | + if pext == None: |
| 1223 | + ext = E.Element ('extend', type = 'start') |
| 1224 | + addElem (s.prevLyric [i], ext, lev + 1) |
| 1225 | + elif pext.get('type') == 'stop': # subsequent extend: stop -> continue |
| 1226 | + pext.set ('type', 'continue') |
| 1227 | + ext = E.Element ('extend', type = 'stop') # always stop on current extend |
| 1228 | + addElem (lyrel, ext, lev + 1) |
| 1229 | + elif lyrobj.name == 'ext': info ('lyric extend error'); continue |
| 1230 | + else: continue # skip other lyric elements or errors |
1210 | 1231 | addElem (nt, lyrel, lev)
|
1211 |
| - addElemT (lyrel, 'syllabic', type, lev + 1) |
1212 |
| - txt = lyrobj.t[0] # the syllabe |
1213 |
| - txt = re.sub (r'(?<!\\)~', ' ', txt) # replace ~ by space when not escaped (preceded by \) |
1214 |
| - txt = re.sub (r'\\(.)', r'\1', txt) # replace all escaped characters by themselves (for the time being) |
1215 |
| - addElemT (lyrel, 'text', txt, lev + 1) |
| 1232 | + s.prevLyric [i] = lyrel # for extension (melisma) on the next note |
1216 | 1233 |
|
1217 | 1234 | def doBeams (s, n, nt, den, lev):
|
1218 | 1235 | if hasattr (n, 'chord') or hasattr (n, 'grace'):
|
@@ -1535,7 +1552,7 @@ def doChordSym (s, maat, sym, lev):
|
1535 | 1552 | addElem (chord, root, lev + 1)
|
1536 | 1553 | addElemT (root, 'root-step', rnt[0], lev + 2)
|
1537 | 1554 | if len (rnt) == 2: addElemT (root, 'root-alter', alterMap [rnt[1]], lev + 2)
|
1538 |
| - kind = s.chordTab.get (sym.kind.t[0], 'major') |
| 1555 | + kind = s.chordTab.get (sym.kind.t[0], 'major') if sym.kind.t else 'major' |
1539 | 1556 | addElemT (chord, 'kind', kind, lev + 1)
|
1540 | 1557 | if hasattr (sym, 'bass'):
|
1541 | 1558 | bnt = sym.bass.t
|
|
0 commit comments