|
|
|
|
|
''' |
|
Copyright (C) 2012-2018: W.G. Vree |
|
Contributions: M. Tarenskeen, N. Liberg, Paul Villiger, Janus Meuris, Larry Myerscough, |
|
Dick Jackson, Jan Wybren de Jong, Mark Zealey. |
|
|
|
This program is free software; you can redistribute it and/or modify it under the terms of the |
|
Lesser GNU General Public License as published by the Free Software Foundation; |
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
See the Lesser GNU General Public License for more details. <http://www.gnu.org/licenses/lgpl.html>. |
|
''' |
|
|
|
try: import xml.etree.cElementTree as E |
|
except: import xml.etree.ElementTree as E |
|
import os, sys, types, re, math |
|
|
|
VERSION = 143 |
|
|
|
python3 = sys.version_info.major > 2 |
|
if python3: |
|
tupletype = tuple |
|
listtype = list |
|
max_int = sys.maxsize |
|
else: |
|
tupletype = types.TupleType |
|
listtype = types.ListType |
|
max_int = sys.maxint |
|
|
|
note_ornamentation_map = { |
|
'ornaments/trill-mark': 'T', |
|
'ornaments/mordent': 'M', |
|
'ornaments/inverted-mordent': 'P', |
|
'ornaments/turn': '!turn!', |
|
'ornaments/inverted-turn': '!invertedturn!', |
|
'technical/up-bow': 'u', |
|
'technical/down-bow': 'v', |
|
'technical/harmonic': '!open!', |
|
'technical/open-string': '!open!', |
|
'technical/stopped': '!plus!', |
|
'technical/snap-pizzicato': '!snap!', |
|
'technical/thumb-position': '!thumb!', |
|
'articulations/accent': '!>!', |
|
'articulations/strong-accent':'!^!', |
|
'articulations/staccato': '.', |
|
'articulations/staccatissimo':'!wedge!', |
|
'articulations/scoop': '!slide!', |
|
'fermata': '!fermata!', |
|
'arpeggiate': '!arpeggio!', |
|
'articulations/tenuto': '!tenuto!', |
|
'articulations/staccatissimo':'!wedge!', |
|
'articulations/spiccato': '!wedge!', |
|
'articulations/breath-mark': '!breath!', |
|
'articulations/detached-legato': '!tenuto!.', |
|
} |
|
|
|
dynamics_map = { |
|
'p': '!p!', |
|
'pp': '!pp!', |
|
'ppp': '!ppp!', |
|
'pppp': '!pppp!', |
|
'f': '!f!', |
|
'ff': '!ff!', |
|
'fff': '!fff!', |
|
'ffff': '!ffff!', |
|
'mp': '!mp!', |
|
'mf': '!mf!', |
|
'sfz': '!sfz!', |
|
} |
|
|
|
percSvg = '''%%beginsvg |
|
<defs> |
|
<text id="x" x="-3" y="0"></text> |
|
<text id="x-" x="-3" y="0"></text> |
|
<text id="x+" x="-3" y="0"></text> |
|
<text id="normal" x="-3.7" y="0"></text> |
|
<text id="normal-" x="-3.7" y="0"></text> |
|
<text id="normal+" x="-3.7" y="0"></text> |
|
<g id="circle-x"><text x="-3" y="0"></text><circle r="4" class="stroke"></circle></g> |
|
<g id="circle-x-"><text x="-3" y="0"></text><circle r="4" class="stroke"></circle></g> |
|
<path id="triangle" d="m-4 -3.2l4 6.4 4 -6.4z" class="stroke" style="stroke-width:1.4"></path> |
|
<path id="triangle-" d="m-4 -3.2l4 6.4 4 -6.4z" class="stroke" style="stroke-width:1.4"></path> |
|
<path id="triangle+" d="m-4 -3.2l4 6.4 4 -6.4z" class="stroke" style="fill:#000"></path> |
|
<path id="square" d="m-3.5 3l0 -6.2 7.2 0 0 6.2z" class="stroke" style="stroke-width:1.4"></path> |
|
<path id="square-" d="m-3.5 3l0 -6.2 7.2 0 0 6.2z" class="stroke" style="stroke-width:1.4"></path> |
|
<path id="square+" d="m-3.5 3l0 -6.2 7.2 0 0 6.2z" class="stroke" style="fill:#000"></path> |
|
<path id="diamond" d="m0 -3l4.2 3.2 -4.2 3.2 -4.2 -3.2z" class="stroke" style="stroke-width:1.4"></path> |
|
<path id="diamond-" d="m0 -3l4.2 3.2 -4.2 3.2 -4.2 -3.2z" class="stroke" style="stroke-width:1.4"></path> |
|
<path id="diamond+" d="m0 -3l4.2 3.2 -4.2 3.2 -4.2 -3.2z" class="stroke" style="fill:#000"></path> |
|
</defs> |
|
%%endsvg''' |
|
|
|
tabSvg = '''%%beginsvg |
|
<style type="text/css"> |
|
.bf {font-family:sans-serif; font-size:7px} |
|
</style> |
|
<defs> |
|
<rect id="clr" x="-3" y="-1" width="6" height="5" fill="white"></rect> |
|
<rect id="clr2" x="-3" y="-1" width="11" height="5" fill="white"></rect>''' |
|
|
|
kopSvg = '<g id="kop%s" class="bf"><use xlink:href="#clr"></use><text x="-2" y="3">%s</text></g>\n' |
|
kopSvg2 = '<g id="kop%s" class="bf"><use xlink:href="#clr2"></use><text x="-2" y="3">%s</text></g>\n' |
|
|
|
def info (s, warn=1): sys.stderr.write ((warn and '-- ' or '') + s + '\n') |
|
|
|
|
|
|
|
|
|
class Measure: |
|
def __init__ (s, p): |
|
s.reset () |
|
s.ixp = p |
|
s.ixm = 0 |
|
s.mdur = 0 |
|
s.divs = 0 |
|
s.mtr = 4,4 |
|
|
|
def reset (s): |
|
s.attr = '' |
|
s.lline = '' |
|
s.rline = '|' |
|
s.lnum = '' |
|
|
|
class Note: |
|
def __init__ (s, dur=0, n=None): |
|
s.tijd = 0 |
|
s.dur = dur |
|
s.fact = None |
|
s.tup = [''] |
|
s.tupabc = '' |
|
s.beam = 0 |
|
s.grace = 0 |
|
s.before = [] |
|
s.after = '' |
|
s.ns = n and [n] or [] |
|
s.lyrs = {} |
|
s.tab = None |
|
s.ntdec = '' |
|
|
|
class Elem: |
|
def __init__ (s, string): |
|
s.tijd = 0 |
|
s.str = string |
|
|
|
class Counter: |
|
def inc (s, key, voice): s.counters [key][voice] = s.counters [key].get (voice, 0) + 1 |
|
def clear (s, vnums): |
|
tups = list( zip (vnums.keys (), len (vnums) * [0])) |
|
s.counters = {'note': dict (tups), 'nopr': dict (tups), 'nopt': dict (tups)} |
|
def getv (s, key, voice): return s.counters[key][voice] |
|
def prcnt (s, ip): |
|
for iv in s.counters ['note']: |
|
if s.getv ('nopr', iv) != 0: |
|
info ( 'part %d, voice %d has %d skipped non printable notes' % (ip, iv, s.getv ('nopr', iv))) |
|
if s.getv ('nopt', iv) != 0: |
|
info ( 'part %d, voice %d has %d notes without pitch' % (ip, iv, s.getv ('nopt', iv))) |
|
if s.getv ('note', iv) == 0: |
|
info ( 'part %d, skipped empty voice %d' % (ip, iv)) |
|
|
|
class Music: |
|
def __init__(s, options): |
|
s.tijd = 0 |
|
s.maxtime = 0 |
|
s.gMaten = [] |
|
s.gLyrics = [] |
|
s.vnums = {} |
|
s.cnt = Counter () |
|
s.vceCnt = 1 |
|
s.lastnote = None |
|
s.bpl = options.b |
|
s.cpl = options.n |
|
s.repbra = 0 |
|
s.nvlt = options.v |
|
s.jscript = options.j |
|
|
|
def initVoices (s, newPart=0): |
|
s.vtimes, s.voices, s.lyrics = {}, {}, {} |
|
for v in s.vnums: |
|
s.vtimes [v] = 0 |
|
s.voices [v] = [] |
|
s.lyrics [v] = [] |
|
if newPart: s.cnt.clear (s.vnums) |
|
|
|
def incTime (s, dt): |
|
s.tijd += dt |
|
if s.tijd < 0: s.tijd = 0 |
|
if s.tijd > s.maxtime: s.maxtime = s.tijd |
|
|
|
def appendElemCv (s, voices, elem): |
|
for v in voices: |
|
s.appendElem (v, elem) |
|
|
|
def insertElem (s, v, elem): |
|
obj = Elem (elem) |
|
obj.tijd = 0 |
|
s.voices [v].insert (0, obj) |
|
|
|
def appendObj (s, v, obj, dur): |
|
obj.tijd = s.tijd |
|
s.voices [v].append (obj) |
|
s.incTime (dur) |
|
if s.tijd > s.vtimes[v]: s.vtimes[v] = s.tijd |
|
|
|
def appendElem (s, v, elem, tel=0): |
|
s.appendObj (v, Elem (elem), 0) |
|
if tel: s.cnt.inc ('note', v) |
|
|
|
def appendElemT (s, v, elem, tijd): |
|
obj = Elem (elem) |
|
obj.tijd = tijd |
|
s.voices [v].append (obj) |
|
|
|
def appendNote (s, v, note, noot): |
|
note.ns.append (note.ntdec + noot) |
|
s.appendObj (v, note, int (note.dur)) |
|
s.lastnote = note |
|
if noot != 'z' and noot != 'x': |
|
s.cnt.inc ('note', v) |
|
if not note.grace: |
|
s.lyrics[v].append (note.lyrs) |
|
|
|
def getLastRec (s, voice): |
|
if s.gMaten: return s.gMaten[-1][voice][-1] |
|
return None |
|
|
|
def getLastMelis (s, voice, num): |
|
if s.gLyrics: |
|
lyrdict = s.gLyrics[-1][voice] |
|
if num in lyrdict: return lyrdict[num][1] |
|
return 0 |
|
|
|
def addChord (s, note, noot): |
|
for d in note.before: |
|
if d not in s.lastnote.before: |
|
s.lastnote.before += [d] |
|
s.lastnote.ns.append (note.ntdec + noot) |
|
|
|
def addBar (s, lbrk, m): |
|
if m.mdur and s.maxtime > m.mdur: info ('measure %d in part %d longer than metre' % (m.ixm+1, m.ixp+1)) |
|
s.tijd = s.maxtime |
|
for v in s.vnums: |
|
if m.lline or m.lnum: |
|
p = s.getLastRec (v) |
|
if p: |
|
x = p.str |
|
if m.lline: |
|
x = (x + m.lline).replace (':|:','::').replace ('||','|') |
|
if s.nvlt == 3: |
|
if m.ixp + v == min (s.vnums): x += m.lnum |
|
elif m.lnum: |
|
x += m.lnum |
|
s.repbra = 1 |
|
p.str = x |
|
elif m.lline: |
|
s.insertElem (v, '|:') |
|
if lbrk: |
|
p = s.getLastRec (v) |
|
if p: p.str += lbrk |
|
if m.attr: |
|
s.insertElem (v, '%s' % m.attr) |
|
s.appendElem (v, ' %s' % m.rline) |
|
s.voices[v] = sortMeasure (s.voices[v], m) |
|
lyrs = s.lyrics[v] |
|
lyrdict = {} |
|
nums = [num for d in lyrs for num in d.keys ()] |
|
maxNums = max (nums + [0]) |
|
for i in range (maxNums, 0, -1): |
|
xs = [syldict.get (i, '') for syldict in lyrs] |
|
melis = s.getLastMelis (v, i) |
|
lyrdict [i] = abcLyr (xs, melis) |
|
s.lyrics[v] = lyrdict |
|
mkBroken (s.voices[v]) |
|
s.gMaten.append (s.voices) |
|
s.gLyrics.append (s.lyrics) |
|
s.tijd = s.maxtime = 0 |
|
s.initVoices () |
|
|
|
def outVoices (s, divs, ip, isSib): |
|
vvmap = {} |
|
vnum_keys = list (s.vnums.keys ()) |
|
if s.jscript or isSib: vnum_keys.sort () |
|
lvc = min (vnum_keys or [1]) |
|
for iv in vnum_keys: |
|
if s.cnt.getv ('note', iv) == 0: |
|
continue |
|
if abcOut.denL: unitL = abcOut.denL |
|
else: unitL = compUnitLength (iv, s.gMaten, divs) |
|
abcOut.cmpL.append (unitL) |
|
vn, vl = [], {} |
|
for im in range (len (s.gMaten)): |
|
measure = s.gMaten [im][iv] |
|
vn.append (outVoice (measure, divs [im], im, ip, unitL)) |
|
checkMelismas (s.gLyrics, s.gMaten, im, iv) |
|
for n, (lyrstr, melis) in s.gLyrics [im][iv].items (): |
|
if n in vl: |
|
while len (vl[n]) < im: vl[n].append ('') |
|
vl[n].append (lyrstr) |
|
else: |
|
vl[n] = im * [''] + [lyrstr] |
|
for n, lyrs in vl.items (): |
|
mis = len (vn) - len (lyrs) |
|
lyrs += mis * [''] |
|
abcOut.add ('V:%d' % s.vceCnt) |
|
if s.repbra: |
|
if s.nvlt == 1 and s.vceCnt > 1: abcOut.add ('I:repbra 0') |
|
if s.nvlt == 2 and iv > lvc: abcOut.add ('I:repbra 0') |
|
if s.cpl > 0: s.bpl = 0 |
|
elif s.bpl == 0: s.cpl = 100 |
|
bn = 0 |
|
while vn: |
|
ib = 1 |
|
chunk = vn [0] |
|
while ib < len (vn): |
|
if s.cpl > 0 and len (chunk) + len (vn [ib]) >= s.cpl: break |
|
if s.bpl > 0 and ib >= s.bpl: break |
|
chunk += vn [ib] |
|
ib += 1 |
|
bn += ib |
|
abcOut.add (chunk + ' %%%d' % bn) |
|
del vn[:ib] |
|
lyrlines = sorted (vl.items ()) |
|
for n, lyrs in lyrlines: |
|
abcOut.add ('w: ' + '|'.join (lyrs[:ib]) + '|') |
|
del lyrs[:ib] |
|
vvmap [iv] = s.vceCnt |
|
s.vceCnt += 1 |
|
s.gMaten = [] |
|
s.gLyrics = [] |
|
s.cnt.prcnt (ip+1) |
|
return vvmap |
|
|
|
class ABCoutput: |
|
pagekeys = 'scale,pageheight,pagewidth,leftmargin,rightmargin,topmargin,botmargin'.split (',') |
|
def __init__ (s, fnmext, pad, X, options): |
|
s.fnmext = fnmext |
|
s.outlist = [] |
|
s.title = 'T:Title' |
|
s.key = 'none' |
|
s.clefs = {} |
|
s.mtr = 'none' |
|
s.tempo = 0 |
|
s.tempo_units = (1,4) |
|
s.pad = pad |
|
s.X = X + 1 |
|
s.denL = options.d |
|
s.volpan = int (options.m) |
|
s.cmpL = [] |
|
s.jscript = options.j |
|
s.tstep = options.t |
|
s.stemless = 0 |
|
s.shiftStem = options.s |
|
if pad: |
|
_, base_name = os.path.split (fnmext) |
|
s.outfile = open (os.path.join (pad, base_name), 'w') |
|
else: s.outfile = sys.stdout |
|
if s.jscript: s.X = 1 |
|
s.pageFmt = {} |
|
for k in s.pagekeys: s.pageFmt [k] = None |
|
if len (options.p) == 7: |
|
for k, v in zip (s.pagekeys, options.p): |
|
try: s.pageFmt [k] = float (v) |
|
except: info ('illegal float %s for %s', (k, v)); continue |
|
|
|
def add (s, str): |
|
s.outlist.append (str + '\n') |
|
|
|
def mkHeader (s, stfmap, partlist, midimap, vmpdct, koppen): |
|
accVce, accStf, staffs = [], [], stfmap[:] |
|
for x in partlist: |
|
try: prgroupelem (x, ('', ''), '', stfmap, accVce, accStf) |
|
except: info ('lousy musicxml: error in part-list') |
|
staves = ' '.join (accStf) |
|
clfnms = {} |
|
for part, (partname, partabbrv) in zip (staffs, accVce): |
|
if not part: continue |
|
firstVoice = part[0][0] |
|
nm = partname.replace ('\n','\\n').replace ('.:','.').strip (':') |
|
snm = partabbrv.replace ('\n','\\n').replace ('.:','.').strip (':') |
|
clfnms [firstVoice] = (nm and 'nm="%s"' % nm or '') + (snm and ' snm="%s"' % snm or '') |
|
hd = ['X:%d\n%s\n' % (s.X, s.title)] |
|
for i, k in enumerate (s.pagekeys): |
|
if s.jscript and k in ['pageheight','topmargin', 'botmargin']: continue |
|
if s.pageFmt [k] != None: hd.append ('%%%%%s %.2f%s\n' % (k, s.pageFmt [k], i > 0 and 'cm' or '')) |
|
if staves and len (accStf) > 1: hd.append ('%%score ' + staves + '\n') |
|
tempo = s.tempo and 'Q:%d/%d=%s\n' % (s.tempo_units [0], s.tempo_units [1], s.tempo) or '' |
|
d = {} |
|
for x in s.cmpL: d[x] = d.get (x, 0) + 1 |
|
if s.jscript: defLs = sorted (d.items (), key=lambda x: (-x[1], x[0])) |
|
else: defLs = sorted (d.items (), key=lambda x: -x[1]) |
|
defL = s.denL and s.denL or defLs [0][0] |
|
hd.append ('L:1/%d\n%sM:%s\n' % (defL, tempo, s.mtr)) |
|
hd.append ('K:%s\n' % s.key) |
|
if s.stemless: hd.append ('U:s=!stemless!\n') |
|
vxs = sorted (vmpdct.keys ()) |
|
for vx in vxs: hd.extend (vmpdct [vx]) |
|
s.dojef = 0 |
|
for vnum, clef in s.clefs.items (): |
|
ch, prg, vol, pan = midimap [vnum-1][:4] |
|
dmap = midimap [vnum - 1][4:] |
|
if dmap and 'perc' not in clef: clef = (clef + ' map=perc').strip (); |
|
hd.append ('V:%d %s %s\n' % (vnum, clef, clfnms.get (vnum, ''))) |
|
if vnum in vmpdct: |
|
hd.append ('%%%%voicemap tab%d\n' % vnum) |
|
hd.append ('K:none\nM:none\n%%clef none\n%%staffscale 1.6\n%%flatbeams true\n%%stemdir down\n') |
|
if 'perc' in clef: hd.append ('K:none\n'); |
|
if s.volpan > 1: |
|
if ch > 0 and ch != vnum: hd.append ('%%%%MIDI channel %d\n' % ch) |
|
if prg > 0: hd.append ('%%%%MIDI program %d\n' % (prg - 1)) |
|
if vol >= 0: hd.append ('%%%%MIDI control 7 %.0f\n' % vol) |
|
if pan >= 0: hd.append ('%%%%MIDI control 10 %.0f\n' % pan) |
|
elif s.volpan > 0: |
|
if dmap and ch > 0: hd.append ('%%%%MIDI channel %d\n' % ch) |
|
if prg > 0: hd.append ('%%%%MIDI program %d\n' % (prg - 1)) |
|
for abcNote, step, midiNote, notehead in dmap: |
|
if not notehead: notehead = 'normal' |
|
if abcMid (abcNote) != midiNote or abcNote != step: |
|
if s.volpan > 0: hd.append ('%%%%MIDI drummap %s %s\n' % (abcNote, midiNote)) |
|
hd.append ('I:percmap %s %s %s %s\n' % (abcNote, step, midiNote, notehead)) |
|
s.dojef = s.tstep |
|
if defL != s.cmpL [vnum-1]: |
|
hd.append ('L:1/%d\n' % s.cmpL [vnum-1]) |
|
s.outlist = hd + s.outlist |
|
if koppen: |
|
k1 = kopSvg.replace ('-2','-5') if s.shiftStem else kopSvg |
|
k2 = kopSvg2.replace ('-2','-5') if s.shiftStem else kopSvg2 |
|
tb = tabSvg.replace ('-3','-6') if s.shiftStem else tabSvg |
|
ks = sorted (koppen.keys ()) |
|
ks = [k2 % (k, k) if len (k) == 2 else k1 % (k, k) for k in ks] |
|
tbs = map (lambda x: x.strip () + '\n', tb.splitlines ()) |
|
s.outlist = tbs + ks + ['</defs>\n%%endsvg\n'] + s.outlist |
|
|
|
def writeall (s): |
|
str = ''.join (s.outlist) |
|
if s.dojef: str = perc2map (str) |
|
if python3: s.outfile.write (str) |
|
else: s.outfile.write (str.encode ('utf-8')) |
|
if s.pad: s.outfile.close () |
|
else: s.outfile.write ('\n') |
|
info ('%s written with %d voices' % (s.fnmext, len (s.clefs)), warn=0) |
|
|
|
|
|
|
|
|
|
def abcLyr (xs, melis): |
|
if not ''.join (xs): return '', 0 |
|
res = [] |
|
for x in xs: |
|
if x == '': |
|
if melis: x = '_' |
|
else: x = '*' |
|
elif x.endswith ('_') and not x.endswith ('\_'): |
|
x = x.replace ('_', '') |
|
melis = 1 |
|
else: melis = 0 |
|
res.append (x) |
|
return (' '.join (res), melis) |
|
|
|
def simplify (a, b): |
|
x, y = a, b |
|
while b: a, b = b, a % b |
|
return x // a, y // a |
|
|
|
def abcdur (nx, divs, uL): |
|
if nx.dur == 0: return '' |
|
num, den = simplify (uL * nx.dur, divs * 4) |
|
if nx.fact: |
|
numfac, denfac = nx.fact |
|
num, den = simplify (num * numfac, den * denfac) |
|
if den > 64: |
|
x = float (num) / den; n = math.floor (x); |
|
if x - n < 0.1 * x: num, den = n, 1; |
|
num64 = 64. * num / den + 1.0e-15 |
|
num, den = simplify (int (round (num64)), 64) |
|
if num == 1: |
|
if den == 1: dabc = '' |
|
elif den == 2: dabc = '/' |
|
else: dabc = '/%d' % den |
|
elif den == 1: dabc = '%d' % num |
|
else: dabc = '%d/%d' % (num, den) |
|
return dabc |
|
|
|
def abcMid (note): |
|
r = re.search (r"([_^]*)([A-Ga-g])([',]*)", note) |
|
if not r: return -1 |
|
acc, n, oct = r.groups () |
|
nUp = n.upper () |
|
p = 60 + [0,2,4,5,7,9,11]['CDEFGAB'.index (nUp)] + (12 if nUp != n else 0); |
|
if acc: p += (1 if acc[0] == '^' else -1) * len (acc) |
|
if oct: p += (12 if oct[0] == "'" else -12) * len (oct) |
|
return p |
|
|
|
def staffStep (ptc, o, clef, tstep): |
|
ndif = 0 |
|
if 'stafflines=1' in clef: ndif += 4 |
|
if not tstep and clef.startswith ('bass'): ndif += 12 |
|
if ndif: |
|
nm7 = 'C,D,E,F,G,A,B'.split (',') |
|
n = nm7.index (ptc) + ndif |
|
ptc, o = nm7 [n % 7], o + n // 7 |
|
if o > 4: ptc = ptc.lower () |
|
if o > 5: ptc = ptc + (o-5) * "'" |
|
if o < 4: ptc = ptc + (4-o) * "," |
|
return ptc |
|
|
|
def setKey (fifths, mode): |
|
sharpness = ['Fb', 'Cb','Gb','Db','Ab','Eb','Bb','F','C','G','D','A', 'E', 'B', 'F#','C#','G#','D#','A#','E#','B#'] |
|
offTab = {'maj':8, 'ion':8, 'm':11, 'min':11, 'aeo':11, 'mix':9, 'dor':10, 'phr':12, 'lyd':7, 'loc':13, 'non':8} |
|
mode = mode.lower ()[:3] |
|
key = sharpness [offTab [mode] + fifths] + (mode if offTab [mode] != 8 else '') |
|
accs = ['F','C','G','D','A','E','B'] |
|
if fifths >= 0: msralts = dict (zip (accs[:fifths], fifths * [1])) |
|
else: msralts = dict (zip (accs[fifths:], -fifths * [-1])) |
|
return key, msralts |
|
|
|
def insTup (ix, notes, fact): |
|
tupcnt = 0 |
|
nx = notes [ix] |
|
if 'start' in nx.tup: |
|
nx.tup.remove ('start') |
|
tix = ix |
|
fn, fd = fact |
|
fnum, fden = nx.fact |
|
tupfact = fnum//fn, fden//fd |
|
while ix < len (notes): |
|
nx = notes [ix] |
|
if isinstance (nx, Elem) or nx.grace: |
|
ix += 1 |
|
continue |
|
if 'start' in nx.tup: |
|
ix, tupcntR = insTup (ix, notes, tupfact) |
|
tupcnt += tupcntR |
|
elif nx.fact: |
|
tupcnt += 1 |
|
if 'stop' in nx.tup: |
|
nx.tup.remove ('stop') |
|
break |
|
if not nx.fact: |
|
ix = lastix |
|
break |
|
lastix = ix |
|
ix += 1 |
|
|
|
tup = (tupfact[0], tupfact[1], tupcnt) |
|
if tup == (3, 2, 3): tupPrefix = '(3' |
|
else: tupPrefix = '(%d:%d:%d' % tup |
|
notes [tix].tupabc = tupPrefix + notes [tix].tupabc |
|
return ix, tupcnt |
|
|
|
def mkBroken (vs): |
|
vs = [n for n in vs if isinstance (n, Note)] |
|
i = 0 |
|
while i < len (vs) - 1: |
|
n1, n2 = vs[i], vs[i+1] |
|
|
|
if not n1.fact and not n2.fact and n1.dur > 0 and n2.beam: |
|
if n1.dur * 3 == n2.dur: |
|
n2.dur = (2 * n2.dur) // 3 |
|
n1.dur = n1.dur * 2 |
|
n1.after = '<' + n1.after |
|
i += 1 |
|
elif n2.dur * 3 == n1.dur: |
|
n1.dur = (2 * n1.dur) // 3 |
|
n2.dur = n2.dur * 2 |
|
n1.after = '>' + n1.after |
|
i += 1 |
|
i += 1 |
|
|
|
def outVoice (measure, divs, im, ip, unitL): |
|
ix = 0 |
|
while ix < len (measure): |
|
nx = measure [ix] |
|
if isinstance (nx, Note) and nx.fact and not nx.grace: |
|
ix, tupcnt = insTup (ix, measure, (1, 1)) |
|
ix += 1 |
|
vs = [] |
|
for nx in measure: |
|
if isinstance (nx, Note): |
|
durstr = abcdur (nx, divs, unitL) |
|
chord = len (nx.ns) > 1 |
|
cns = [nt[:-1] for nt in nx.ns if nt.endswith ('-')] |
|
tie = '' |
|
if chord and len (cns) == len (nx.ns): |
|
nx.ns = cns |
|
tie = '-' |
|
s = nx.tupabc + ''.join (nx.before) |
|
if chord: s += '[' |
|
for nt in nx.ns: s += nt |
|
if chord: s += ']' + tie |
|
if s.endswith ('-'): s, tie = s[:-1], '-' |
|
s += durstr + tie |
|
s += nx.after |
|
nospace = nx.beam |
|
else: |
|
if isinstance (nx.str, listtype): nx.str = nx.str [0] |
|
s = nx.str |
|
nospace = 1 |
|
if nospace: vs.append (s) |
|
else: vs.append (' ' + s) |
|
vs = ''.join (vs) |
|
while vs.find ('!ped!!ped!') >= 0: vs = vs.replace ('!ped!!ped!','!ped!') |
|
while vs.find ('!ped-up!!ped-up!') >= 0: vs = vs.replace ('!ped-up!!ped-up!','!ped-up!') |
|
while vs.find ('!8va(!!8va)!') >= 0: vs = vs.replace ('!8va(!!8va)!','') |
|
return vs |
|
|
|
def sortMeasure (voice, m): |
|
voice.sort (key=lambda o: o.tijd) |
|
time = 0 |
|
v = [] |
|
rs = [] |
|
for i, nx in enumerate (voice): |
|
if nx.tijd > time and chkbug (nx.tijd - time, m): |
|
v.append (Note (nx.tijd - time, 'x')) |
|
rs.append (len (v) - 1) |
|
if isinstance (nx, Elem): |
|
if nx.tijd < time: nx.tijd = time |
|
v.append (nx) |
|
time = nx.tijd |
|
continue |
|
if nx.tijd < time: |
|
if nx.ns[0] == 'z': continue |
|
if v[-1].tijd <= nx.tijd: |
|
if v[-1].ns[0] == 'z': |
|
v[-1].dur = nx.tijd - v[-1].tijd |
|
if v[-1].dur == 0: del v[-1] |
|
info ('overlap in part %d, measure %d: rest shortened' % (m.ixp+1, m.ixm+1)) |
|
else: |
|
v[-1].ns += nx.ns |
|
info ('overlap in part %d, measure %d: added chord' % (m.ixp+1, m.ixm+1)) |
|
nx.dur = (nx.tijd + nx.dur) - time |
|
if nx.dur <= 0: continue |
|
nx.tijd = time |
|
else: |
|
info ('overlapping notes in one voice! part %d, measure %d, note %s discarded' % (m.ixp+1, m.ixm+1, isinstance (nx, Note) and nx.ns or nx.str)) |
|
continue |
|
v.append (nx) |
|
if isinstance (nx, Note): |
|
if nx.ns [0] in 'zx': |
|
rs.append (len (v) - 1) |
|
elif len (rs): |
|
if nx.beam and not nx.grace: |
|
for j in rs: v[j].beam = nx.beam |
|
rs = [] |
|
time = nx.tijd + nx.dur |
|
|
|
|
|
if time == 0: info ('empty measure in part %d, measure %d, it should contain at least a rest to advance the time!' % (m.ixp+1, m.ixm+1)) |
|
return v |
|
|
|
def getPartlist (ps): |
|
xs = [] |
|
e = [] |
|
for x in list (ps): |
|
if x.tag == 'part-group': |
|
num, type = x.get ('number'), x.get ('type') |
|
if type == 'start': |
|
if num in e: |
|
xs.append (E.Element ('part-group', number = num, type = 'stop')) |
|
xs.append (x) |
|
else: |
|
xs.append (x) |
|
e.append (num) |
|
else: |
|
if num in e: |
|
e.remove (num) |
|
xs.append (x) |
|
else: pass |
|
else: xs.append (x) |
|
for num in reversed (e): |
|
xs.append (E.Element ('part-group', number = num, type = 'stop')) |
|
return xs |
|
|
|
def parseParts (xs, d, e): |
|
if not xs: return [],[] |
|
x = xs.pop (0) |
|
if x.tag == 'part-group': |
|
num, type = x.get ('number'), x.get ('type') |
|
if type == 'start': |
|
s = [x.findtext (n, '') for n in ['group-symbol','group-barline','group-name','group-abbreviation']] |
|
d [num] = s |
|
e.append (num) |
|
elemsnext, rest1 = parseParts (xs, d, e) |
|
elems, rest2 = parseParts (rest1, d, e) |
|
return [elemsnext] + elems, rest2 |
|
else: |
|
nums = e.pop () |
|
if xs and xs[0].get ('type') == 'stop': |
|
if num != nums: |
|
d[nums], d[num] = d[num], d[nums] |
|
sym = d[num] |
|
return [sym], xs |
|
else: |
|
elems, rest = parseParts (xs, d, e) |
|
name = x.findtext ('part-name',''), x.findtext ('part-abbreviation','') |
|
return [name] + elems, rest |
|
|
|
def bracePart (part): |
|
if not part: return [] |
|
brace = [] |
|
for ivs in part: |
|
if len (ivs) == 1: |
|
brace.append ('%s' % ivs[0]) |
|
else: |
|
brace += ['('] + ['%s' % iv for iv in ivs] + [')'] |
|
brace.append ('|') |
|
del brace[-1] |
|
if len (part) > 1: |
|
brace = ['{'] + brace + ['}'] |
|
return brace |
|
|
|
def prgroupelem (x, gnm, bar, pmap, accVce, accStf): |
|
if type (x) == tupletype: |
|
y = pmap.pop (0) |
|
if gnm[0]: x = [n1 + ':' + n2 for n1, n2 in zip (gnm, x)] |
|
accVce.append (x) |
|
accStf.extend (bracePart (y)) |
|
elif len (x) == 2 and type (x[0]) == tupletype: |
|
y = pmap.pop (0) |
|
nms = [n1 + ':' + n2 for n1, n2 in zip (x[0], x[1][2:])] |
|
accVce.append (nms) |
|
accStf.extend (bracePart (y)) |
|
else: |
|
prgrouplist (x, bar, pmap, accVce, accStf) |
|
|
|
def prgrouplist (x, pbar, pmap, accVce, accStf): |
|
sym, bar, gnm, gabbr = x[-1] |
|
bar = bar == 'yes' or pbar |
|
accStf.append (sym == 'brace' and '{' or '[') |
|
for z in x[:-1]: |
|
prgroupelem (z, (gnm, gabbr), bar, pmap, accVce, accStf) |
|
if bar: accStf.append ('|') |
|
if bar: del accStf [-1] |
|
accStf.append (sym == 'brace' and '}' or ']') |
|
|
|
def compUnitLength (iv, maten, divs): |
|
uLmin, minLen = 0, max_int |
|
for uL in [4,8,16]: |
|
vLen = 0 |
|
for im, m in enumerate (maten): |
|
for e in m[iv]: |
|
if isinstance (e, Elem) or e.dur == 0: continue |
|
vLen += len (abcdur (e, divs [im], uL)) |
|
if vLen < minLen: uLmin, minLen = uL, vLen |
|
return uLmin |
|
|
|
def doSyllable (syl): |
|
txt = '' |
|
for e in syl: |
|
if e.tag == 'elision': txt += '~' |
|
elif e.tag == 'text': |
|
txt += (e.text or '').replace ('_','\_').replace('-', r'\-').replace(' ', '~') |
|
if not txt: return txt |
|
if syl.findtext('syllabic') in ['begin', 'middle']: txt += '-' |
|
if syl.find('extend') is not None: txt += '_' |
|
return txt |
|
|
|
def checkMelismas (lyrics, maten, im, iv): |
|
if im == 0: return |
|
maat = maten [im][iv] |
|
curlyr = lyrics [im][iv] |
|
prvlyr = lyrics [im-1][iv] |
|
for n, (lyrstr, melis) in prvlyr.items (): |
|
if n not in curlyr and melis: |
|
ms = getMelisma (maat) |
|
if ms: curlyr [n] = (ms, 0) |
|
|
|
def getMelisma (maat): |
|
ms = [] |
|
for note in maat: |
|
if not isinstance (note, Note): continue |
|
if note.grace: continue |
|
if note.ns [0] in 'zx': break |
|
ms.append ('_') |
|
return ' '.join (ms) |
|
|
|
def perc2map (abcIn): |
|
fillmap = {'diamond':1, 'triangle':1, 'square':1, 'normal':1}; |
|
abc = map (lambda x: x.strip (), percSvg.splitlines ()) |
|
id='default' |
|
maps = {'default': []}; |
|
dmaps = {'default': []} |
|
r1 = re.compile (r'V:\s*(\S+)') |
|
ls = abcIn.splitlines () |
|
for x in ls: |
|
if 'I:percmap' in x: |
|
noot, step, midi, kop = map (lambda x: x.strip (), x.split ()[1:]) |
|
if kop in fillmap: kop = kop + '+' + ',' + kop |
|
x = '%%%%map perc%s %s print=%s midi=%s heads=%s' % (id, noot, step, midi, kop) |
|
maps [id].append (x) |
|
if '%%MIDI' in x: dmaps [id].append (x) |
|
if 'V:' in x: |
|
r = r1.match (x) |
|
if r: |
|
id = r.group (1); |
|
if id not in maps: maps [id] = []; dmaps [id] = [] |
|
ids = sorted (maps.keys ()) |
|
for id in ids: abc += maps [id] |
|
id='default' |
|
for x in ls: |
|
if 'I:percmap' in x: continue |
|
if '%%MIDI' in x: continue |
|
if 'V:' in x or 'K:' in x: |
|
r = r1.match (x) |
|
if r: id = r.group (1) |
|
abc.append (x) |
|
if id in dmaps and len (dmaps [id]) > 0: abc.extend (dmaps [id]); del dmaps [id] |
|
if 'perc' in x and 'map=' not in x: x += ' map=perc'; |
|
if 'map=perc' in x and len (maps [id]) > 0: abc.append ('%%voicemap perc' + id); |
|
if 'map=off' in x: abc.append ('%%voicemap'); |
|
else: |
|
abc.append (x) |
|
return '\n'.join (abc) + '\n' |
|
|
|
def addoct (ptc, o): |
|
p = ptc |
|
if o > 4: p = ptc.lower () |
|
if o > 5: p = p + (o-5) * "'" |
|
if o < 4: p = p + (4-o) * "," |
|
return p |
|
|
|
def chkbug (dt, m): |
|
if dt > m.divs / 16: return 1 |
|
info ('MuseScore bug: incorrect duration, smaller then 1/64! in measure %d, part %d' % (m.ixm, m.ixp)) |
|
return 0 |
|
|
|
|
|
|
|
|
|
class Parser: |
|
note_alts = [ |
|
[x.strip () for x in '=C, ^C, =D, ^D, =E, =F, ^F, =G, ^G, =A, ^A, =B'.split (',')], |
|
[x.strip () for x in '^B, _D,^^C, _E, _F, ^E, _G,^^F, _A,^^G, _B, _C'.split (',')], |
|
[x.strip () for x in '__D,^^B,__E,__F,^^D,__G,^^E,__A,_/A,__B,__C,^^A'.split (',')] ] |
|
step_map = {'C':0,'D':2,'E':4,'F':5,'G':7,'A':9,'B':11} |
|
def __init__ (s, options): |
|
|
|
s.slurBuf = {} |
|
s.dirStk = {} |
|
s.ingrace = 0 |
|
s.msc = Music (options) |
|
s.unfold = options.u |
|
s.ctf = options.c |
|
s.gStfMap = [] |
|
s.midiMap = [] |
|
s.drumInst = {} |
|
s.drumNotes = {} |
|
s.instMid = [] |
|
s.midDflt = [-1,-1,-1,-91] |
|
s.msralts = {} |
|
s.curalts = {} |
|
s.stfMap = {} |
|
s.vce2stf = {} |
|
s.clefMap = {} |
|
s.curClef = {} |
|
s.stemDir = {} |
|
s.clefOct = {} |
|
s.curStf = {} |
|
s.nolbrk = options.x; |
|
s.jscript = options.j |
|
s.ornaments = sorted (note_ornamentation_map.items ()) |
|
s.doPageFmt = len (options.p) == 1 |
|
s.tstep = options.t |
|
s.dirtov1 = options.v1 |
|
s.ped = options.ped |
|
s.wstems = options.stm |
|
s.pedVce = None |
|
s.repeat_str = {} |
|
s.tabVceMap = {} |
|
s.koppen = {} |
|
|
|
def matchSlur (s, type2, n, v2, note2, grace, stopgrace): |
|
if type2 not in ['start', 'stop']: return |
|
if n == None: n = '1' |
|
if n in s.slurBuf: |
|
type1, v1, note1, grace1 = s.slurBuf [n] |
|
if type2 != type1: |
|
if v2 == v1: |
|
if type1 == 'start' and (not grace1 or not stopgrace): |
|
note1.before = ['('] + note1.before |
|
note2.after += ')' |
|
|
|
del s.slurBuf [n] |
|
else: |
|
info ('double slur numbers %s-%s in part %d, measure %d, voice %d note %s, first discarded' % (type2, n, s.msr.ixp+1, s.msr.ixm+1, v2, note2.ns)) |
|
s.slurBuf [n] = (type2, v2, note2, grace) |
|
else: |
|
s.slurBuf [n] = (type2, v2, note2, grace) |
|
|
|
def doNotations (s, note, nttn, isTab): |
|
for key, val in s.ornaments: |
|
if nttn.find (key) != None: note.before += [val] |
|
trem = nttn.find ('ornaments/tremolo') |
|
if trem != None: |
|
type = trem.get ('type') |
|
if type == 'single': |
|
note.before.insert (0, '!%s!' % (int (trem.text) * '/')) |
|
else: |
|
note.fact = None |
|
if s.tstep: |
|
if type == 'stop': note.before.insert (0, '!trem%s!' % trem.text); |
|
else: |
|
if type == 'start': note.before.insert (0, '!%s-!' % (int (trem.text) * '/')); |
|
fingering = nttn.findall ('technical/fingering') |
|
for finger in fingering: |
|
if not isTab: note.before += ['!%s!' % finger.text] |
|
snaar = nttn.find ('technical/string') |
|
if snaar != None and isTab: |
|
if s.tstep: |
|
fret = nttn.find ('technical/fret') |
|
if fret != None: note.tab = (snaar.text, fret.text) |
|
else: |
|
deco = '!%s!' % snaar.text |
|
if deco not in note.ntdec: note.ntdec += deco |
|
wvln = nttn.find ('ornaments/wavy-line') |
|
if wvln != None: |
|
if wvln.get ('type') == 'start': note.before = ['!trill(!'] + note.before |
|
elif wvln.get ('type') == 'stop': note.before = ['!trill)!'] + note.before |
|
glis = nttn.find ('glissando') |
|
if glis == None: glis = nttn.find ('slide') |
|
if glis != None: |
|
lt = '~' if glis.get ('line-type') =='wavy' else '-' |
|
if glis.get ('type') == 'start': note.before = ['!%s(!' % lt] + note.before |
|
elif glis.get ('type') == 'stop': note.before = ['!%s)!' % lt] + note.before |
|
|
|
def tabnote (s, alt, ptc, oct, v, ntrec): |
|
p = s.step_map [ptc] + int (alt or '0') |
|
if p > 11: oct += 1 |
|
if p < 0: oct -= 1 |
|
p = p % 12 |
|
snaar_nw, fret_nw = ntrec.tab |
|
for i in range (4): |
|
na = s.note_alts [i % 3] [p] |
|
o = oct |
|
if na in ['^B', '^^B']: o -= 1 |
|
if na in ['_C', '__C']: o += 1 |
|
if '/' in na or i == 3: o = 9 |
|
nt = addoct (na, o) |
|
snaar, fret = s.tabmap.get ((v, nt), ('', '')) |
|
if not snaar: break |
|
if snaar_nw == snaar: return nt |
|
if i == 3: |
|
fmt = 'rejected: voice %d note %3s string %s fret %2s remains: string %s fret %s' |
|
info (fmt % (v, nt, snaar_nw, fret_nw, snaar, fret), 1) |
|
ntrec.tab = (snaar, fret) |
|
s.tabmap [v, nt] = ntrec.tab |
|
return nt |
|
|
|
def ntAbc (s, ptc, oct, note, v, ntrec, isTab): |
|
acc2alt = {'double-flat':-2,'flat-flat':-2,'flat':-1,'natural':0,'sharp':1,'sharp-sharp':2,'double-sharp':2} |
|
oct += s.clefOct.get (s.curStf [v], 0) |
|
acc = note.findtext ('accidental') |
|
alt = note.findtext ('pitch/alter') |
|
if ntrec.tab: return s.tabnote (alt, ptc, oct, v, ntrec) |
|
elif isTab and s.tstep: |
|
nt = ['__','_','','^','^^'][int (alt or '0') + 2] + addoct (ptc, oct) |
|
info ('no string notation found for note %s in voice %d' % (nt, v), 1) |
|
p = addoct (ptc, oct) |
|
if alt == None and s.msralts.get (ptc, 0): alt = 0 |
|
if alt == None and (p, v) in s.curalts: alt = 0 |
|
if acc == None and alt == None: return p |
|
elif acc != None: |
|
alt = acc2alt [acc] |
|
else: |
|
alt = int (float (alt)) |
|
if (p, v) in s.curalts: |
|
if alt == s.curalts [(p, v)]: return p |
|
elif alt == s.msralts.get (ptc, 0): return p |
|
tieElms = note.findall ('tie') + note.findall ('notations/tied') |
|
if 'stop' in [e.get ('type') for e in tieElms]: return p |
|
info ('accidental %d added in part %d, measure %d, voice %d note %s' % (alt, s.msr.ixp+1, s.msr.ixm+1, v+1, p)) |
|
s.curalts [(p, v)] = alt |
|
p = ['__','_','=','^','^^'][alt+2] + p |
|
return p |
|
|
|
def doNote (s, n): |
|
note = Note () |
|
v = int (n.findtext ('voice', '1')) |
|
if s.isSib: v += 100 * int (n.findtext ('staff', '1')) |
|
chord = n.find ('chord') != None |
|
p = n.findtext ('pitch/step') or n.findtext ('unpitched/display-step') |
|
o = n.findtext ('pitch/octave') or n.findtext ('unpitched/display-octave') |
|
r = n.find ('rest') |
|
numer = n.findtext ('time-modification/actual-notes') |
|
if numer: |
|
denom = n.findtext ('time-modification/normal-notes') |
|
note.fact = (int (numer), int (denom)) |
|
note.tup = [x.get ('type') for x in n.findall ('notations/tuplet')] |
|
dur = n.findtext ('duration') |
|
grc = n.find ('grace') |
|
note.grace = grc != None |
|
note.before, note.after = [], '' |
|
if note.grace and not s.ingrace: |
|
s.ingrace = 1 |
|
note.before = ['{'] |
|
if grc.get ('slash') == 'yes': note.before += ['/'] |
|
stopgrace = not note.grace and s.ingrace |
|
if stopgrace: |
|
s.ingrace = 0 |
|
s.msc.lastnote.after += '}' |
|
if dur == None or note.grace: dur = 0 |
|
if r == None and n.get ('print-object') == 'no': |
|
if chord: return |
|
r = 1 |
|
note.dur = int (dur) |
|
if r == None and (not p or not o): |
|
s.msc.cnt.inc ('nopt', v) |
|
o, p = 5,'E' |
|
isTab = s.curClef and s.curClef.get (s.curStf [v], '').startswith ('tab') |
|
nttn = n.find ('notations') |
|
if nttn != None: s.doNotations (note, nttn, isTab) |
|
e = n.find ('stem') if r == None else None |
|
if e != None and e.text == 'none' and (not isTab or v in s.hasStems or s.tstep): |
|
note.before += ['s']; abcOut.stemless = 1; |
|
e = n.find ('accidental') |
|
if e != None and e.get ('parentheses') == 'yes': note.ntdec += '!courtesy!' |
|
if r != None: noot = 'x' if n.get ('print-object') == 'no' or isTab else 'z' |
|
else: noot = s.ntAbc (p, int (o), n, v, note, isTab) |
|
if n.find ('unpitched') != None: |
|
clef = s.curClef [s.curStf [v]] |
|
step = staffStep (p, int (o), clef, s.tstep) |
|
instr = n.find ('instrument') |
|
instId = instr.get ('id') if instr != None else 'dummyId' |
|
midi = s.drumInst.get (instId, abcMid (noot)) |
|
nh = n.findtext ('notehead', '').replace (' ','-') |
|
if nh == 'x': noot = '^' + noot.replace ('^','').replace ('_','') |
|
if nh in ['circle-x','diamond','triangle']: noot = '_' + noot.replace ('^','').replace ('_','') |
|
if nh and n.find ('notehead').get ('filled','') == 'yes': nh += '+' |
|
if nh and n.find ('notehead').get ('filled','') == 'no': nh += '-' |
|
s.drumNotes [(v, noot)] = (step, midi, nh) |
|
tieElms = n.findall ('tie') + n.findall ('notations/tied') |
|
if 'start' in [e.get ('type') for e in tieElms]: |
|
noot = noot + '-' |
|
note.beam = sum ([1 for b in n.findall('beam') if b.text in ['continue', 'end']]) + int (note.grace) |
|
lyrlast = 0; rsib = re.compile (r'^.*verse') |
|
for e in n.findall ('lyric'): |
|
lyrnum = int (rsib.sub ('', e.get ('number', '1'))) |
|
if lyrnum == 0: lyrnum = lyrlast + 1 |
|
else: lyrlast = lyrnum |
|
note.lyrs [lyrnum] = doSyllable (e) |
|
stemdir = n.findtext ('stem') |
|
if s.wstems and (stemdir == 'up' or stemdir == 'down'): |
|
if stemdir != s.stemDir.get (v, ''): |
|
s.stemDir [v] = stemdir |
|
s.msc.appendElem (v, '[I:stemdir %s]' % stemdir) |
|
if chord: s.msc.addChord (note, noot) |
|
else: |
|
xmlstaff = int (n.findtext ('staff', '1')) |
|
if s.curStf [v] != xmlstaff: |
|
dstaff = xmlstaff - s.curStf [v] |
|
s.curStf [v] = xmlstaff |
|
s.msc.appendElem (v, '[I:staff %+d]' % dstaff) |
|
s.msc.appendNote (v, note, noot) |
|
for slur in n.findall ('notations/slur'): |
|
s.matchSlur (slur.get ('type'), slur.get ('number'), v, s.msc.lastnote, note.grace, stopgrace) |
|
|
|
def doAttr (s, e): |
|
teken = {'C1':'alto1','C2':'alto2','C3':'alto','C4':'tenor','F4':'bass','F3':'bass3','G2':'treble','TAB':'tab','percussion':'perc'} |
|
dvstxt = e.findtext ('divisions') |
|
if dvstxt: s.msr.divs = int (dvstxt) |
|
steps = int (e.findtext ('transpose/chromatic', '0')) |
|
fifths = e.findtext ('key/fifths') |
|
first = s.msc.tijd == 0 and s.msr.ixm == 0 |
|
if fifths: |
|
key, s.msralts = setKey (int (fifths), e.findtext ('key/mode','major')) |
|
if first and not steps and abcOut.key == 'none': |
|
abcOut.key = key |
|
elif key != abcOut.key or not first: |
|
s.msr.attr += '[K:%s]' % key |
|
beats = e.findtext ('time/beats') |
|
if beats: |
|
unit = e.findtext ('time/beat-type') |
|
mtr = beats + '/' + unit |
|
if first: abcOut.mtr = mtr |
|
else: s.msr.attr += '[M:%s]' % mtr |
|
s.msr.mtr = int (beats), int (unit) |
|
s.msr.mdur = (s.msr.divs * s.msr.mtr[0] * 4) // s.msr.mtr[1] |
|
for ms in e.findall('measure-style'): |
|
n = int (ms.get ('number', '1')) |
|
voices = s.stfMap [n] |
|
for mr in ms.findall('measure-repeat'): |
|
ty = mr.get('type') |
|
if ty == 'start': |
|
s.repeat_str [n] = [s.msr.ixm, mr.text] |
|
for v in voices: |
|
s.msc.insertElem (v, s.repeat_str [n]) |
|
elif ty == 'stop': |
|
start_ix, text_ = s.repeat_str [n] |
|
repeat_count = s.msr.ixm - start_ix |
|
if text_: |
|
mid_str = "%s " % text_ |
|
repeat_count /= int (text_) |
|
else: |
|
mid_str = "" |
|
s.repeat_str [n][0] = '[I:repeat %s%d]' % (mid_str, repeat_count) |
|
del s.repeat_str [n] |
|
toct = e.findtext ('transpose/octave-change', '') |
|
if toct: steps += 12 * int (toct) |
|
for clef in e.findall ('clef'): |
|
n = int (clef.get ('number', '1')) |
|
sgn = clef.findtext ('sign') |
|
line = clef.findtext ('line', '') if sgn not in ['percussion','TAB'] else '' |
|
cs = teken.get (sgn + line, '') |
|
oct = clef.findtext ('clef-octave-change', '') or '0' |
|
if oct: cs += {-2:'-15', -1:'-8', 1:'+8', 2:'+15'}.get (int (oct), '') |
|
s.clefOct [n] = -int (oct); |
|
if steps: cs += ' transpose=' + str (steps) |
|
stfdtl = e.find ('staff-details') |
|
if stfdtl and int (stfdtl.get ('number', '1')) == n: |
|
lines = stfdtl.findtext ('staff-lines') |
|
if lines: |
|
lns= '|||' if lines == '3' and sgn == 'TAB' else lines |
|
cs += ' stafflines=%s' % lns |
|
s.stafflines = int (lines) |
|
strings = stfdtl.findall ('staff-tuning') |
|
if strings: |
|
tuning = [st.findtext ('tuning-step') + st.findtext ('tuning-octave') for st in strings] |
|
cs += ' strings=%s' % ','.join (tuning) |
|
capo = stfdtl.findtext ('capo') |
|
if capo: cs += ' capo=%s' % capo |
|
s.curClef [n] = cs |
|
if first: s.clefMap [n] = cs |
|
else: |
|
voices = s.stfMap[n] |
|
for v in voices: |
|
if n != s.curStf [v]: |
|
dstaff = n - s.curStf [v] |
|
s.curStf [v] = n |
|
s.msc.appendElem (v, '[I:staff %+d]' % dstaff) |
|
s.msc.appendElem (v, '[K:%s]' % cs) |
|
|
|
def findVoice (s, i, es): |
|
stfnum = int (es[i].findtext ('staff',1)) |
|
vs = s.stfMap [stfnum] |
|
v1 = vs [0] if vs else 1 |
|
if s.dirtov1: return stfnum, v1, v1 |
|
for e in es [i+1:]: |
|
if e.tag == 'note': |
|
v = int (e.findtext ('voice', '1')) |
|
if s.isSib: v += 100 * int (e.findtext ('staff', '1')) |
|
stf = s.vce2stf [v] |
|
return stf, v, v1 |
|
if e.tag == 'backup': break |
|
return stfnum, v1, v1 |
|
|
|
def doDirection (s, e, i, es): |
|
def addDirection (x, vs, tijd, stfnum): |
|
if not x: return |
|
vs = s.stfMap [stfnum] if '!8v' in x else [vs] |
|
for v in vs: |
|
if tijd != None: |
|
s.msc.appendElemT (v, x.replace ('(',')').replace ('ped','ped-up'), tijd) |
|
else: |
|
s.msc.appendElem (v, x) |
|
def startStop (dtype, vs, stfnum=1): |
|
typmap = {'down':'!8va(!', 'up':'!8vb(!', 'crescendo':'!<(!', 'diminuendo':'!>(!', 'start':'!ped!'} |
|
type = t.get ('type', '') |
|
k = dtype + t.get ('number', '1') |
|
if type in typmap: |
|
x = typmap [type] |
|
if k in s.dirStk: |
|
stype, tijd = s.dirStk [k]; del s.dirStk [k] |
|
if stype == 'stop': |
|
addDirection (x, vs, tijd, stfnum) |
|
else: |
|
info ('%s direction %s has no stop in part %d, measure %d, voice %d' % (dtype, stype, s.msr.ixp+1, s.msr.ixm+1, vs+1)) |
|
s.dirStk [k] = ((type , vs)) |
|
else: |
|
s.dirStk [k] = ((type , vs)) |
|
elif type == 'stop': |
|
if k in s.dirStk: |
|
type, vs = s.dirStk [k]; del s.dirStk [k] |
|
if type == 'stop': |
|
info ('%s direction %s has double stop in part %d, measure %d, voice %d' % (dtype, type, s.msr.ixp+1, s.msr.ixm+1, vs+1)) |
|
x = '' |
|
else: |
|
x = typmap [type].replace ('(',')').replace ('ped','ped-up') |
|
else: |
|
s.dirStk [k] = ('stop', s.msc.tijd) |
|
x = '' |
|
else: raise ValueError ('wrong direction type') |
|
addDirection (x, vs, None, stfnum) |
|
tempo, wrdstxt = None, '' |
|
plcmnt = e.get ('placement') |
|
stf, vs, v1 = s.findVoice (i, es) |
|
jmp = '' |
|
jmps = [('dacapo','D.C.'),('dalsegno','D.S.'),('tocoda','dacoda'),('fine','fine'),('coda','O'),('segno','S')] |
|
t = e.find ('sound') |
|
if t != None: |
|
minst = t.find ('midi-instrument') |
|
if minst: |
|
prg = t.findtext ('midi-instrument/midi-program') |
|
chn = t.findtext ('midi-instrument/midi-channel') |
|
vids = [v for v, id in s.vceInst.items () if id == minst.get ('id')] |
|
if vids: vs = vids [0] |
|
parm, inst = ('program', str (int (prg) - 1)) if prg else ('channel', chn) |
|
if inst and abcOut.volpan > 0: s.msc.appendElem (vs, '[I:MIDI= %s %s]' % (parm, inst)) |
|
tempo = t.get ('tempo') |
|
if tempo: |
|
tempo = '%.0f' % float (tempo) |
|
tempo_units = (1,4) |
|
for r, v in jmps: |
|
if t.get (r, ''): jmp = v; break |
|
dirtypes = e.findall ('direction-type') |
|
for dirtyp in dirtypes: |
|
units = { 'whole': (1,1), 'half': (1,2), 'quarter': (1,4), 'eighth': (1,8) } |
|
metr = dirtyp.find ('metronome') |
|
if metr != None: |
|
t = metr.findtext ('beat-unit', '') |
|
if t in units: tempo_units = units [t] |
|
else: tempo_units = units ['quarter'] |
|
if metr.find ('beat-unit-dot') != None: |
|
tempo_units = simplify (tempo_units [0] * 3, tempo_units [1] * 2) |
|
tmpro = re.search ('[.\d]+', metr.findtext ('per-minute')) |
|
if tmpro: tempo = tmpro.group () |
|
t = dirtyp.find ('wedge') |
|
if t != None: startStop ('wedge', vs) |
|
allwrds = dirtyp.findall ('words') |
|
if not allwrds: allwrds = dirtyp.findall ('rehearsal') |
|
for wrds in allwrds: |
|
if jmp: |
|
s.msc.appendElem (vs, '!%s!' % jmp , 1) |
|
break |
|
plc = plcmnt == 'below' and '_' or '^' |
|
if float (wrds.get ('default-y', '0')) < 0: plc = '_' |
|
wrdstxt += (wrds.text or '').replace ('"','\\"').replace ('\n', '\\n') |
|
wrdstxt = wrdstxt.strip () |
|
for key, val in dynamics_map.items (): |
|
if dirtyp.find ('dynamics/' + key) != None: |
|
s.msc.appendElem (vs, val, 1) |
|
if dirtyp.find ('coda') != None: s.msc.appendElem (vs, 'O', 1) |
|
if dirtyp.find ('segno') != None: s.msc.appendElem (vs, 'S', 1) |
|
t = dirtyp.find ('octave-shift') |
|
if t != None: startStop ('octave-shift', vs, stf) |
|
t = dirtyp.find ('pedal') |
|
if t != None and s.ped: |
|
if not s.pedVce: s.pedVce = vs |
|
startStop ('pedal', s.pedVce) |
|
if dirtyp.findtext ('other-direction') == 'diatonic fretting': s.diafret = 1; |
|
if tempo: |
|
tempo = '%.0f' % float (tempo) |
|
if s.msc.tijd == 0 and s.msr.ixm == 0: |
|
abcOut.tempo = tempo |
|
abcOut.tempo_units = tempo_units |
|
else: |
|
s.msc.appendElem (v1, '[Q:%d/%d=%s]' % (tempo_units [0], tempo_units [1], tempo)) |
|
if wrdstxt: s.msc.appendElem (vs, '"%s%s"' % (plc, wrdstxt), 1) |
|
|
|
def doHarmony (s, e, i, es): |
|
_, vt, _ = s.findVoice (i, es) |
|
short = {'major':'', 'minor':'m', 'augmented':'+', 'diminished':'dim', 'dominant':'7', 'half-diminished':'m7b5'} |
|
accmap = {'major':'maj', 'dominant':'', 'minor':'m', 'diminished':'dim', 'augmented':'+', 'suspended':'sus'} |
|
modmap = {'second':'2', 'fourth':'4', 'seventh':'7', 'sixth':'6', 'ninth':'9', '11th':'11', '13th':'13'} |
|
altmap = {'1':'#', '0':'', '-1':'b'} |
|
root = e.findtext ('root/root-step','') |
|
alt = altmap.get (e.findtext ('root/root-alter'), '') |
|
sus = '' |
|
kind = e.findtext ('kind', '') |
|
if kind in short: kind = short [kind] |
|
elif '-' in kind: |
|
triad, mod = kind.split ('-') |
|
kind = accmap.get (triad, '') + modmap.get (mod, '') |
|
if kind.startswith ('sus'): kind, sus = '', kind |
|
elif kind == 'none': kind = e.find ('kind').get ('text','') |
|
degrees = e.findall ('degree') |
|
for d in degrees: |
|
kind += altmap.get (d.findtext ('degree-alter'),'') + d.findtext ('degree-value','') |
|
kind = kind.replace ('79','9').replace ('713','13').replace ('maj6','6') |
|
bass = e.findtext ('bass/bass-step','') + altmap.get (e.findtext ('bass/bass-alter'),'') |
|
s.msc.appendElem (vt, '"%s%s%s%s%s"' % (root, alt, kind, sus, bass and '/' + bass), 1) |
|
|
|
def doBarline (s, e): |
|
rep = e.find ('repeat') |
|
if rep != None: rep = rep.get ('direction') |
|
if s.unfold: |
|
return rep and (rep == 'forward' and 1 or 2) or 0 |
|
loc = e.get ('location', 'right') |
|
if loc == 'right': |
|
style = e.findtext ('bar-style') |
|
if style == 'light-light': s.msr.rline = '||' |
|
elif style == 'light-heavy': s.msr.rline = '|]' |
|
if rep != None: |
|
if rep == 'forward': s.msr.lline = ':' |
|
else: s.msr.rline = ':|' |
|
end = e.find ('ending') |
|
if end != None: |
|
if end.get ('type') == 'start': |
|
n = end.get ('number', '1').replace ('.','').replace (' ','') |
|
try: list (map (int, n.split (','))) |
|
except: n = '"%s"' % n.strip () |
|
s.msr.lnum = n |
|
elif s.msr.rline == '|': |
|
s.msr.rline = '||' |
|
return 0 |
|
|
|
def doPrint (s, e): |
|
if e.get ('new-system') == 'yes' or e.get ('new-page') == 'yes': |
|
if not s.nolbrk: return '$' |
|
|
|
def doPartList (s, e): |
|
for sp in e.findall ('part-list/score-part'): |
|
midi = {} |
|
for m in sp.findall ('midi-instrument'): |
|
x = [m.findtext (p, s.midDflt [i]) for i,p in enumerate (['midi-channel','midi-program','volume','pan'])] |
|
pan = float (x[3]) |
|
if pan >= -90 and pan <= 90: |
|
pan = (float (x[3]) + 90) / 180 * 127 |
|
midi [m.get ('id')] = [int (x[0]), int (x[1]), float (x[2]) * 1.27, pan] |
|
up = m.findtext ('midi-unpitched') |
|
if up: s.drumInst [m.get ('id')] = int (up) - 1 |
|
s.instMid.append (midi) |
|
ps = e.find ('part-list') |
|
xs = getPartlist (ps) |
|
partlist, _ = parseParts (xs, {}, []) |
|
return partlist |
|
|
|
def mkTitle (s, e): |
|
def filterCredits (y): |
|
cs = [] |
|
for x in credits: |
|
if y < 6 and (x in title or x in mvttl): continue |
|
if y < 5 and (x in composer or x in lyricist): continue |
|
if y < 4 and ((title and title in x) or (mvttl and mvttl in x)): continue |
|
if y < 3 and ([1 for c in composer if c in x] or [1 for c in lyricist if c in x]): continue |
|
if y < 2 and re.match (r'^[\d\W]*$', x): continue |
|
cs.append (x) |
|
if y == 0 and (title + mvttl): cs = '' |
|
return cs |
|
title = e.findtext ('work/work-title', '').strip () |
|
mvttl = e.findtext ('movement-title', '').strip () |
|
composer, lyricist, credits = [], [], [] |
|
for creator in e.findall ('identification/creator'): |
|
if creator.text: |
|
if creator.get ('type') == 'composer': |
|
composer += [line.strip () for line in creator.text.split ('\n')] |
|
elif creator.get ('type') in ('lyricist', 'transcriber'): |
|
lyricist += [line.strip () for line in creator.text.split ('\n')] |
|
for rights in e.findall ('identification/rights'): |
|
if rights.text: |
|
lyricist += [line.strip () for line in rights.text.split ('\n')] |
|
for credit in e.findall('credit'): |
|
cs = ''.join (e.text or '' for e in credit.findall('credit-words')) |
|
credits += [re.sub (r'\s*[\r\n]\s*', ' ', cs)] |
|
credits = filterCredits (s.ctf) |
|
if title: title = 'T:%s\n' % title.replace ('\n', '\nT:') |
|
if mvttl: title += 'T:%s\n' % mvttl.replace ('\n', '\nT:') |
|
if credits: title += '\n'.join (['T:%s' % c for c in credits]) + '\n' |
|
if composer: title += '\n'.join (['C:%s' % c for c in composer]) + '\n' |
|
if lyricist: title += '\n'.join (['Z:%s' % c for c in lyricist]) + '\n' |
|
if title: abcOut.title = title[:-1] |
|
s.isSib = 'Sibelius' in (e.findtext ('identification/encoding/software') or '') |
|
if s.isSib: info ('Sibelius MusicXMl is unreliable') |
|
|
|
def doDefaults (s, e): |
|
if not s.doPageFmt: return |
|
d = e.find ('defaults'); |
|
if d == None: return; |
|
mils = d.findtext ('scaling/millimeters') |
|
tenths = d.findtext ('scaling/tenths') |
|
if not mils or not tenths: return |
|
xmlScale = float (mils) / float (tenths) / 10 |
|
space = 10 * xmlScale |
|
abcScale = space / 0.2117 |
|
abcOut.pageFmt ['scale'] = abcScale |
|
eks = 2 * ['page-layout/'] + 4 * ['page-layout/page-margins/'] |
|
eks = [a+b for a,b in zip (eks, 'page-height,page-width,left-margin,right-margin,top-margin,bottom-margin'.split (','))] |
|
for i in range (6): |
|
v = d.findtext (eks [i]) |
|
k = abcOut.pagekeys [i+1] |
|
if not abcOut.pageFmt [k] and v: |
|
try: abcOut.pageFmt [k] = float (v) * xmlScale |
|
except: info ('illegal value %s for xml element %s', (v, eks [i])); continue |
|
|
|
def locStaffMap (s, part, maten): |
|
vmap = {} |
|
s.vceInst = {} |
|
s.msc.vnums = {} |
|
s.hasStems = {} |
|
s.stfMap, s.clefMap = {}, {} |
|
ns = part.findall ('measure/note') |
|
for n in ns: |
|
v = int (n.findtext ('voice', '1')) |
|
if s.isSib: v += 100 * int (n.findtext ('staff', '1')) |
|
s.msc.vnums [v] = 1 |
|
sn = int (n.findtext ('staff', '1')) |
|
s.stfMap [sn] = [] |
|
if v not in vmap: |
|
vmap [v] = {sn:1} |
|
else: |
|
d = vmap[v] |
|
d[sn] = d.get (sn, 0) + 1 |
|
x = n.find ('instrument') |
|
if x != None: s.vceInst [v] = x.get ('id') |
|
x, noRest = n.findtext ('stem'), n.find ('rest') == None |
|
if noRest and (not x or x != 'none'): s.hasStems [v] = 1 |
|
vks = list (vmap.keys ()) |
|
if s.jscript or s.isSib: vks.sort () |
|
for v in vks: |
|
xs = [(n, sn) for sn, n in vmap[v].items ()] |
|
xs.sort () |
|
stf = xs[-1][1] |
|
s.stfMap [stf].append (v) |
|
s.vce2stf [v] = stf |
|
s.curStf [v] = stf |
|
|
|
def addStaffMap (s, vvmap): |
|
part = [] |
|
for stf, voices in sorted (s.stfMap.items ()): |
|
locmap = [vvmap [iv] for iv in voices if iv in vvmap] |
|
nostem = [(iv not in s.hasStems) for iv in voices if iv in vvmap] |
|
if locmap: |
|
part.append (locmap) |
|
clef = s.clefMap.get (stf, 'treble') |
|
for i, iv in enumerate (locmap): |
|
clef_attr = '' |
|
if clef.startswith ('tab'): |
|
if nostem [i] and 'nostems' not in clef: clef_attr = ' nostems' |
|
if s.diafret and 'diafret' not in clef: clef_attr += ' diafret' |
|
abcOut.clefs [iv] = clef + clef_attr |
|
s.gStfMap.append (part) |
|
|
|
def addMidiMap (s, ip, vvmap): |
|
instr = s.instMid [ip] |
|
if instr.values (): defInstr = list(instr.values ())[0] |
|
else: defInstr = s.midDflt |
|
xs = [] |
|
for v, vabc in vvmap.items (): |
|
ks = sorted (s.drumNotes.items ()) |
|
ds = [(nt, step, midi, head) for (vd, nt), (step, midi, head) in ks if v == vd] |
|
id = s.vceInst.get (v, '') |
|
if id in instr: |
|
xs.append ((vabc, instr [id] + ds)) |
|
else: xs.append ((vabc, defInstr + ds)) |
|
xs.sort () |
|
s.midiMap.extend ([midi for v, midi in xs]) |
|
snaarmap = ['E','G','B','d', 'f', 'a', "c'", "e'"] |
|
diamap = '0,1-,1,1+,2,3,3,4,4,5,6,6+,7,8-,8,8+,9,10,10,11,11,12,13,13+,14'.split (',') |
|
for k in sorted (s.tabmap.keys ()): |
|
v, noot = k; |
|
snaar, fret = s.tabmap [k]; |
|
if s.diafret: fret = diamap [int (fret)] |
|
vabc = vvmap [v] |
|
snaar = s.stafflines - int (snaar) |
|
xs = s.tabVceMap.get (vabc, []) |
|
xs.append ('%%%%map tab%d %s print=%s heads=kop%s\n' % (vabc, noot, snaarmap [snaar], fret)) |
|
s.tabVceMap [vabc] = xs |
|
s.koppen [fret] = 1 |
|
|
|
def parse (s, fobj): |
|
vvmapAll = {} |
|
e = E.parse (fobj) |
|
s.mkTitle (e) |
|
s.doDefaults (e) |
|
partlist = s.doPartList (e) |
|
parts = e.findall ('part') |
|
for ip, p in enumerate (parts): |
|
maten = p.findall ('measure') |
|
s.locStaffMap (p, maten) |
|
s.drumNotes = {} |
|
s.clefOct = {} |
|
s.curClef = {} |
|
s.stemDir = {} |
|
s.tabmap = {} |
|
s.diafret = 0 |
|
s.stafflines = 5 |
|
s.msc.initVoices (newPart = 1) |
|
aantalHerhaald = 0 |
|
herhaalMaat = 0 |
|
divisions = [] |
|
s.msr = Measure (ip) |
|
while s.msr.ixm < len (maten): |
|
maat = maten [s.msr.ixm] |
|
herhaal, lbrk = 0, '' |
|
s.msr.reset () |
|
s.curalts = {} |
|
es = list (maat) |
|
for i, e in enumerate (es): |
|
if e.tag == 'note': s.doNote (e) |
|
elif e.tag == 'attributes': s.doAttr (e) |
|
elif e.tag == 'direction': s.doDirection (e, i, es) |
|
elif e.tag == 'sound': s.doDirection (maat, i, es) |
|
elif e.tag == 'harmony': s.doHarmony (e, i, es) |
|
elif e.tag == 'barline': herhaal = s.doBarline (e) |
|
elif e.tag == 'backup': |
|
dt = int (e.findtext ('duration')) |
|
if chkbug (dt, s.msr): s.msc.incTime (-dt) |
|
elif e.tag == 'forward': |
|
dt = int (e.findtext ('duration')) |
|
if chkbug (dt, s.msr): s.msc.incTime (dt) |
|
elif e.tag == 'print': lbrk = s.doPrint (e) |
|
s.msc.addBar (lbrk, s.msr) |
|
divisions.append (s.msr.divs) |
|
if herhaal == 1: |
|
herhaalMaat = s.msr.ixm |
|
s.msr.ixm += 1 |
|
elif herhaal == 2: |
|
if aantalHerhaald < 1: |
|
s.msr.ixm = herhaalMaat |
|
aantalHerhaald += 1 |
|
else: |
|
aantalHerhaald = 0 |
|
s.msr.ixm += 1 |
|
else: s.msr.ixm += 1 |
|
for rv in s.repeat_str.values (): |
|
rv [0] = '[I:repeat %s %d]' % (rv [1], 1) |
|
vvmap = s.msc.outVoices (divisions, ip, s.isSib) |
|
s.addStaffMap (vvmap) |
|
s.addMidiMap (ip, vvmap) |
|
vvmapAll.update (vvmap) |
|
if vvmapAll: |
|
abcOut.mkHeader (s.gStfMap, partlist, s.midiMap, s.tabVceMap, s.koppen) |
|
abcOut.writeall () |
|
else: info ('nothing written, %s has no notes ...' % abcOut.fnmext) |
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
from optparse import OptionParser |
|
from glob import glob |
|
from zipfile import ZipFile |
|
ustr = '%prog [-h] [-u] [-m] [-c C] [-d D] [-n CPL] [-b BPL] [-o DIR] [-v V]\n' |
|
ustr += '[-x] [-p PFMT] [-t] [-s] [-i] [--v1] [--noped] [--stems] <file1> [<file2> ...]' |
|
parser = OptionParser (usage=ustr, version=str(VERSION)) |
|
parser.add_option ("-u", action="store_true", help="unfold simple repeats") |
|
parser.add_option ("-m", action="store", help="0 -> no %%MIDI, 1 -> minimal %%MIDI, 2-> all %%MIDI", default=0) |
|
parser.add_option ("-c", action="store", type="int", help="set credit text filter to C", default=0, metavar='C') |
|
parser.add_option ("-d", action="store", type="int", help="set L:1/D", default=0, metavar='D') |
|
parser.add_option ("-n", action="store", type="int", help="CPL: max number of characters per line (default 100)", default=0, metavar='CPL') |
|
parser.add_option ("-b", action="store", type="int", help="BPL: max number of bars per line", default=0, metavar='BPL') |
|
parser.add_option ("-o", action="store", help="store abc files in DIR", default='', metavar='DIR') |
|
parser.add_option ("-v", action="store", type="int", help="set volta typesetting behaviour to V", default=0, metavar='V') |
|
parser.add_option ("-x", action="store_true", help="output no line breaks") |
|
parser.add_option ("-p", action="store", help="pageformat PFMT (cm) = scale, pageheight, pagewidth, leftmargin, rightmargin, topmargin, botmargin", default='', metavar='PFMT') |
|
parser.add_option ("-j", action="store_true", help="switch for compatibility with javascript version") |
|
parser.add_option ("-t", action="store_true", help="translate perc- and tab-staff to ABC code with %%map, %%voicemap") |
|
parser.add_option ("-s", action="store_true", help="shift node heads 3 units left in a tab staff") |
|
parser.add_option ("--v1", action="store_true", help="start-stop directions allways to first voice of staff") |
|
parser.add_option ("--noped", action="store_false", help="skip all pedal directions", dest='ped', default=True) |
|
parser.add_option ("--stems", action="store_true", help="translate stem directions", dest='stm', default=False) |
|
parser.add_option ("-i", action="store_true", help="read xml file from standard input") |
|
options, args = parser.parse_args () |
|
if options.n < 0: parser.error ('only values >= 0') |
|
if options.b < 0: parser.error ('only values >= 0') |
|
if options.d and options.d not in [2**n for n in range (10)]: |
|
parser.error ('D should be on of %s' % ','.join ([str(2**n) for n in range (10)])) |
|
options.p = options.p and options.p.split (',') or [] |
|
if len (args) == 0 and not options.i: parser.error ('no input file given') |
|
pad = options.o |
|
if pad: |
|
if not os.path.exists (pad): os.mkdir (pad) |
|
if not os.path.isdir (pad): parser.error ('%s is not a directory' % pad) |
|
fnmext_list = [] |
|
for i in args: fnmext_list += glob (i) |
|
if options.i: fnmext_list = ['stdin.xml'] |
|
if not fnmext_list: parser.error ('none of the input files exist') |
|
for X, fnmext in enumerate (fnmext_list): |
|
fnm, ext = os.path.splitext (fnmext) |
|
if ext.lower () not in ('.xml','.mxl','.musicxml'): |
|
info ('skipped input file %s, it should have extension .xml or .mxl' % fnmext) |
|
continue |
|
if os.path.isdir (fnmext): |
|
info ('skipped directory %s. Only files are accepted' % fnmext) |
|
continue |
|
if fnmext == 'stdin.xml': |
|
fobj = sys.stdin |
|
elif ext.lower () == '.mxl': |
|
z = ZipFile(fnmext) |
|
for n in z.namelist(): |
|
if (n[:4] != 'META') and (n[-4:].lower() == '.xml'): |
|
fobj = z.open (n) |
|
break |
|
else: |
|
fobj = open (fnmext, 'rb') |
|
|
|
abcOut = ABCoutput (fnm + '.abc', pad, X, options) |
|
psr = Parser (options) |
|
try: |
|
psr.parse (fobj) |
|
except: |
|
etype, value, traceback = sys.exc_info () |
|
info ('** %s occurred: %s in %s' % (etype, value, fnmext), 0) |
|
|