Next round of patches: After Han-Wens comments about coding style (no
exceptions, spaces, etc.), I reworked all my patches so far. Here they are
all again:
-) 0001-Convert-articulations-like-fermata-staccato-tenuto.patch:
Convert articulations like fermata, staccato, tenuto, tremolo (only
single-note tremolo), accents, etc.
These entries in the xml are inside the <notation>...</notation> tags, listed
in the <ornaments>, <technical> and <articulations> tags.
-) 0002-Sorting-of-the-parts-in-the-.ly-output.-Currently-t.patch:
Sorting of the parts in the .ly output. Currently, their order was not first
staff, second staff, third, etc. but seemingly random
Basically, I use the part_list to sort the individual music voices in the
order as they appear in the score before printing them out.
-) 0003-Also-convert-0-in-part-ids-to-Zero-simplify-tha.patch:
Also convert '0' in part ids to 'Zero', simplify that code a bit.
-) 0004-Don-t-crash-when-a-score-does-not-have-an-explicit-k.patch:
Don't crash when a score does not have an explicit key or clef set (e.g.
Rosegarden produces such files).
-) 0005-Convert-dynamic-marks-given-in-a-direction-tag-a.patch:
Convert dynamic marks (given in a <direction> tag, assigned to the staff at a
given position in xml, not to a note like in lilypond)
In the LilyPondVoiceBuilder, I added a method to store any pending dynamics
and print them out only after the next note or rest (everything with
duration>0) is encountered.
Also convert (de-)crescendo (begin/end also given in a <direction> tag, not
assigned to a particular note)
Comment about broken dynamics, when they appear as first element of a part
before any note (so that no voice_id is known yet).
Cheers,
Reinhold
--
------------------------------------------------------------------
Reinhold Kainhofer, Vienna University of Technology, Austria
email: [EMAIL PROTECTED], http://reinhold.kainhofer.com/
* Financial and Actuarial Mathematics, TU Wien, http://www.fam.tuwien.ac.at/
* K Desktop Environment, http://www.kde.org, KOrganizer maintainer
* Chorvereinigung "Jung-Wien", http://www.jung-wien.at/
From ad228b1e645d8f8e0bf0116d784a76f73dbc0a67 Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <[EMAIL PROTECTED]>
Date: Sun, 19 Aug 2007 23:50:29 +0200
Subject: [PATCH] Convert articulations like fermata, staccato, tenuto, tremolo (only single-note tremolo), accents, etc.
These entries in the xml are inside the <notation>...</notation> tags, listed in the <ornaments>, <technical> and <articulations> tags.
---
python/musicexp.py | 29 ++++++++++-
python/musicxml.py | 36 +++++++++++++
scripts/musicxml2ly.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 200 insertions(+), 1 deletions(-)
diff --git a/python/musicexp.py b/python/musicexp.py
index e7b3870..56252f3 100644
--- a/python/musicexp.py
+++ b/python/musicexp.py
@@ -498,7 +498,34 @@ class TieEvent(Event):
def ly_expression (self):
return '~'
-
+
+class ArticulationEvent (Event):
+ def __init__ (self):
+ self.type = None
+ self.force_direction = None
+
+ def direction_mod (self):
+ dirstr = { 1: '^', -1: '_', 0: '-' }.get (self.force_direction)
+ if dirstr:
+ return dirstr
+ else:
+ return ''
+
+ def ly_expression (self):
+ return '%s\\%s' % (self.direction_mod (), self.type)
+
+
+class TremoloEvent (Event):
+ def __init__ (self):
+ self.bars = 0;
+
+ def ly_expression (self):
+ str=''
+ if self.bars > 0:
+ str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
+ return str
+
+
class RhythmicEvent(Event):
def __init__ (self):
Event.__init__ (self)
diff --git a/python/musicxml.py b/python/musicxml.py
index 3466473..304b2c6 100644
--- a/python/musicxml.py
+++ b/python/musicxml.py
@@ -450,6 +450,32 @@ class Staff (Music_xml_node):
class Instrument (Music_xml_node):
pass
+class Fermata (Music_xml_node):
+ pass
+class Dynamics (Music_xml_node):
+ pass
+class Articulations (Music_xml_node):
+ pass
+class Accent (Music_xml_node):
+ pass
+class Staccato (Music_xml_node):
+ pass
+class Tenuto (Music_xml_node):
+ pass
+class Tremolo (Music_xml_node):
+ pass
+class Technical (Music_xml_node):
+ pass
+class Ornaments (Music_xml_node):
+ pass
+
+
+class Direction (Music_xml_node):
+ pass
+class DirType (Music_xml_node):
+ pass
+
+
## need this, not all classes are instantiated
## for every input file.
class_dict = {
@@ -477,6 +503,16 @@ class_dict = {
'type': Type,
'part-list': Part_list,
'staff': Staff,
+ 'fermata': Fermata,
+ 'articulations': Articulations,
+ 'accent': Accent,
+ 'staccato': Staccato,
+ 'tenuto': Tenuto,
+ 'tremolo': Tremolo,
+ 'technical': Technical,
+ 'ornaments': Ornaments,
+ 'direction': Direction,
+ 'direction-type': DirType
}
def name2class_name (name):
diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py
index e358268..3a5f465 100644
--- a/scripts/musicxml2ly.py
+++ b/scripts/musicxml2ly.py
@@ -168,6 +168,99 @@ def musicxml_spanner_to_lily_event (mxl_event):
return ev
+def musicxml_direction_to_indicator (direction):
+ val = { "above": 1, "upright": 1, "below": -1, "downright": -1 }.get (direction)
+ if val:
+ return val
+ else:
+ return ''
+
+def musicxml_fermata_to_lily_event (mxl_event):
+ ev = musicexp.ArticulationEvent ()
+ ev.type = "fermata"
+ if hasattr (mxl_event, 'type'):
+ dir = musicxml_direction_to_indicator (mxl_event.type)
+ if dir:
+ ev.force_direction = dir
+ return ev
+
+def musicxml_tremolo_to_lily_event(mxl_event):
+ if mxl_event.get_name () != "tremolo":
+ return
+ ev = musicexp.TremoloEvent ()
+ ev.bars = mxl_event.get_text ()
+ return ev
+
+# TODO: Some translations are missing!
+articulations_dict = {
+ ##### ORNAMENTS
+ "trill-mark": "trill",
+ "turn": "turn",
+ #"delayed-turn": "?",
+ "inverted-turn": "reverseturn",
+ #"shake": "?",
+ #"wavy-line": "?",
+ "mordent": "mordent",
+ #"inverted-mordent": "?",
+ #"schleifer": "?"
+ ##### TECHNICALS
+ "up-bow": "upbow",
+ "down-bow": "downbow",
+ #"harmonic": "",
+ #"open-string": "",
+ #"thumb-position": "",
+ #"fingering": "",
+ #"pluck": "",
+ #"double-tongue": "",
+ #"triple-tongue": "",
+ #"stopped": "",
+ #"snap-pizzicato": "",
+ #"fret": "",
+ #"string": "",
+ #"hammer-on": "",
+ #"pull-off": "",
+ #"bend": "",
+ #"tap": "",
+ #"heel": "",
+ #"toe": "",
+ #"fingernails": ""
+ ##### ARTICULATIONS
+ "accent": "accent",
+ "strong-accent": "marcato",
+ "staccato": "staccato",
+ "tenuto": "tenuto",
+ #"detached-legato": "",
+ "staccatissimo": "staccatissimo",
+ #"spiccato": "",
+ #"scoop": "",
+ #"plop": "",
+ #"doit": "",
+ #"falloff": "",
+ "breath-mark": "breathe",
+ #"caesura": "caesura",
+ #"stress": "",
+ #"unstress": ""
+}
+
+def musicxml_articulation_to_lily_event(mxl_event):
+ ev = musicexp.ArticulationEvent ()
+ tp = articulations_dict.get (mxl_event.get_name ())
+ if not tp:
+ return
+
+ ev.type = tp
+
+ # Some articulations use the type attribute, other the placement...
+ dir = None
+ if hasattr (mxl_event, 'type'):
+ dir = musicxml_direction_to_indicator (mxl_event.type)
+ if hasattr (mxl_event, 'placement'):
+ dir = musicxml_direction_to_indicator (mxl_event.placement)
+ if dir:
+ ev.force_direction = dir
+ return ev
+
+
instrument_drumtype_dict = {
'Acoustic Snare Drum': 'acousticsnare',
'Side Stick': 'sidestick',
@@ -353,6 +446,12 @@ def musicxml_voice_to_lily_voice (voice):
notations = n.get_maybe_exist_typed_child (musicxml.Notations)
tuplet_event = None
span_events = []
+
+ # The <notation> element can have the following children (+ means implemented, ~ partially, - not):
+ # +tied | +slur | +tuplet | glissando | slide |
+ # ornaments | technical | articulations | dynamics |
+ # +fermata | arpeggiate | non-arpeggiate |
+ # accidental-mark | other-notation
if notations:
if notations.get_tuplet():
tuplet_event = notations.get_tuplet()
@@ -375,6 +474,43 @@ def musicxml_voice_to_lily_voice (voice):
mxl_tie = notations.get_tie ()
if mxl_tie and mxl_tie.type == 'start':
ev_chord.append (musicexp.TieEvent ())
+
+ fermatas = notations.get_named_children ('fermata')
+ for a in fermatas:
+ ev = musicxml_fermata_to_lily_event (a);
+ if ev:
+ ev_chord.append (ev)
+
+ # Articulations can contain the following child elements:
+ # accent | strong-accent | staccato | tenuto |
+ # detached-legato | staccatissimo | spiccato |
+ # scoop | plop | doit | falloff | breath-mark |
+ # caesura | stress | unstress
+ # Technical can contain the following child elements:
+ # up-bow | down-bow | harmonic | open-string |
+ # thumb-position | fingering | pluck | double-tongue |
+ # triple-tongue | stopped | snap-pizzicato | fret |
+ # string | hammer-on | pull-off | bend | tap | heel |
+ # toe | fingernails | other-technical
+ # Ornaments can contain the following child elements:
+ # trill-mark | turn | delayed-turn | inverted-turn |
+ # shake | wavy-line | mordent | inverted-mordent |
+ # schleifer | tremolo | other-ornament, accidental-mark
+ ornaments = notations.get_named_children ('ornaments')
+ for a in ornaments:
+ for ch in a.get_named_children ('tremolo'):
+ ev = musicxml_tremolo_to_lily_event (ch)
+ if ev:
+ ev_chord.append (ev)
+
+ ornaments += notations.get_named_children ('articulations')
+ ornaments += notations.get_named_children ('technical')
+
+ for a in ornaments:
+ for ch in a.get_all_children ():
+ ev = musicxml_articulation_to_lily_event (ch)
+ if ev:
+ ev_chord.append (ev)
mxl_beams = [b for b in n.get_named_children ('beam')
if (b.get_type () in ('begin', 'end')
--
1.5.2.3
From a09121d886f75c61bdd6c4544f0b3b1215988777 Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <[EMAIL PROTECTED]>
Date: Sun, 19 Aug 2007 23:51:51 +0200
Subject: [PATCH] Sorting of the parts in the .ly output. Currently, their order was not first staff, second staff, third, etc. but seemingly random
Basically, I use the part_list to sort the individual music voices in the order as they appear in the score before printing them out.
---
scripts/musicxml2ly.py | 10 +++++++---
1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py
index 3a5f465..7307dd2 100644
--- a/scripts/musicxml2ly.py
+++ b/scripts/musicxml2ly.py
@@ -648,9 +648,13 @@ def music_xml_voice_name_to_lily_name (part, name):
str = "Part%sVoice%s" % (part.id, name)
return musicxml_id_to_lily (str)
-def print_voice_definitions (printer, voices):
+def print_voice_definitions (printer, part_list, voices):
+ part_dict={}
for (part, nv_dict) in voices.items():
-
+ part_dict[part.id] = (part, nv_dict)
+
+ for part in part_list:
+ (part, nv_dict) = part_dict[part.id]
for (name, (voice, mxlvoice)) in nv_dict.items ():
k = music_xml_voice_name_to_lily_name (part, name)
printer.dump ('%s = ' % k)
@@ -763,7 +767,7 @@ def convert (filename, options):
printer.set_file (open (defs_ly_name, 'w'))
print_ly_preamble (printer, filename)
- print_voice_definitions (printer, voices)
+ print_voice_definitions (printer, part_list, voices)
printer.close ()
--
1.5.2.3
From 0ab607a65fefe22209037fa7a7449a0c75d2c6d5 Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <[EMAIL PROTECTED]>
Date: Mon, 20 Aug 2007 00:21:30 +0200
Subject: [PATCH] Convert dynamic marks (given in a <direction> tag, assigned to the staff at a given position in xml, not to a note like in lilypond)
In the LilyPondVoiceBuilder, I added a method to store any pending dynamics and print them out only after the next note or rest (everything with duration>0) is encountered.
Also convert (de-)crescendo (begin/end also given in a <direction> tag, not assigned to a particular note)
Comment about broken dynamics, when they appear as first element of a part before any note (so that no voice_id is known yet).
---
python/musicexp.py | 44 +++++++++++++++++++++++++++++++++++++
python/musicxml.py | 12 +++++++--
scripts/musicxml2ly.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 108 insertions(+), 5 deletions(-)
diff --git a/python/musicexp.py b/python/musicexp.py
index 56252f3..b543dbc 100644
--- a/python/musicexp.py
+++ b/python/musicexp.py
@@ -499,6 +499,50 @@ class TieEvent(Event):
return '~'
+class HairpinEvent (Event):
+ def __init__ (self, type):
+ self.type = type
+ def hairpin_to_ly (self):
+ val = ''
+ tp = { 0: '\!', 1: '\<', -1: '\>' }.get (self.type)
+ if tp:
+ val += tp
+ return val
+
+ def ly_expression (self):
+ return self.hairpin_to_ly ()
+
+ def print_ly (self, printer):
+ val = self.hairpin_to_ly ()
+ if val:
+ printer.dump (val)
+
+
+
+class DynamicsEvent (Event):
+ def __init__ (self):
+ self.type = None
+ self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
+ "mp", "mf",
+ "f", "ff", "fff", "ffff",
+ "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
+ def ly_expression (self):
+ if self.type == None:
+ return;
+ elif self.type in self.available_commands:
+ return '\%s' % self.type
+ else:
+ return '\markup{ \dynamic %s }' % self.type
+
+ def print_ly (self, printer):
+ if self.type == None:
+ return
+ elif self.type in self.available_commands:
+ printer.dump ("\\%s" % self.type)
+ else:
+ printer.dump ("\\markup{ \\dynamic %s }" % self.type)
+
+
class ArticulationEvent (Event):
def __init__ (self):
self.type = None
diff --git a/python/musicxml.py b/python/musicxml.py
index e7fe77d..132ce5a 100644
--- a/python/musicxml.py
+++ b/python/musicxml.py
@@ -357,14 +357,16 @@ class Part (Music_xml_node):
for n in elements:
voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
- if not (voice_id or isinstance (n, Attributes)):
+ # TODO: If the first element of a voice is a dynamics entry,
+ # then voice_id is not yet set! Thus it will currently be ignored
+ if not (voice_id or isinstance (n, Attributes) or isinstance (n, Direction) ):
continue
if isinstance (n, Attributes) and not start_attr:
start_attr = n
continue
- if isinstance (n, Attributes):
+ if isinstance (n, Attributes) or isinstance (n, Direction):
for v in voices.values ():
v.add_element (n)
continue
@@ -477,6 +479,8 @@ class Direction (Music_xml_node):
pass
class DirType (Music_xml_node):
pass
+class Wedge (Music_xml_node):
+ pass
## need this, not all classes are instantiated
@@ -515,7 +519,9 @@ class_dict = {
'technical': Technical,
'ornaments': Ornaments,
'direction': Direction,
- 'direction-type': DirType
+ 'direction-type': DirType,
+ 'dynamics': Dynamics,
+ 'wedge': Wedge
}
def name2class_name (name):
diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py
index 4bb3081..9a09fa2 100644
--- a/scripts/musicxml2ly.py
+++ b/scripts/musicxml2ly.py
@@ -261,13 +261,43 @@ def musicxml_articulation_to_lily_event(mxl_event):
return ev
+def musicxml_direction_to_lily( n ):
+ # TODO: Handle the <staff> element!
+ res = []
+ dirtype = n.get_maybe_exist_typed_child (musicxml.DirType)
+ if not dirtype:
+ return res
+
+ for entry in dirtype.get_all_children ():
+ if entry.get_name () == "dynamics":
+ for dynentry in entry.get_all_children ():
+ dynamics_available = ( "p", "pp", "ppp", "pppp", "ppppp", "pppppp",
+ "f", "ff", "fff", "ffff", "fffff", "ffffff",
+ "mp", "mf", "sf", "sfp", "sfpp", "fp",
+ "rf", "rfz", "sfz", "sffz", "fz" )
+ if not dynentry.get_name() in dynamics_available:
+ continue
+ event = musicexp.DynamicsEvent ()
+ event.type = dynentry.get_name ()
+ res.append (event)
+
+ if entry.get_name() == "wedge":
+ if hasattr (entry, 'type'):
+ wedgetype = entry.type;
+ wedgetypeval = {"crescendo" : 1, "decrescendo" : -1,
+ "diminuendo" : -1, "stop" : 0 }.get (wedgetype)
+ if wedgetypeval != None:
+ event = musicexp.HairpinEvent (wedgetypeval)
+ res.append (event)
+
+ return res
+
instrument_drumtype_dict = {
'Acoustic Snare Drum': 'acousticsnare',
'Side Stick': 'sidestick',
'Open Triangle': 'opentriangle',
'Mute Triangle': 'mutetriangle',
- 'Tambourine': 'tambourine',
-
+ 'Tambourine': 'tambourine'
}
def musicxml_note_to_lily_main_event (n):
@@ -312,6 +342,7 @@ class NegativeSkip:
class LilyPondVoiceBuilder:
def __init__ (self):
self.elements = []
+ self.pending_dynamics = []
self.end_moment = Rational (0)
self.begin_moment = Rational (0)
self.pending_multibar = Rational (0)
@@ -339,6 +370,16 @@ class LilyPondVoiceBuilder:
self.begin_moment = self.end_moment
self.end_moment = self.begin_moment + duration
+ # Insert all pending dynamics right after the note/rest:
+ if duration > Rational (0):
+ for d in self.pending_dynamics:
+ self.elements.append (d)
+ self.pending_dynamics = []
+
+ def add_dynamics (self, dynamic):
+ # store the dynamic item(s) until we encounter the next note/rest:
+ self.pending_dynamics.append (dynamic)
+
def add_bar_check (self, number):
b = musicexp.BarCheck ()
b.bar_number = number
@@ -390,6 +431,11 @@ def musicxml_voice_to_lily_voice (voice):
if n.get_name () == 'forward':
continue
+ if isinstance (n, musicxml.Direction):
+ for a in musicxml_direction_to_lily (n):
+ voice_builder.add_dynamics (a)
+ continue
+
if not n.get_maybe_exist_named_child ('chord'):
try:
voice_builder.jumpto (n._when)
@@ -512,6 +558,13 @@ def musicxml_voice_to_lily_voice (voice):
if ev:
ev_chord.append (ev)
+ dynamics = notations.get_named_children ('dynamics')
+ for a in dynamics:
+ for ch in a.get_all_children ():
+ ev = musicxml_dynamics_to_lily_event (ch)
+ if ev:
+ ev_chord.append (ev)
+
mxl_beams = [b for b in n.get_named_children ('beam')
if (b.get_type () in ('begin', 'end')
and b.is_primary ())]
--
1.5.2.3
From 39ded4e618d27ff62eedea7fa69f2958c06ea8e9 Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <[EMAIL PROTECTED]>
Date: Sun, 19 Aug 2007 23:53:26 +0200
Subject: [PATCH] Also convert '0' in part ids to 'Zero', simplify that code a bit.
---
scripts/musicxml2ly.py | 7 +++----
1 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py
index 7307dd2..a82f3dd 100644
--- a/scripts/musicxml2ly.py
+++ b/scripts/musicxml2ly.py
@@ -558,12 +558,11 @@ def musicxml_voice_to_lily_voice (voice):
def musicxml_id_to_lily (id):
- digits = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
- 'nine', 'ten']
+ digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight',
+ 'Nine', 'Ten']
for dig in digits:
- d = digits.index (dig) + 1
- dig = dig[0].upper() + dig[1:]
+ d = digits.index (dig)
id = re.sub ('%d' % d, dig, id)
id = re.sub ('[^a-zA-Z]', 'X', id)
--
1.5.2.3
From 7c9b19bb8d3e4a83cb5e11d9155d014d0a6e583f Mon Sep 17 00:00:00 2001
From: Reinhold Kainhofer <[EMAIL PROTECTED]>
Date: Sun, 19 Aug 2007 23:58:27 +0200
Subject: [PATCH] Don't crash when a score does not have an explicit key or clef set (e.g. Rosegarden produces such files).
---
python/musicxml.py | 7 +++++--
scripts/musicxml2ly.py | 4 ++--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/python/musicxml.py b/python/musicxml.py
index 304b2c6..e7fe77d 100644
--- a/python/musicxml.py
+++ b/python/musicxml.py
@@ -39,10 +39,13 @@ class Xml_node:
p = p.get_parent ()
def get_typed_children (self, klass):
- return [c for c in self._children if isinstance(c, klass)]
+ if not klass:
+ return []
+ else:
+ return [c for c in self._children if isinstance(c, klass)]
def get_named_children (self, nm):
- return self.get_typed_children (class_dict[nm])
+ return self.get_typed_children (class_dict.get (nm))
def get_named_child (self, nm):
return self.get_maybe_exist_named_child (nm)
diff --git a/scripts/musicxml2ly.py b/scripts/musicxml2ly.py
index a82f3dd..4bb3081 100644
--- a/scripts/musicxml2ly.py
+++ b/scripts/musicxml2ly.py
@@ -131,10 +131,10 @@ def musicxml_attributes_to_lily (attrs):
'key': musicxml_key_to_lily
}
for (k, func) in attr_dispatch.items ():
- childs = attrs.get_named_children (k)
+ children = attrs.get_named_children (k)
## ugh: you get clefs spread over staves for piano
- if childs:
+ if children:
elts.append (func (attrs))
return elts
--
1.5.2.3
_______________________________________________
lilypond-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/lilypond-devel