# -*- coding: ISO-8859-1 -*- """(c) 2012 Bernd Jungmann >>> Sibelius 7 Patch Falls capella beim Import von MusicXML-Dateien, die mit Sibelius 7 erzeugt sind, abstürzen sollte, kann dieses Skript vielleicht helfen.| Es kann auch helfen, falls bei Notenzeilen, die mit geschweiften Klammern zusammengefasst sind, die Noten in einer einzigen Zeile dargestellt werden, falls Wiederholungszeichen fehlen, falls überflüssige unsichtbare Pausen vorkommen oder falls Grafikobjekte fälschlich weit weg von ihren Ankernoten angezeigt werden.|| Falls in einer MusicXML-Datei meinedatei.xml Problemstellen gefunden werden, wird daraus die abgewandelte Version meinedatei_mod.xml erzeugt, in der bestimmte für capella bis Version 7.1-?? problematische MusicXML-Konstrukte vermieden sind. || Bernd Jungmann 8.5.2014 <<< Sibelius 7 Patch

If capella should crash on import of MusicXML files generated by Sibelius 7 or other, this script may be able to help.

It can help, too, if in stave pairs associated with braces, all notes are written to only one stave, if repeat marks are lacking, if superfluous invisible rests occur, or if graphic objects are displayed with a considerable x-offset from their expected positions.

If the script finds such issues in a MusicXML file myfile.xml, it creates the changed file myfile_mod.xml which can be imported more successfully.

Bernd Jungmann 08/05/14

Sibelius 7 Patch

Falls capella beim Import von MusicXML-Dateien, die mit Sibelius 7 oder anderen Programmen erzeugt sind, abstürzen sollte, kann dieses Skript vielleicht helfen.

Es kann auch helfen, falls bei Notenzeilen, die mit geschweiften Klammern zusammengefasst sind, die Noten in einer einzigen Zeile dargestellt werden, falls Wiederholungszeichen fehlen, falls überflüssige unsichtbare Pausen vorkommen oder falls Grafikobjekte fälschlich weit weg von ihren Ankernoten angezeigt werden.

Falls in einer MusicXML-Datei meinedatei.xml Problemstellen gefunden werden, wird daraus die abgewandelte Version meinedatei_mod.xml erzeugt, in der bestimmte für capella problematische MusicXML-Konstrukte vermieden sind.

Bernd Jungmann 8.5.14

Vermeidung von Widersprüchen, falls in note mit keine voice-Angabe steht 8.5.14 Absturzvermeidung bei Fontnamen mit mehr als 32 Zeichen 18.4.13 Korrekter Import bei 0 3.4.13 Absturzvermeidung bei metronome ohne beat-unit und beat-unit-dot 18.1.13 Absturzvermeidung bei print new-page im ersten Takt 7.11.12. Fremdsprachenunterstützung 31.5.12 patchLyric jetzt noch robuster. 26.4.2012 """ german = { "title" : "Sibelius 7 Patch", "noChange":"keine Änderung für capella erforderlich", "LyricsChanges":" Änderungen lyric number", "VoiceChanges":" Änderungen voice", "BarlineChanges":" Änderungen barline", "ForwardBackward":" Änderungen forward - backward", "PrintChanges":" Änderungen print", "XOffsetChanged":" Änderungen default-x", "Metronome":" Änderungen metronome", "Alter":" Änderungen alter", "FontFamily":" Änderungen font-family", "doitPrompt":" sind erforderlich. Jetzt durchführen?", "filter":"MusicXML-Dateien (*.xml)", "open":"MusicXML-Datei öffnen", "done":" durchgeführt" } english = { "title" : "Sibelius 7 Patch", "noChange":"No change for capella necessary", "LyricsChanges":" changes lyric number", "VoiceChanges":" changes voice", "BarlineChanges":" barline changes", "ForwardBackward":" changes forward - backward", "PrintChanges":" changes print", "XOffsetChanged":" changes default-x", "Metronome":" changes metronome", "Alter":" changes alter", "FontFamily":" Änderungen font-family", "doitPrompt":" are necessary. Change now?", "filter":"MusicXML files (*.xml)", "open":"Open MusicXML file", "done":" executed" } try: setStringTable( ("en", english), ("de", german)) except: def tr(s): return english[s] import xml.dom.minidom, getopt, sys, string query = False def getFirstChild(parent, tag): for child in parent.childNodes: if child.nodeType == child.ELEMENT_NODE and child.tagName == tag: return child return None def getText(parent): for child in parent.childNodes: if child.nodeType == child.TEXT_NODE: return child.nodeValue def setText(parent, text): for child in parent.childNodes: if child.nodeType == child.TEXT_NODE: child.nodeValue = text def patchLyric(note): # Für capella ab 7.1-09 nicht mehr nötig changed = 0 myNumber = 1 for lyric in note.childNodes: if lyric.nodeType == lyric.ELEMENT_NODE and lyric.tagName == "lyric": number = lyric.getAttribute("number") if not str(myNumber) == number: lyric.setAttribute("number",str(myNumber)) changed += 1 myNumber += 1 return changed def patchBarline(doc, barline): report = 0 repeat = getFirstChild(barline, "repeat") if repeat: # Für capella ab 7.1-09 nicht mehr nötig barstyle = getFirstChild(barline,"bar-style") if not barstyle: barstyle = doc.createElement("bar-style") if repeat.getAttribute("direction") == "backward": styletext = "light-heavy" else: styletext = "heavy-light" barstyle.appendChild(doc.createTextNode(styletext)) barline.appendChild(barstyle) report = 1 # nicht am Taktende? if barline.nextSibling: nextS = barline.nextSibling while nextS.nodeType != nextS.ELEMENT_NODE and nextS.nextSibling: nextS = nextS.nextSibling if nextS.nodeType == nextS.ELEMENT_NODE and nextS.tagName == "backup": lastS = None while nextS.nextSibling: nextS = nextS.nextSibling if nextS.nodeType == nextS.ELEMENT_NODE: lastS = nextS if lastS and lastS.tagName != "barline": if nextS.nodeType == nextS.ELEMENT_NODE: barline.parentNode.appendChild(barline) else: barline.parentNode.insertBefore(barline, nextS) report += 1 return report def patchForward(forward): nextF = forward.nextSibling while nextF and nextF.nodeType != nextF.ELEMENT_NODE: nextF = nextF.nextSibling if nextF and nextF.nodeType == nextF.ELEMENT_NODE: if nextF.tagName == "backup": durationB = getFirstChild(nextF, "duration") durationF = getFirstChild(forward, "duration") if getText(durationB) == getText(durationF): forward.parentNode.removeChild(nextF) forward.parentNode.removeChild(forward) return 1 return 0 def patchXOffset(direction): # Für capella ab 7.1-09 nicht mehr nötig wordss = direction.getElementsByTagName("words") for words in wordss: if words.getAttribute("default-x"): words.removeAttribute("default-x") return 1 return 0 def patchMetronome(direction): metronomes = direction.getElementsByTagName("metronome") for metronome in metronomes: beatunit = metronome.getElementsByTagName("beat-unit") if len(beatunit) == 0: beatunitdot = metronome.getElementsByTagName("beat-unit-dot") if len(beatunitdot) == 0: # capella bis 7.1-14 stürzt dann ab metronome.parentNode.removeChild(metronome) return 1 return 0 def patchAlter(note): Alters = note.getElementsByTagName("alter") for alter in Alters: text = getText(alter) if float(text) == 0.0: alter.parentNode.removeChild(alter) return 1 return 0 def patchFontFamily(root): truncated = 0 creditWords = root.getElementsByTagName("credit-words") for word in creditWords: fontfamily = word.getAttribute("font-family") if len(fontfamily) > 32: word.setAttribute("font-family",fontfamily[0:31]) truncated +=1 words = root.getElementsByTagName("words") for word in words: fontfamily = word.getAttribute("font-family") if len(fontfamily) > 32: word.setAttribute("font-family",fontfamily[0:31]) truncated +=1 return truncated def patch(inFile, outFile): doc = xml.dom.minidom.parse(inFile) root = getFirstChild(doc, "score-partwise") reportLyricChanges = 0 reportBarlineChanges = 0 reportForwardBackward = 0 reportXOffsetChanged = 0 reportPrintChanges = 0 reportMetronomesRemoved = 0 reportAltersRemoved = 0 reportFontFamilyTruncated = patchFontFamily(root) for part in root.childNodes: if part.nodeType == part.ELEMENT_NODE and part.tagName == "part": nMeasure = 0 for measure in part.childNodes: if measure.nodeType == measure.ELEMENT_NODE and measure.tagName == "measure": nMeasure += 1 for obj in measure.childNodes: if obj.nodeType == obj.ELEMENT_NODE: # print new-page="yes" im ersten Takt bringt capella durcheinander. # Das ist wohl ein capella-Fehler, kann aber hier umgangen werden. if obj.tagName == "print": if nMeasure == 1: if obj.getAttribute("new-page") == "yes": obj.removeAttribute("new-page") reportPrintChanges += 1 # Taktstriche nicht am Taktende, Taktstriche mit default bar-style? if obj.tagName == "barline": reportBarlineChanges += patchBarline(doc, obj) for obj in measure.childNodes: if obj.nodeType == obj.ELEMENT_NODE: # partXvoiceY -> Y if obj.tagName == "note": reportLyricChanges += patchLyric(obj) reportAltersRemoved += patchAlter(obj) # überflüssige forward - backward - Situationen? elif obj.tagName == "forward": reportForwardBackward += patchForward(obj) # default-x bei words-Elementen wird in capella fälschlich wie relative-x interpretiert elif obj.tagName == "direction": reportXOffsetChanged += patchXOffset(obj) reportMetronomesRemoved += patchMetronome(obj) # voice 1 in stave 2 soll nicht sein (Bei Notation in Nachbarzeile ist das aber möglich) reportVoiceChanges = 0 for child in root.childNodes: if child.nodeType == child.ELEMENT_NODE and child.tagName == "part": maxVoice = {} minVoice = {} notes = child.getElementsByTagName("note") # auch noch bei direction, evtl. weitere? notes.extend(child.getElementsByTagName("direction")) # Analyse for note in notes: staff = getFirstChild(note, "staff") if staff: staffNumber = int(getText(staff)) voice = getFirstChild(note, "voice") if voice: voiceNumber = int(getText(voice)) else: voiceNumber = 1 if not maxVoice.has_key(staffNumber) or maxVoice[staffNumber] < voiceNumber: maxVoice[staffNumber] = voiceNumber if not minVoice.has_key(staffNumber) or minVoice[staffNumber] > voiceNumber: minVoice[staffNumber] = voiceNumber # Ist etwas an den voice-Nummern zu ändern? toAdd = 0 for i in range(1, len(maxVoice)): if minVoice[i+1] <= maxVoice[i]: toAdd += maxVoice[i] - minVoice[i+1] + 1 # Modifikation for note in notes: staff = getFirstChild(note, "staff") if staff: staffNumber = int(getText(staff)) if staffNumber == i+1: voice = getFirstChild(note, "voice") if voice: voiceNumber = int(getText(voice)) setText(voice, str(voiceNumber + toAdd)) reportVoiceChanges += 1 else: chord = getFirstChild(note, "chord") if chord == None: # falls in chord eine voice-Angabe fehlt, nichts ändern! voiceNumber = 1 voice = doc.createElement("voice") voice.appendChild(doc.createTextNode(str(voiceNumber + toAdd))) type = getFirstChild(note,"type") if type: note.insertBefore(voice,type) # es gehört eigentlich vor type, falls das existiert else: note.insertBefore(voice,staff) # so klappt's gelegentlich auch. reportVoiceChanges += 1 changes = reportLyricChanges + reportVoiceChanges + reportBarlineChanges + reportForwardBackward + reportPrintChanges \ + reportMetronomesRemoved + reportAltersRemoved + reportFontFamilyTruncated if query: if changes == 0: messageBox(tr("title"),tr("noChange")) elif 0 == messageBox(tr("title"), str(reportLyricChanges) + tr("LyricsChanges")+", "+ str(reportVoiceChanges) + tr("VoiceChanges") + ", "+ str(reportBarlineChanges)+tr("BarlineChanges")+", "+ str(reportForwardBackward)+tr("ForwardBackward")+", "+ str(reportPrintChanges)+tr("PrintChanges")+", "+ str(reportMetronomesRemoved)+tr("Metronome")+", "+ str(reportAltersRemoved)+tr("Alter")+", "+ str(reportFontFamilyTruncated)+tr("FontFamily")+", "+ str(reportXOffsetChanged)+tr("XOffsetChanged")+tr("doitPrompt"), buttons=1): return else: print >> sys.stdout, str(reportLyricChanges) + tr("LyricsChanges")+tr("done") print >> sys.stdout, str(reportVoiceChanges) + tr("VoiceChanges")+tr("done") print >> sys.stdout, str(reportBarlineChanges)+tr("BarlineChanges")+tr("done") print >> sys.stdout, str(reportForwardBackward)+tr("ForwardBackward")+tr("done") print >> sys.stdout, str(reportPrintChanges)+tr("PrintChanges")+tr("done") print >> sys.stdout, str(reportMetronomesRemoved)+tr("Metronome")+tr("done") print >> sys.stdout, str(reportAltersRemoved)+tr("Alter")+tr("done") print >> sys.stdout, str(reportFontFamilyTruncated)+tr("FontFamily")+tr("done") print >> sys.stdout, str(reportXOffsetChanged)+tr("XOffsetChanged")+tr("done") if changes > 0: outStr = doc.toxml('utf-8') f = file(outFile, 'wt') f.write(outStr) inFile = "" outFile = "" doit=True try: if activeScore(): query = True fd = FileDialog(True) fd.addFilter(tr("filter"), "*.xml") fd.setDefaultExt("xml") fd.setStartPath(activeScore().pathName()) fd.setTitle(tr("open")) if fd.run(): inFile = fd.filePath() else: doit=False except: import tkMessageBox, tkFileDialog try: opts, args = getopt.getopt(sys.argv[1:], "h:f:o:", ["help", "filename=", "destination="]) except getopt.GetoptError: usage() if doit: if inFile == "": for f,a in opts: if f in ("-h", "--help"): print >> sys.stderr, __doc__ sys.exit() elif f == "-f": inFile = a elif f == "-o": outFile = a if inFile == "": inFile = tkFileDialog.askopenfilename(filetypes=[(tr("filter"), "xml")]) print >> sys.stdout, "inFile "+inFile if outFile == "": outFile = inFile[:-4] + "_mod.xml" patch(inFile, outFile)