# -*- coding: ISO-8859-1 -*- """ capellaScript -- Urs Ganz >>> Balken über Vorschläge hinweg fortsetzen In einer Stimme markierte Noten werden | über Vorschläge hinweg mit Balken verbunden | nach der Methode, die in der Capella-Hilfe | im Kapitel "12.2 Grafikobjekte an (unsichtbaren)| Noten verankern" beschrieben ist.| | Voraussetzung:| - Anfang und Ende der Markierung ist | jeweils eine Note (bzw. Akkord) <=1/8.. >=1/32| - Vorschläge ohne Dauer; Wert und Ausrichtung| können von den Balken-Noten abweichen| - mind. 1 Vorschlag markiert| | Vorzeichen für die Vorschlagsnoten werden| immer gesetzt und müssen von Hand nachbear-| beitet werden.| | Version 4: Urs Ganz 06.06.2005| <<< History:| Version 4: Urs Ganz 06.06.2005| - Absturz behoben, wenn Cursor am Zeilenende ohne Markierung| Version 3: Urs Ganz 07.05.2005| - Neu dürfen verschieden lange Hauptnoten vorkommen| Version 2: Urs Ganz 12.04.2005| - Version 1 war nur mit normalem G-Schlüssel korrekt| - Version 2 unterstützt alle gängigen Schlüssel| Version 1: Urs Ganz 06.04.2005| - nach einer Vorlage von Bernd Jungmann (überbalkte Pausen)| """ from caplib.capDOM import ScoreChange import tempfile def latin1(u): return u.encode('Latin-1') class MarkedScoreChange (ScoreChange): def __init__(self, inputFile, outputFile, cursor1, cursor2): self.sysI1, self.staffI1, self.voiceI1, self.objI1 = cursor1 self.sysI2,self.staffI2,self.voiceI2,self.objI2 = cursor2 self.sysCurrent = 0 self.staffCurrent = 0 self.voiceCurrent = 0 self.objCurrent = 0 self.lastchord = 0 ScoreChange.__init__(self, inputFile, outputFile) def copyElement(self, el): ScoreChange.copyElement(self, el) # Positionsmerker aktualisieren if el.tagName == 'system': self.sysCurrent = self.sysCurrent + 1 self.staffCurrent = 0 self.voiceCurrent = 0 self.objCurrent = 0 if el.tagName == 'staff': self.staffCurrent = self.staffCurrent + 1 self.voiceCurrent = 0 self.objCurrent = 0 if el.tagName == 'voice': self.voiceCurrent = self.voiceCurrent + 1 self.objCurrent = 0 if el.tagName in ['clefSign','keySign','timeSign','chord','rest','ExplicitBarline']: self.objCurrent = self.objCurrent + 1 def isInMarkedArea(self): if self.sysI1 <= self.sysCurrent and self.sysCurrent < self.sysI2: return True if self.sysI1 == self.sysCurrent and self.sysCurrent == self.sysI2 \ and self.staffI1 == self.staffCurrent \ and self.voiceI1 == self.voiceCurrent \ and self.objI1 <= self.objCurrent and self.objCurrent < self.objI2: return True return False # capella3.ttf-Code für die Grafik-Vorschlagnoten def getNoteCode(rat,drc): if str(drc) == "down": adc = 1 else: adc = 0 if str(rat) == "1/8": return unichr(164 + adc) if str(rat) == "1/16": return unichr(166 + adc) if str(rat) == "1/32": return unichr(168 + adc) return unichr(170 + adc) # capella3.ttf-Code für die Grafik-Vorzeichen def getAltrCode(stp): if str(stp) == '-2': return 'P' if str(stp) == '-1': return 'Q' if str(stp) == '': return 'R' if str(stp) == '1': return 'S' return 'T' # Grafik-Offset zu B5 gem. Schlüssel def getGoffCode(clf): gof0 = 0.0 gof1 = 0.0 gof2 = 0.0 if str(clf) == 'treble': clf = 'G2' if str(clf) == 'bass': clf = 'F4' if str(clf) == 'alto': clf = 'C3' if str(clf) == 'tenor': clf = 'C4' clf1 = str(clf).strip('12345+-') gof1 = ord(str(clf).strip('CFG+-'))-48 clf3 = str(clf).strip('CFG12345') if str(clf3) == '+': gof2 = 3.5 elif str(clf3) == '-': gof2 = -3.5 if str(clf1) == 'G': gof0 = 2.0 if str(clf1) == 'F': gof0 = -2.0 if str(clf1) == 'C': gof0 = 0.0 return gof0 - gof1 + gof2 class BalkenScoreChange (MarkedScoreChange): def changeElement(self, el): global clef global used # Schlüssel im System merken (für Grafik-Offset) if el.tagName in 'clefSign' and not used: clef = el.getAttribute('clef') if self.isInMarkedArea(): # jetzt sind wir in der Stimme, in der die Markierung ist. # Wir wissen bereits: Wir können hier alle Vorschläge (ohne Zeit) ersetzen # durch Akkorde gleicher Spieldauer wie die Hauptnoten if el.tagName == 'clefSign': # individuellen Schlüssel - falls vorhanden - merken (für Grafik-Offset) clef = el.getAttribute('clef') if el.tagName == 'chord': # Dauer merken für Auswahl des Grafikelements duration = el.getElementsByTagName('duration')[0] rDur = duration.getAttribute('base') nDur = duration.getAttribute('noDuration') if not nDur: # Hauptnoten: Sicherstellen, dass der Balken durchgezogen ist (ausser beim Letzten) if self.objCurrent < self.objI2-1: beam = self.doc.createElement('beam') el.appendChild(beam) beam.setAttribute('group','force') else: # Vorschlag (alle Attribute für die Grafik merken, Vorschlag auf gleiches # Tempo wie Hauptnote, jedoch ohne Zeit, setzen und unsichtbar machen) used = 1 heads = el.getElementsByTagName('heads')[0] newheads = el.appendChild(heads.cloneNode(True)) headlist = newheads.getElementsByTagName('head') for head in headlist: # Tonlage bestimmen pitch = head.getAttribute('pitch') lpitch = pitch.strip('1234567890') if lpitch == "A": lpitch = 'H' elif lpitch == "B": lpitch = 'I' rpitch = pitch.strip('ABCDEFG') # Vorzeichen bestimmen nalter = head.getElementsByTagName('alter') if nalter: alter = head.getElementsByTagName('alter')[0] step = alter.getAttribute('step') disp = alter.getAttribute('display') # Halsrichtung bestimmen ("up","down") nstem = el.getElementsByTagName('stem') if nstem: stem = el.getElementsByTagName('stem')[0] dir = stem.getAttribute('dir') elif rpitch > '5' or pitch == 'B5': dir = 'down' else: dir = 'up' # Vorschlags-Akkord unsichtbar machen olddisplays = el.getElementsByTagName('display') if olddisplays: display = olddisplays[0] else: display = self.doc.createElement('display') el.appendChild(display) display.setAttribute('invisible','true') # Balken durchziehen beam = self.doc.createElement('beam') el.appendChild(beam) beam.setAttribute('group','force') # Tempo wie Hauptnoten setzen (ohne Zeit) dura = self.doc.createElement('duration') dura.setAttribute('base',str(hDur)) # hDur = Global gesetzt in "BalkenVerlaengern()" dura.setAttribute('noDuration','true') el.appendChild(dura) # Vorschlag als DrawObject erzeugen font = self.doc.createElement('font') font.setAttribute('face','capella3') font.setAttribute('height','12') font.setAttribute('charSet','2') font.setAttribute('pitchAndFamily','2') text = self.doc.createElement('text') text.setAttribute('x','0') # Grafik-Offset des Tones zu B5 (default-Position) inkl. Schlüssel off = (66-ord(lpitch))*0.5+(54-ord(rpitch))*3.5+getGoffCode(clef) text.setAttribute('y',str(off)) text.appendChild(font) string = self.doc.createTextNode(getNoteCode(rDur,dir)) content = self.doc.createElement('content') content.appendChild(string) text.appendChild(content) drawObj = self.doc.createElement('drawObj') drawObj.appendChild(text) # Vorm Anhängen feststellen, ob's schon drawObjects gibt olddrawObjects = el.getElementsByTagName('drawObjects') if(olddrawObjects): drawObjects = olddrawObjects[0] else: drawObjects = self.doc.createElement('drawObjects') el.appendChild(drawObjects) drawObjects.appendChild(drawObj) # Vorzeichen als DrawObject erzeugen if nalter: font = self.doc.createElement('font') font.setAttribute('face','capella3') font.setAttribute('height','12') font.setAttribute('charSet','2') font.setAttribute('pitchAndFamily','2') text = self.doc.createElement('text') text.setAttribute('x','-1') text.setAttribute('y',str(off)) text.appendChild(font) string = self.doc.createTextNode(getAltrCode(step)) content = self.doc.createElement('content') content.appendChild(string) text.appendChild(content) drawObj = self.doc.createElement('drawObj') drawObj.appendChild(text) # Vorm Anhängen feststellen, ob's schon drawObjects gibt olddrawObjects = el.getElementsByTagName('drawObjects') if(olddrawObjects): drawObjects = olddrawObjects[0] else: drawObjects = self.doc.createElement('drawObjects') el.appendChild(drawObjects) drawObjects.appendChild(drawObj) # Hilfslinien als Drawobject erzeugen (mit Tenuto-Strich) if abs(off) >= 3.0: ofi = int(off) ido = int(-off) / abs(int(off)) while abs(ofi) >= 3: font = self.doc.createElement('font') font.setAttribute('face','capella3') font.setAttribute('height','18') font.setAttribute('charSet','2') font.setAttribute('pitchAndFamily','2') text = self.doc.createElement('text') text.setAttribute('x','-0.5') text.setAttribute('y',str(ofi)) text.appendChild(font) string = self.doc.createTextNode(unichr(200)) content = self.doc.createElement('content') content.appendChild(string) text.appendChild(content) drawObj = self.doc.createElement('drawObj') drawObj.appendChild(text) # Vorm Anhängen feststellen, ob's schon drawObjects gibt olddrawObjects = el.getElementsByTagName('drawObjects') if(olddrawObjects): drawObjects = olddrawObjects[0] else: drawObjects = self.doc.createElement('drawObjects') el.appendChild(drawObjects) drawObjects.appendChild(drawObj) ofi = ofi + ido def BalkenVerlaengern(score,(sysind, staffind, voiceind, objind),(sysind2, staffind2, voiceind2, objind2)): if sysind == sysind2 and staffind == staffind2 and voiceind == voiceind2: sys = score.system(sysind) staff = sys.staff(staffind) voice = staff.voice(voiceind) no = voice.nNoteObjs() if objind <= no and objind2 <= no and (objind <> no or objind2 <> no): # man kann auch rückwärts markieren. Wozu eigentlich?? if objind > objind2: obji = objind objind = objind2 objind2 = obji # wir untersuchen, ob gleichlange Balkennoten mit Vorschlägen dazwischen vorliegen firstobj = voice.noteObj(objind) duration = firstobj.duration() maxduration = Rational("7/32") minduration = Rational("1/32") if firstobj.isChord() and duration <= maxduration and duration >= minduration: # wir merken uns die Dauer der Balkennoten für den unsichtbaren Ersatz der Vorschläge global clef clef = 0 global used used = 0 global hDur if duration <= Rational("7/32") and duration >= Rational("1/8"): hDur = Rational("1/8") elif duration <= Rational("7/64") and duration >= Rational("1/16"): hDur = Rational("1/16") elif duration <= Rational("7/128") and duration >= Rational("1/32"): hDur = Rational("1/32") ind = objind + 1 nVorschlag = 0 while ind < objind2: obj = voice.noteObj(ind) if (obj.duration() <= maxduration and obj.duration() >= minduration) or obj.duration() == 0: if obj.isChord(): if obj.duration() == 0: nVorschlag = nVorschlag + 1 ind = ind + 1 else: break if ind == objind2 and nVorschlag > 0 and obj.isChord() and obj.duration() <> 0: # wir sind sicher, dass erstes und letztes Objekt Akkorde mit gleich vielen # Balken sind und dass dazwischen höchstens Vorschläge ohne Wert liegen. score.registerUndo("Balken über Vorschlägen") tempFile1 = tempfile.mktemp('.capx') tempFile2 = tempfile.mktemp('.capx') score.write(tempFile1) BalkenScoreChange(tempFile1,tempFile2,(sysind, staffind, voiceind, objind),\ (sysind, staffind, voiceind, objind2)) score.read(tempFile2) os.remove(tempFile1) os.remove(tempFile2) else: messageBox("Keine Aktion!","Die Markierung muß mit einer Note beginnen, mit einer Note aufhören \n\ und muss mindestens einen Vorschlag (ohne Zeit) enthalten.") else: messageBox("Keine Aktion!","Der markierte Bereich muss mit einer Note <= 1/8.. und >= 1/32 anfangen") else: messageBox("Keine Aktion!","Die Markierung muß mit einer Note beginnen, mit einer Note aufhören \n\ und muss mindestens einen Vorschlag (ohne Zeit) enthalten.") else: messageBox("Keine Aktion!","Markierung muss in einer einzigen Zeile und Stimme sein") # Hauptprogramm as = activeScore() if as: sel = curSelection() start = sel[0] stop = sel[1] BalkenVerlaengern(as, start, stop)