# -*- coding: ISO-8859-1 -*-
""" capellaScript -- (c) Paul Villiger
>>> Brillenschlangen
Mit diesem Script lassen sich Tremoli oder sg. Brillenschlangen realisieren.
Eingabemöglichkeiten:|
- Anzahl der Balken|
- Anzahle der langen Balken|
- Halsrichtung nach oben/unten
- Balken mit oder ohne Abstand zum Hals|
- Balkenabstand zum Hals|
- Faktor zwischen langen und kurzen Balken|
- Alle Tremoli justieren|
- Tremoli für Vorspiel auflösen
Anwendung: von der ersten bis zur letzten Note markieren und Skript aufrufen.
Wenn sich das Notenbild ändert, das Skript nochmals aufrufen.
Die erste Note bestimmt die Dauer, alle folgenden Noten sind ohne Wert. Die Noten in der Mitte
sind unsichtbar und dienen als Platzhalter. Die Dialogwerte werden gespeichert. Die Tremolibalken
hängen am Halsende und können mit n + auf/ab verschoben werden.
<<<
History: 21.04.04 - Erste Ausgabe
22.04.04 - Anpassung über ganze Partitur möglich
25.04.04 - neu Halsrichtung oben/unten implementiert
25.05.04 - Capella Stepup 05, posX enthält jetzt auch shiftX
25.06.04 - Fehler wenn Noten von rechts nach links markiert wurden
29.08.07 - Script-Internationalisierung
15.09.09 - Brillenschlangen auflösen
31.01.11 - geänderte Halslängen werden berücksichtigt
Halslängen bei Akkorden werden richtig berechnet
01.02.11 - Einstellung maximale Balkensteigung
- Automatische Anpassung der Halslänge
- Halslängen zurücksetzen
03.02.11 - Balken horizontal justiert
"""
german = ("de", {
'beamCount' : 'Anzahl Balken',
'longBeamCount' : 'Anzahl lange Balken',
'beamAtStem' : 'Balken am Notenhals',
'stemDirUp' : 'Halsrichtung nach oben',
'distToStem' : 'Abstand vom Notenhals',
'factorBeam' : 'Faktor lange/kurze Balken',
'recalc1' : 'Alle Elemente der ganzen Partitur\n\rneu berechnen',
'recalc2' : 'Wenn Noten verschoben werden, Skript erneut aufrufen',
'dialogHeader' : '--- Brillenschlangen ---',
'error' : 'Fehler',
'noActiveScore' : 'keine aktive Partitur',
'regUndo' : 'Brillenschlangen',
'expandAll' : 'Brillenschlangen expandieren',
'noLimit' : 'keine Anpassung',
'resetStem' : 'Halslänge zurücksetzen',
'maxInclination' : 'Maximale Balkensteigung \n\r (Notenzeilen/2)'
})
try:
exec('from %s import translations' % ( translationModule() ))
translations.append(german)
setLanguages(translations)
except:
def tr(s):
return german[1].get(s, "???")
#-------------------------------------------------------------------
from xml.dom.minidom import parseString, NodeList, Node, Element
from caplib.rational import Rational
import tempfile, string, new
# START SETTINGS ################
class settings:
def __init__(self):
self.stemDirUp = True
self.atStem = True
self.numBars = 2
self.numLongBars = 2
self.factShortBars = 0.5
self.stemDist = 0.5
self.changeAll = False
self.expandAll = False
self.maxInclination = 3
defaults = settings()
dlgSet = settings()
options = ScriptOptions()
opt = options.get()
def getOptions():
global dlgSet
dlgSet.stemDirUp = eval(opt.get('Brillenschlange_stemDirUp', str(dlgSet.stemDirUp)))
dlgSet.atStem = eval(opt.get('Brillenschlange_atStem', str(dlgSet.atStem)))
dlgSet.stemDist = eval(opt.get('Brillenschlange_stemDist', str(dlgSet.stemDist)))
dlgSet.numBars = eval(opt.get('Brillenschlange_numBars', str(dlgSet.numBars)))
dlgSet.numLongBars = eval(opt.get('Brillenschlange_numLongBars', str(dlgSet.numLongBars)))
dlgSet.factShortBars = eval(opt.get('Brillenschlange_factShortBars',str(dlgSet.factShortBars)))
dlgSet.maxInclination = eval(opt.get('Brillenschlange_maxInclination',str(dlgSet.maxInclination)))
def setOptions():
global dlgSet
opt.update(dict(Brillenschlange_stemDirUp = str(dlgSet.stemDirUp),
Brillenschlange_atStem = str(dlgSet.atStem),
Brillenschlange_stemDist = str(dlgSet.stemDist),
Brillenschlange_numBars = str(dlgSet.numBars),
Brillenschlange_numLongBars = str(dlgSet.numLongBars),
Brillenschlange_factShortBars= str(dlgSet.factShortBars),
Brillenschlange_maxInclination= str(dlgSet.maxInclination)
))
options.set(opt)
# END SETTINGS ################
doc = parseString('')
def gotoChild(self, name, new=False):
newEl = None
if new:
pass
else:
for child in self.childNodes:
if child.nodeType == child.ELEMENT_NODE and child.tagName == name:
newEl = child
break
if newEl == None:
newEl = doc.createElement(name)
self.appendChild(newEl)
return newEl
Node.gotoChild = new.instancemethod(gotoChild,None,Node)
def scriptDialog():
global dlgSet
getOptions()
labelLeerzeile=Label('', width = 20)
cboxBalken = ComboBox (['1','2','3','4'], value = dlgSet.numBars - 1 )
cboxBalkenLang = ComboBox (['1','2','3','4'], value = dlgSet.numLongBars -1 )
cboxAmHals = CheckBox('', value = dlgSet.atStem)
cboxStemUp = CheckBox('', value = dlgSet.stemDirUp)
editAbstand = Edit(str(dlgSet.stemDist))
editFaktor = Edit(str(dlgSet.factShortBars))
maxInclination = ComboBox([tr('resetStem'),tr('noLimit'), '0','1','2','3','4','5'], width = 17, value = dlgSet.maxInclination + 2)
cboxAll = CheckBox('', value = False)
expandAll = CheckBox('', value = False)
labelLeer = Label(' ')
vbox = VBox([HBox([Label(tr('beamCount'), width = 25), cboxBalken]),
HBox([Label(tr('longBeamCount'), width = 25), cboxBalkenLang]),
HBox([Label(tr('stemDirUp'), width = 25), cboxStemUp]),
HBox([Label(tr('beamAtStem'), width = 25), cboxAmHals]),
HBox([Label(tr('distToStem'),width= 25), editAbstand]),
HBox([Label(tr('factorBeam'),width= 25), editFaktor]),
HBox([Label(tr('maxInclination'), width = 25), maxInclination ]),
labelLeer,
HBox([Label(tr('recalc1'), width = 25), cboxAll]),
labelLeer,
HBox([Label(tr('expandAll'), width= 25), expandAll]),
labelLeer,
Label(tr('recalc2')),
labelLeer])
dlg = Dialog(tr('dialogHeader'), vbox)
if dlg.run():
dlgSet.numBars = cboxBalken.value() + 1
dlgSet.numLongBars = cboxBalkenLang.value() + 1
dlgSet.atStem = cboxAmHals.value()
dlgSet.stemDirUp = cboxStemUp.value()
dlgSet.stemDist = eval(editAbstand.value())
dlgSet.factShortBars = eval(editFaktor.value())
dlgSet.changeAll = cboxAll.value()
dlgSet.expandAll = expandAll.value()
dlgSet.maxInclination = maxInclination.value() - 2
if dlgSet.numLongBars > dlgSet.numBars:
dlgSet.numLongBars = dlgSet.numBars
setOptions()
def getCursor():
sel = curSelection()
result = None
if sel == 0:
messageBox( tr('error'), tr('noActiveScore') )
return result
result = sel
return result
def getElementObjects(objList): # returns a List
newList = NodeList()
for n in range(objList.length):
if objList[n].nodeType == objList[n].ELEMENT_NODE:
newList.append(objList[n])
return newList
def addElementNode(el,tagName):
# add new Node to el if Node "tagName" does not exist
# otherwise return the existing Node
global doc
childs = el.childNodes
for n in range(childs.length):
if childs[n].nodeType ==childs[n].ELEMENT_NODE and childs[n].tagName == tagName:
return childs[n]
newChild = doc.createElement(tagName)
el.appendChild(newChild)
return newChild
def addNewElementNode(el,tagName):
# add new Node with tagName "tagName" to el
global doc
newChild = doc.createElement(tagName)
el.appendChild(newChild)
return newChild
def getPositions(sel):
[sy,st,vo,ob1],[sy2,st2,vo2, ob2] = sel
system = activeScore().system(sy)
staff = system.staff(st)
voice = staff.voice(vo)
pos1 = voice.noteObj(ob1).posX()
pos2 = voice.noteObj(ob2-1).posX()
return (pos1, pos2)
def addBarLine(obj, x1, y1, dx, dy, d, e1, e2):
# d = Distanz x zum Hals
# e1/2 = Extradistanz x Hals1/2
s = dy / dx # Steigung
px1 = x1 + d + e1
py1 = y1 + px1 * s
px2 = x1 + dx - d - e2
py2 = y1 + px2 * s
drawObj = addNewElementNode(obj,'drawObj')
polygon = addNewElementNode(drawObj,'polygon')
polygon.setAttribute('filled','true')
polygon.setAttribute('lineWidth','0')
basic = addNewElementNode(drawObj,'basic')
basic.setAttribute('horizAlign','1')
basic.setAttribute('vertAlign','3')
points = addNewElementNode(polygon,'points')
point = addNewElementNode(points,'point')
point.setAttribute('x',str(px1))
point.setAttribute('y',str(py1))
point = addNewElementNode(points,'point')
point.setAttribute('x',str(px1))
point.setAttribute('y',str(py1 +0.5))
point = addNewElementNode(points,'point')
point.setAttribute('x',str(px2))
point.setAttribute('y',str(py2+0.5))
point = addNewElementNode(points,'point')
point.setAttribute('x',str(px2))
point.setAttribute('y',str(py2))
def setNoteAttributes(score, sel, stemUp):
[sy,st,vo,ob1],[sy2,st2,vo2, ob2] = sel
system = score.getElementsByTagName('system')[sy]
staff = system.getElementsByTagName('staff')[st]
voice = staff.getElementsByTagName('voice')[vo]
noteObject = voice.getElementsByTagName('noteObjects')[0]
objList = getElementObjects(noteObject.childNodes)
dur1 = '1/1'
for no in objList[ob1:ob2]:
if no.tagName == 'chord':
stem = addElementNode(no,'stem')
if stemUp:
stem.setAttribute('dir','up')
else:
stem.setAttribute('dir','down')
duration = addElementNode(no,'duration')
display = addElementNode(no,'display')
if no == objList[ob1]:
dur1 = duration.getAttribute('base')
else:
duration.setAttribute('noDuration','true')
display.setAttribute('postGrace','true')
if no == objList[ob2-1]:
pass
else:
display.setAttribute('invisible','true')
return dur1
def getLengthening(chord):
lengthening = 0.0
stem = chord.getElementsByTagName('stem')
if stem:
if stem[0].hasAttribute('lengthening'):
lengthening = float(stem[0].getAttribute('lengthening'))
return lengthening
def addLengthening(chord, leng):
stem = chord.gotoChild('stem')
if stem.hasAttribute('lengthening'):
lengthening = float(stem.getAttribute('lengthening'))
else:
lengthening = 0.0
lengthening += leng
stem.setAttribute('lengthening', str(lengthening))
def clearLengthening(chord):
stem = chord.getElementsByTagName('stem')
if stem:
if stem[0].hasAttribute('lengthening'):
stem[0].removeAttribute('lengthening')
def addDiatonic(pitch, offset):
dp = string.find('CDEFGAB', pitch[0]) + 7 * int(pitch[1])
dp += offset
dpNew = 'CDEFGAB'[dp % 7] + str(dp / 7)
return dpNew
def clef2CenterPitch(clef):
clefs = {'bass':'F4','treble':'G2','alto':'C3','tenor':'C4'}
cl = clefs.get(clef, clef)
clef2Pitch = {'G':'F6', 'C': 'B5', 'F': 'E5'}
pitch = clef2Pitch[cl[0]]
pitch = addDiatonic(pitch, - 2 * int(cl[1]))
if len(cl) > 2:
if cl[2] == '-':
pitch[1] -= 1
elif cl[2] == '+':
pitch[1] += 1
return pitch
def setBarLines(score,sel):
notenString='CDEFGAB'
[sy,st,vo,ob1],[sy2,st2,vo2, ob2] = sel
ob2 = ob2 - 1
system = score.getElementsByTagName('system')[sy]
staff = system.getElementsByTagName('staff')[st]
voice = staff.getElementsByTagName('voice')[vo]
noteObject = voice.getElementsByTagName('noteObjects')[0]
objList = getElementObjects(noteObject.childNodes)
obj1 = objList[ob1]
obj2 = objList[ob2]
# get clef offset
clef1 = clef2 = 0
for i in range(len(objList)):
if objList[i].tagName == 'clefSign':
cl = clef2CenterPitch( objList[i].getAttribute('clef'))
off = notenString.find(cl[0]) + 7 * int(cl[1])
if i < ob1:
clef1 = off
if i < ob2:
clef2 = off
if ob1 < ob2 and obj1.tagName == 'chord' and obj2.tagName == 'chord':
(pos1, pos2) = getPositions(sel)
heads1 = obj1.getElementsByTagName('head')
heads2 = obj2.getElementsByTagName('head')
if dlgSet.stemDirUp:
pitch1 = heads1[heads1.length -1].getAttribute('pitch')
pitch2 = heads2[heads2.length -1].getAttribute('pitch')
lenDir = +1
else:
pitch1 = heads1[0].getAttribute('pitch')
pitch2 = heads2[0].getAttribute('pitch')
lenDir = -1
off1 = notenString.find(pitch1[0]) + 7 * int(pitch1[1])
off2 = notenString.find(pitch2[0]) + 7 * int(pitch2[1])
# adjust to center line
if dlgSet.stemDirUp:
if clef1 - off1 > 7 :
off1 = clef1 - 7
if clef2 - off2 > 7 :
off2 = clef2 - 7
else:
if off1 - clef1 > 7 :
off1 = clef1 + 7
if off2 - clef2 > 7 :
off2 = clef2 + 7
if dlgSet.maxInclination == -2:
clearLengthening(obj1)
clearLengthening(obj2)
off1 += getLengthening(obj1) * 2.0 * lenDir
off2 += getLengthening(obj2) * 2.0 * lenDir
if dlgSet.maxInclination >= 0:
maxDy = dlgSet.maxInclination
if abs(off1 - off2) > maxDy:
if dlgSet.stemDirUp:
if off1 > off2:
addLengthening(obj2, (off1 - off2 - maxDy) / 2.0 )
off2 = off1 - maxDy
elif off2 > off1:
addLengthening(obj1, (off2 - off1 - maxDy) / 2.0 )
off1 = off2 - maxDy
else:
if off1 > off2:
addLengthening(obj1, (off1 - off2 - maxDy) / 2.0 )
off1 = off2 + maxDy
elif off2 > off1:
addLengthening(obj2, (off2 - off1 - maxDy) / 2.0 )
off2 = off1 + maxDy
dy = (off1 - off2) / 2.0
dx = pos2 - pos1
dur1= setNoteAttributes(score,sel,dlgSet.stemDirUp)
if dur1 == '2/1':
extraDist = 2.0
elif dur1 =='1/1':
extraDist = 1.4
else:
extraDist = 0
if dlgSet.atStem:
stemDist = 0
else:
stemDist = dlgSet.stemDist
drawObjects = addElementNode(obj1,'drawObjects')
for basic in drawObjects.getElementsByTagName('basic'):
if basic.hasAttribute('tag') and '1793-' in basic.getAttribute('tag'):
basic.parentNode.parentNode.removeChild(basic.parentNode)
drawObj = addNewElementNode(drawObjects,'drawObj')
group = addNewElementNode(drawObj,'group')
basic = addNewElementNode(drawObj,'basic')
# Die Formatinformation wird im zweiten Teil des Tags abgespeichert:
# Einer Bit 0,1 = Anzahl Balken -1, Bit 2 = atStem
# Zehner Bit 0,1 = Anzahl Langer Balken -1, Bit 2 = stemDirUp
# Hunderter = Anzahl Noten
tag = (dlgSet.numBars-1) + 4 * int(dlgSet.atStem) + 10 * (dlgSet.numLongBars-1) + 40 * int(dlgSet.stemDirUp) + 100 * (ob2 - ob1)
basic.setAttribute('tag','1793-'+str(tag))
if dlgSet.stemDirUp:
for n in range(dlgSet.numBars):
if n < dlgSet.numLongBars:
addBarLine(group, -0.05, n * 0.75, dx, dy, stemDist, 0, extraDist)
else:
addDist = (dx - extraDist - 2.0 * stemDist) * (1-dlgSet.factShortBars) / 2.0
addBarLine(group, -0.05, n * 0.75, dx, dy, stemDist+addDist, 0, extraDist)
else:
for n in range(dlgSet.numBars):
if n < dlgSet.numLongBars:
addBarLine(group, +0.05, - n * 0.75 - 0.5, dx, dy, stemDist, extraDist, 0)
else:
addDist = (dx - extraDist - 2.0 * stemDist) * (1-dlgSet.factShortBars) / 2.0
addBarLine(group, +0.05, - n * 0.75 - 0.5, dx, dy, stemDist+addDist, extraDist, 0)
def getDuration(note):
""" Dauer einer Note oder Pause als rationale Zahl in ganzen Noten
muss ein 'chord'- oder 'rest'-Knoten sein
(kopiert aus capDOM.py und angepasst)
"""
duration = note.gotoChild('duration')
if duration.getAttribute('noDuration') == 'true':
return 0
n = Rational(str(duration.getAttribute('base')))
dots = duration.getAttribute('dots')
if dots != '':
if int(dots) == 1: n = (3 * n) / 2
elif int(dots) == 2: n = (7 * n) / 4
else: n = (15 * n) / 8
tuplet = duration.gotoChild('tuplet')
if tuplet.hasAttribute('count'):
denominator = Rational(int(tuplet.getAttribute('count')))
numerator = Rational(1)
if tuplet.getAttribute('tripartite') == 'true':
numerator = Rational(3,2)
while numerator < denominator: numerator *= 2
if tuplet.getAttribute('prolong') != 'true':
numerator /= 2
n = (n * numerator) / denominator
return n
def expandBarLines(score,sel):
notenString='CDEFGAB'
[sy,st,vo,ob1],[sy2,st2,vo2, ob2] = sel
ob2 = ob2 - 1
system = score.getElementsByTagName('system')[sy]
staff = system.getElementsByTagName('staff')[st]
voice = staff.getElementsByTagName('voice')[vo]
noteObject = voice.getElementsByTagName('noteObjects')[0]
objList = getElementObjects(noteObject.childNodes)
obj1 = objList[ob1]
obj2 = objList[ob2]
if ob1 < ob2 and obj1.tagName == 'chord' and obj2.tagName == 'chord':
noteDuration = getDuration(obj1)
heads1 = obj1.getElementsByTagName('heads')[0]
heads2 = obj2.getElementsByTagName('heads')[0]
singleDuration = Rational('1/4') / 2**dlgSet.numBars
while noteDuration > 0:
el1 = doc.createElement('chord')
duration = el1.gotoChild('duration')
duration.setAttribute('base',str(singleDuration))
newHead = heads1.cloneNode(True)
el1.appendChild(newHead)
obj1.parentNode.insertBefore(el1,obj1)
el1 = doc.createElement('chord')
duration = el1.gotoChild('duration')
duration.setAttribute('base',str(singleDuration))
newHead = heads2.cloneNode(True)
el1.appendChild(newHead)
obj1.parentNode.insertBefore(el1,obj1)
noteDuration -= 2 * singleDuration
objects = objList[ob1:ob2+1]
for ob in objects:
ob.parentNode.removeChild(ob)
def changeDoc(score):
global fonts, dlgSet
scriptDialog()
if not (dlgSet.changeAll or dlgSet.expandAll):
sel = getCursor()
if sel == None or sel[0] == sel[1] or sel[0][0:3] <> sel[1][0:3]:
return
if sel[0][3] > sel[1][3]:
[a,b,c,d],[e,f,g,h] = sel
sel = [e,f,g,h],[a,b,c,d]
setBarLines(score,sel)
elif dlgSet.expandAll:
systems = score.getElementsByTagName('system')
for sy in range(systems.length):
staves = systems[sy].getElementsByTagName('staff')
for st in range(staves.length):
voices = staves[st].getElementsByTagName('voice')
for vo in range(voices.length):
noteObject = voices[vo].getElementsByTagName('noteObjects')[0]
found = True
while found:
found = False
objList = getElementObjects(noteObject.childNodes)
for ob in range(objList.length):
sel = None
for basic in objList[ob].getElementsByTagName('basic'):
if basic.hasAttribute('tag') and '1793-' in basic.getAttribute('tag'):
found = True
num = eval(basic.getAttribute('tag')[5:])
ob2 = ob + num / 100 + 1
dlgSet.numBars = 1 + (num % 10) % 4
dlgSet.numLongBars = 1 + ((num %100) / 10 ) % 4
dlgSet.stemDirUp = num % 100 >= 40
dlgSet.atStem = num % 10 >= 4
sel = [sy, st, vo, ob],[sy, st, vo, ob2]
expandBarLines(score,sel)
if found: break
if found: break
elif dlgSet.changeAll :
systems = score.getElementsByTagName('system')
for sy in range(systems.length):
staves = systems[sy].getElementsByTagName('staff')
for st in range(staves.length):
voices = staves[st].getElementsByTagName('voice')
for vo in range(voices.length):
noteObject = voices[vo].getElementsByTagName('noteObjects')[0]
objList = getElementObjects(noteObject.childNodes)
for ob in range(objList.length):
sel = None
for basic in objList[ob].getElementsByTagName('basic'):
if basic.hasAttribute('tag') and '1793-' in basic.getAttribute('tag'):
num = eval(basic.getAttribute('tag')[5:])
ob2 = ob + num / 100 + 1
dlgSet.numBars = 1 + (num % 10) % 4
dlgSet.numLongBars = 1 + ((num %100) / 10 ) % 4
dlgSet.stemDirUp = num % 100 >= 40
dlgSet.atStem = num % 10 >= 4
sel = [sy, st, vo, ob],[sy, st, vo, ob2]
setBarLines(score,sel)
# Hauptprogramm:
from caplib.capDOM import ScoreChange
import tempfile
class ScoreChange(ScoreChange):
def changeScore(self, score):
global doc
doc = score.parentNode
changeDoc(score)
if activeScore():
activeScore().registerUndo( tr('regUndo') )
tempInput = tempfile.mktemp('.capx')
tempOutput = tempfile.mktemp('.capx')
activeScore().write(tempInput)
ScoreChange(tempInput, tempOutput)
activeScore().read(tempOutput)
os.remove(tempInput)
os.remove(tempOutput)