From 664becdedd4d52c72217c241ec18fe4bf913285d Mon Sep 17 00:00:00 2001 From: Reinhold Kainhofer Date: Sat, 1 Sep 2007 14:36:38 +0200 Subject: [PATCH] MusicXML: Convert all clefs, escape " in header fields, code simplification -) Extend the ClefChange class to convert all different clefs (G,C,F, percussion,tab) to the corresponding lilypond clef names. MusicXML also allows a "None" key, which lilypond does not :-( -) In the header fields and the lyrics, escape all quotes by \". Only wrap the whole string in quotes if it is necessary (string contains numbers, spaces, quotes, or starts with a period or comma). This makes it much easier to edit the lyrics later on. -) Several coding style issued pointed out by Han-Wen --- python/musicexp.py | 29 ++++++++++++++++++++++--- python/musicxml.py | 34 +++++++++++++++++++++++------- scripts/musicxml2ly.py | 53 ++++++++++++++++++++--------------------------- 3 files changed, 74 insertions(+), 42 deletions(-) diff --git a/python/musicexp.py b/python/musicexp.py index 6d7bc94..cf29aa6 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -5,6 +5,7 @@ import re from rational import Rational + class Output_stack_element: def __init__ (self): self.factor = Rational (1) @@ -763,10 +764,27 @@ class ClefChange (Music): def __init__ (self): Music.__init__ (self) self.type = 'G' - - + self.position = 2 + self.octave = 0 + + def octave_modifier (self): + return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '') + def clef_name (self): + return {('G', 2): "treble", + ('G', 1): "french", + ('C', 1): "soprano", + ('C', 2): "mezzosoprano", + ('C', 3): "alto", + ('C', 4): "tenor", + ('C', 5): "baritone", + ('F', 3): "varbaritone", + ('F', 4): "bass", + ('F', 5): "subbass", + ("percussion", 2): "percussion", + ("TAB", 5): "tab"}.get ((self.type, self.position), None) def ly_expression (self): - return '\\clef "%s"' % self.type + return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ()) + clef_dict = { "G": ("clefs.G", -2, -6), "C": ("clefs.C", 0, 0), @@ -774,7 +792,10 @@ class ClefChange (Music): } def lisp_expression (self): - (glyph, pos, c0) = self.clef_dict.get (self.type) + try: + (glyph, pos, c0) = self.clef_dict[self.type] + except KeyError: + return "" clefsetting = """ (make-music 'SequentialMusic 'elements (list diff --git a/python/musicxml.py b/python/musicxml.py index fe8dd60..a24cb53 100644 --- a/python/musicxml.py +++ b/python/musicxml.py @@ -1,6 +1,16 @@ import new import string from rational import * +import re + +def escape_ly_output_string (input_string): + return_string = input_string + needs_quotes = re.search ("[0-9\" ]", return_string) or re.search ("^[,.]", return_string); + return_string = string.replace (return_string, "\"", "\\\"") + if needs_quotes: + return_string = "\"" + return_string + "\"" + return return_string + class Xml_node: def __init__ (self): @@ -108,7 +118,7 @@ class Identification (Xml_node): def get_creator (self, type): creators = self.get_named_children ('creator') - # return the first creator tag that has type 'editor' + # return the first creator tag that has the particular type for i in creators: if hasattr (i, 'type') and i.type == type: return i.get_text () @@ -223,14 +233,22 @@ class Attributes (Measure_element): print 'error: requested time signature, but time sig unknown' return (4, 4) - def get_clef_sign (self): + # returns clef information in the form ("cleftype", position, octave-shift) + def get_clef_information (self): + clefinfo = ['G', 2, 0] mxl = self.get_named_attribute ('clef') + if not mxl: + return clefinfo sign = mxl.get_maybe_exist_named_child ('sign') if sign: - return sign.get_text () - else: - print 'clef requested, but unknow' - return 'G' + clefinfo[0] = sign.get_text() + line = mxl.get_maybe_exist_named_child ('line') + if line: + clefinfo[1] = string.atoi (line.get_text ()) + octave = mxl.get_maybe_exist_named_child ('clef-octave-change') + if octave: + clefinfo[2] = string.atoi (octave.get_text ()) + return clefinfo def get_key_signature (self): "return (fifths, mode) tuple" @@ -336,11 +354,11 @@ class Lyric (Music_xml_node): elif text == "_" and continued: return "__" elif continued and text: - return "\"" + text + "\" --" + return escape_ly_output_string (text) + " --" elif continued: return "--" elif text: - return "\"" + text + "\"" + return escape_ly_output_string (text) else: return "" diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py index 46f3da3..fbc7525 100644 --- a/scripts/musicxml2ly.py +++ b/scripts/musicxml2ly.py @@ -28,49 +28,42 @@ def progress (str): # extract those into a hash, indexed by proper lilypond header attributes def extract_score_information (tree): score_information = {} + def set_if_exists (field, value): + if value: + score_information[field] = value + work = tree.get_maybe_exist_named_child ('work') if work: - if work.get_work_title (): - score_information['title'] = work.get_work_title () - if work.get_work_number (): - score_information['worknumber'] = work.get_work_number () - if work.get_opus (): - score_information['opus'] = work.get_opus () + set_if_exists ('title', work.get_work_title ()) + set_if_exists ('worknumber', work.get_work_number ()) + set_if_exists ('opus', work.get_opus ()) else: movement_title = tree.get_maybe_exist_named_child ('movement-title') if movement_title: - score_information['title'] = movement_title.get_text () + set_if_exists ('title', movement_title.get_text ()) identifications = tree.get_named_children ('identification') for ids in identifications: - if ids.get_rights (): - score_information['copyright'] = ids.get_rights () - if ids.get_composer (): - score_information['composer'] = ids.get_composer () - if ids.get_arranger (): - score_information['arranger'] = ids.get_arranger () - if ids.get_editor (): - score_information['editor'] = ids.get_editor () - if ids.get_poet (): - score_information['poet'] = ids.get_poet () + set_if_exists ('copyright', ids.get_rights ()) + set_if_exists ('composer', ids.get_composer ()) + set_if_exists ('arranger', ids.get_arranger ()) + set_if_exists ('editor', ids.get_editor ()) + set_if_exists ('poet', ids.get_poet ()) - if ids.get_encoding_software (): - score_information['tagline'] = ids.get_encoding_software () - score_information['encodingsoftware'] = ids.get_encoding_software () - if ids.get_encoding_date (): - score_information['encodingdate'] = ids.get_encoding_date () - if ids.get_encoding_person (): - score_information['encoder'] = ids.get_encoding_person () - if ids.get_encoding_description (): - score_information['encodingdescription'] = ids.get_encoding_description () + set_if_exists ('tagline', ids.get_encoding_software ()) + set_if_exists ('encodingsoftware', ids.get_encoding_software ()) + set_if_exists ('encodingdate', ids.get_encoding_date ()) + set_if_exists ('encoder', ids.get_encoding_person ()) + set_if_exists ('encodingdescription', ids.get_encoding_description ()) return score_information + def print_ly_information (printer, score_information): printer.dump ('\header {') printer.newline () - for k in score_information.keys (): - printer.dump ('%s = "%s"' % (k, score_information[k])) + for (k, text) in score_information.items (): + printer.dump ('%s = %s' % (k, musicxml.escape_ly_output_string (text))) printer.newline () printer.dump ('}') printer.newline () @@ -135,7 +128,7 @@ def group_tuplets (music_list, events): def musicxml_clef_to_lily (attributes): change = musicexp.ClefChange () - change.type = attributes.get_clef_sign () + (change.type, change.position, change.octave) = attributes.get_clef_information () return change def musicxml_time_to_lily (attributes): @@ -356,7 +349,7 @@ def musicxml_articulation_to_lily_event (mxl_event): dir = musicxml_direction_to_indicator (mxl_event.type) if hasattr (mxl_event, 'placement'): dir = musicxml_direction_to_indicator (mxl_event.placement) - # \breathe cannot have any direction modifyer (^, _, -)! + # \breathe cannot have any direction modifier (^, _, -)! if dir and tp != "breathe": ev.force_direction = dir return ev -- 1.5.2.3