option formatting, lilypond style.
* python/musicexp.py: grab from Ikebana: a library for composing
ly music expressions.
(Output_printer): class for advanced .ly printing.
(eg. tupletting)
* python/musicxml.py: new file. Read MusicXML MiniDOM tree, and
convert to pythonesque structure.
* Documentation/user/converters.itely (Invoking musicxml2ly): new node.
+2005-12-05 Han-Wen Nienhuys <hanwen@xs4all.nl>
+
+ * scripts/musicxml2ly.py (NonDentedHeadingFormatter.format_headi):
+ option formatting, lilypond style.
+
+ * python/musicexp.py: grab from Ikebana: a library for composing
+ ly music expressions.
+ (Output_printer): class for advanced .ly printing.
+ (eg. tupletting)
+
+ * python/musicxml.py: new file. Read MusicXML MiniDOM tree, and
+ convert to pythonesque structure.
+
+ * python/rational.py: PD rational number class.
+
+ * Documentation/user/converters.itely (Invoking musicxml2ly): new node.
+
2005-12-04 Erik Sandberg <mandolaerik@gmail.com>
* lily/part-combine-iterator.cc: Minor bugfix
@itemize @bullet
+@item A MusicXML importer is included now.
+
@item Texts set in a TrueType font are now kerned. This requires CVS
Pango or Pango 1.12.
The list of articulation scripts is incomplete. Empty measures
confuse @command{etf2ly}. Sequences of grace notes are ended improperly.
+@node Invoking musicxml2ly
+@section Invoking @code{musicxml2ly}
+
+@uref{http://@/www.@/recordarde@/.com/xml/,MusicXML} is a XML dialect
+for representing music notation.
+
+@command{musicxml2ly} extracts the notes from part-wise MusicXML
+files, and writes it to a .ly file.
+
+
+The following options are supported by @command{musicxml2ly}:
+
+
+@table @code
+@item -h,--help
+print usage and option summary.
+@item -o,--output=@var{file}
+set output filename to @var{file}. (default: print to stdout)
+@item -v,--version
+print version information.
+@end table
@node Invoking abc2ly
@section Invoking @code{abc2ly}
%{_bindir}/midi2ly
%{_bindir}/lilypond-book
%{_bindir}/mup2ly
+%{_bindir}/musicxml2ly
%{_bindir}/lilypond-invoke-editor
%doc THANKS
%endif
%{_mandir}/man1/abc2ly.1.gz
+%{_mandir}/man1/musicxml2ly.1.gz
%{_mandir}/man1/convert-ly.1.gz
%{_mandir}/man1/etf2ly.1.gz
%{_mandir}/man1/lilypond.1.gz
import inspect
import sys
import string
-from rational import Rational
-
-def flatten_list (fl):
- if type(fl) == type((1,)):
- return
-
- flattened = []
- for f in fl:
- flattened += flatten_list (fl)
-
-def is_derived (deriv_class, maybe_base):
- if deriv_class == maybe_base:
- return True
+import re
- for c in deriv_class.__bases__:
- if is_derived (c, maybe_base):
- return True
-
- return False
+from rational import Rational
+class Output_stack_element:
+ def __init__ (self):
+ self.factor = Rational (1)
+ def copy (self):
+ o = Output_stack_element()
+ o.factor = self.factor
+ return o
class Output_printer:
+
+ ## TODO: support for \relative.
+
def __init__ (self):
self.line = ''
- self.indent = 0
+ self.indent = 4
+ self.nesting = 0
self.file = sys.stdout
self.line_len = 72
+ self.output_state_stack = [Output_stack_element()]
+ self._skipspace = False
+ self.last_duration = None
+
+ def get_indent (self):
+ return self.nesting * self.indent
+
+ def override (self):
+ last = self.output_state_stack[-1]
+ self.output_state_stack.append (last.copy())
+ def add_factor (self, factor):
+ self.override()
+ self.output_state_stack[-1].factor *= factor
+
+ def revert (self):
+ del self.output_state_stack[-1]
+ if not self.output_state_stack:
+ raise 'empty'
+
+ def duration_factor (self):
+ return self.output_state_stack[-1].factor
+
+ def print_verbatim (self, str):
+ self.line += str
+
+ def print_duration_string (self, str):
+ if self.last_duration == str:
+ return
+
+ self.print_verbatim (str)
+
def add_word (self, str):
if (len (str) + 1 + len (self.line) > self.line_len):
self.newline()
+ self._skipspace = True
+
+ self.nesting += str.count ('<') + str.count ('{')
+ self.nesting -= str.count ('>') + str.count ('}')
- self.indent += str.count ('<') + str.count ('{')
- self.indent -= str.count ('>') + str.count ('}')
- self.line += ' ' + str
+ if not self._skipspace:
+ self.line += ' '
+ self.line += str
+ self._skipspace = False
def newline (self):
self.file.write (self.line + '\n')
- self.line = ' ' * self.indent
+ self.line = ' ' * self.indent * self.nesting
+ self._skipspace = True
+
+ def skipspace (self):
+ self._skipspace = True
+ def __call__(self, arg):
+ self.dump (arg)
+
def dump (self, str):
- words = string.split (str)
- for w in words:
- self.add_word (w)
-
+ if self._skipspace:
+ self._skipspace = False
+ self.print_verbatim (str)
+ else:
+ words = string.split (str)
+ for w in words:
+ self.add_word (w)
+
class Duration:
def __init__ (self):
- self.duration_log = 2
+ self.duration_log = 0
self.dots = 0
self.factor = Rational (1)
self.factor.numerator (),
self.factor.denominator ())
- def ly_expression (self):
+
+ def ly_expression (self, factor = None):
+ if not factor:
+ factor = self.factor
+
str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
- if self.factor <> Rational (1,1):
- str += '*%d/%d' % (self.factor.numerator (),self.factor.denominator ())
+ if factor <> Rational (1,1):
+ str += '*%d/%d' % (factor.numerator (), factor.denominator ())
return str
-
+
+ def print_ly (self, outputter):
+ if isinstance (outputter, Output_printer):
+ str = self.ly_expression (self.factor / outputter.duration_factor ())
+ outputter.print_duration_string (str)
+ else:
+ outputter (self.ly_expression ())
+
+ def __repr__(self):
+ return self.ly_expression()
+
def copy (self):
d = Duration ()
d.dots = self.dots
base = Rational (1, dur)
return base * dot_fact * self.factor
+
class Pitch:
def __init__ (self):
self.step = 0
self.octave = 0
+ def __repr__(self):
+ return self.ly_expression()
+
+ def transposed (self, interval):
+ c = self.copy ()
+ c.alteration += interval.alteration
+ c.step += interval.step
+ c.octave += interval.octave
+ c.normalize ()
+
+ target_st = self.semitones() + interval.semitones()
+ c.alteration += target_st - c.semitones()
+ return c
+
+ def normalize (c):
+ while c.step < 0:
+ c.step += 7
+ c.octave -= 1
+ c.octave += c.step / 7
+ c.step = c.step % 7
+
+
def lisp_expression (self):
return '(ly:make-pitch %d %d %d)' % (self.octave,
self.step,
return p
def steps (self):
- return self.step + self.octave * 7
+ return self.step + self.octave *7
+
+ def semitones (self):
+ return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
def ly_step_expression (self):
str = 'cdefgab'[self.step]
str += "," * (-self.octave - 1)
return str
-
+ def print_ly (self, outputter):
+ outputter (self.ly_expression())
+
class Music:
def __init__ (self):
- self.tag = None
self.parent = None
self.start = Rational (0)
pass
def get_length(self):
return Rational (0)
- def set_tag (self, counter, tag_dict):
- self.tag = counter
- tag_dict [counter] = self
- return counter + 1
def get_properties (self):
return ''
return self.parent.elements.index (self)
else:
return None
-
+ def name (self):
+ return self.__class__.__name__
+
def lisp_expression (self):
name = self.name()
- tag = ''
- if self.tag:
- tag = "'input-tag %d" % self.tag
props = self.get_properties ()
# props += 'start %f ' % self.start
- return "(make-music '%s %s %s)" % (name, tag, props)
+ return "(make-music '%s %s)" % (name, props)
def set_start (self, start):
self.start = start
def print_ly (self, printer):
printer (self.ly_expression ())
-
-class Music_document:
+
+
+class Comment (Music):
+ def __name__ (self):
+ self.text = ''
+ def print_ly (self, printer):
+ if isinstance (printer, Output_printer):
+ lines = string.split (self.text, '\n')
+ for l in lines:
+ if l:
+ printer.print_verbatim ('% ' + l)
+ printer.newline ()
+ else:
+ printer ('% ' + re.sub ('\n', '\n% ', self.text))
+ printer ('\n')
+
+
+
+class MusicWrapper (Music):
def __init__ (self):
- self.music = test_expr ()
- self.tag_dict = {}
- self.touched = True
-
- def recompute (self):
- self.tag_dict = {}
- self.music.set_tag (0, self.tag_dict)
- self.music.set_start (Rational (0))
-
+ Music.__init__(self)
+ self.element = None
+ def print_ly (self, func):
+ self.element.print_ly (func)
+
+class TimeScaledMusic (MusicWrapper):
+ def print_ly (self, func):
+ if isinstance(func, Output_printer):
+ func ('\\times %d/%d ' %
+ (self.numerator, self.denominator))
+ func.add_factor (Rational (self.numerator, self.denominator))
+ MusicWrapper.print_ly (self, func)
+ func.revert ()
+ else:
+ func (r'\times 1/1 ')
+ MusicWrapper.print_ly (self, func)
+
class NestedMusic(Music):
def __init__ (self):
Music.__init__ (self)
self.elements = []
def has_children (self):
return self.elements
- def set_tag (self, counter, dict):
- counter = Music.set_tag (self, counter, dict)
- for e in self.elements :
- counter = e.set_tag (counter, dict)
- return counter
+
def insert_around (self, succ, elt, dir):
assert elt.parent == None
return None
class SequentialMusic (NestedMusic):
- def name(self):
- return 'SequentialMusic'
-
def print_ly (self, printer):
printer ('{')
for e in self.elements:
def lisp_sub_expression (self, pred):
name = self.name()
- tag = ''
- if self.tag:
- tag = "'input-tag %d" % self.tag
props = self.get_subset_properties (pred)
- return "(make-music '%s %s %s)" % (name, tag, props)
+ return "(make-music '%s %s)" % (name, props)
def set_start (self, start):
for e in self.elements:
start += e.get_length()
class EventChord(NestedMusic):
- def name(self):
- return "EventChord"
-
def get_length (self):
l = Rational (0)
for e in self.elements:
def print_ly (self, printer):
note_events = [e for e in self.elements if
- is_derived (e.__class__, NoteEvent)]
+ isinstance (e, NoteEvent)]
+
rest_events = [e for e in self.elements if
- is_derived (e.__class__, RhythmicEvent)
- and not is_derived (e.__class__, NoteEvent)]
+ isinstance (e, RhythmicEvent)
+ and not isinstance (e, NoteEvent)]
other_events = [e for e in self.elements if
- not is_derived (e.__class__, RhythmicEvent)]
+ not isinstance (e, RhythmicEvent)]
if rest_events:
- printer (rest_events[0].ly_expression ())
+ rest_events[0].print_ly (printer)
elif len (note_events) == 1:
- printer (note_events[0].ly_expression ())
+ note_events[0].print_ly (printer)
elif note_events:
pitches = [x.pitch.ly_expression () for x in note_events]
- printer ('<%s>' % string.join (pitches)
- + note_events[0].duration.ly_expression ())
+ printer ('<%s>' % string.join (pitches))
+ note_events[0].duration.print_ly (printer)
else:
pass
+
# print 'huh', rest_events, note_events, other_events
-
- for e in other_events:
+ for e in other_events:
e.print_ly (printer)
class Event(Music):
- def __init__ (self):
- Music.__init__ (self)
+ pass
- def name (self):
- return "Event"
+class SpanEvent (Event):
+ def __init__(self):
+ Event.__init__ (self)
+ self.span_direction = 0
+ def get_properties(self):
+ return "'span-direction %d" % self.span_direction
+class SlurEvent (SpanEvent):
+ def ly_expression (self):
+ return {-1: '(',
+ 0:'',
+ 1:')'}[self.span_direction]
class ArpeggioEvent(Music):
- def name (self):
- return 'ArpeggioEvent'
-
def ly_expression (self):
return ('\\arpeggio')
return ("'duration %s"
% self.duration.lisp_expression ())
- def name (self):
- return 'RhythmicEvent'
-
class RestEvent (RhythmicEvent):
- def name (self):
- return 'RestEvent'
def ly_expression (self):
return 'r%s' % self.duration.ly_expression ()
+
+ def print_ly (self, printer):
+ printer('r')
+ if isinstance(printer, Output_printer):
+ printer.skipspace()
+ self.duration.print_ly (printer)
class SkipEvent (RhythmicEvent):
- def name (self):
- return 'SkipEvent'
def ly_expression (self):
return 's%s' % self.duration.ly_expression ()
RhythmicEvent.__init__ (self)
self.pitch = Pitch()
- def name (self):
- return 'NoteEvent'
-
def get_properties (self):
return ("'pitch %s\n 'duration %s"
% (self.pitch.lisp_expression (),
return '%s%s' % (self.pitch.ly_expression (),
self.duration.ly_expression ())
+ def print_ly (self, printer):
+ self.pitch.print_ly (printer)
+ self.duration.print_ly (printer)
-
-class KeySignatureEvent (Event):
- def __init__ (self, tonic, scale):
- Event.__init__ (self)
- self.scale = scale
- self.tonic = tonic
- def name (self):
- return 'KeySignatureEvent'
+class KeySignatureChange (Music):
+ def __init__ (self):
+ Music.__init__ (self)
+ self.scale = []
+ self.tonic = Pitch()
+ self.mode = 'major'
+
def ly_expression (self):
- return '\\key %s \\major' % self.tonic.ly_step_expression ()
+ return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
+ self.mode)
def lisp_expression (self):
pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
return """ (make-music 'KeyChangeEvent
'pitch-alist %s) """ % scale_str
-class ClefEvent (Event):
- def __init__ (self, t):
- Event.__init__ (self)
- self.type = t
+class TimeSignatureChange (Music):
+ def __init__ (self):
+ Music.__init__ (self)
+ self.fraction = (4,4)
+ def ly_expression (self):
+ return '\\time %d/%d ' % self.fraction
+
+class ClefChange (Music):
+ def __init__ (self):
+ Music.__init__ (self)
+ self.type = 'G'
- def name (self):
- return 'ClefEvent'
+
def ly_expression (self):
return '\\clef "%s"' % self.type
clef_dict = {
""" % (glyph, pos, c0)
return clefsetting
+
+def test_pitch ():
+ bflat = Pitch()
+ bflat.alteration = -1
+ bflat.step = 6
+ bflat.octave = -1
+ fifth = Pitch()
+ fifth.step = 4
+ down = Pitch ()
+ down.step = -4
+ down.normalize ()
+
+
+ print bflat.semitones()
+ print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
+ print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
+
+ print bflat.semitones(), 'down'
+ print bflat.transposed (down)
+ print bflat.transposed (down).transposed (down)
+ print bflat.transposed (down).transposed (down).transposed (down)
+
def test_expr ():
m = SequentialMusic()
l = 2
evc.insert_around (None, n, 0)
m.insert_around (None, evc, 0)
- evc = ClefEvent("G")
+ evc = ClefChange("G")
m.insert_around (None, evc, 0)
evc = EventChord()
if __name__ == '__main__':
+ test_pitch()
+ raise 1
expr = test_expr()
expr.set_start (Rational (0))
print expr.ly_expression()
self._data = None
self._original = None
self._name = 'xml_node'
+ self._parent = None
+
+ def is_first (self):
+ return self._parent.get_typed_children (self.__class__)[0] == self
def original (self):
return self._original
return ''.join ([c.get_text () for c in self._children])
def get_typed_children (self, klass):
- return [c for c in self._children if c.__class__ == klass]
+ 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])
def get_children (self, predicate):
return [c for c in self._children if predicate(c)]
def get_all_children (self):
return self._children
+ def get_maybe_exist_named_child (self, name):
+ return self.get_maybe_exist_typed_child (class_dict[name])
+
def get_maybe_exist_typed_child (self, klass):
cn = self.get_typed_children (klass)
if len (cn)==0:
Xml_node.__init__ (self)
self.duration = Rational (0)
self.start = Rational (0)
-
-class Attributes (Music_xml_node):
- def __init__ (self):
- self._dict = {}
- def set_attributes_from_previous (self, dict):
- self._dict.update (dict)
- def read_self (self):
- for c in self.get_all_children ():
- self._dict[c.get_name()] = c
- def get_named_attribute (self, name):
- return self._dict[name]
-
class Duration (Music_xml_node):
def get_length (self):
dur = string.atoi (self.get_text ()) * Rational (1,4)
return dur
class Hash_comment (Music_xml_node):
- def to_ly (self, output_func):
- output_func ('%% %s\n ' % self.get_text())
+ pass
class Pitch (Music_xml_node):
def get_step (self):
alter = string.atoi (ch.get_text ().strip ())
return alter
- def to_ly (self, output_func):
- oct = (self.get_octave () - 4)
- oct_str = ''
- if oct > 0:
- oct_str = "'" * oct
- elif oct < 0:
- oct_str = "," * -oct
-
- alt = self.get_alteration ()
- alt_str = ''
- if alt > 0:
- alt_str = 'is' * alt
- elif alt < 0:
- alt_str = 'es' * alt
+class Measure_element (Music_xml_node):
+ def get_voice_id (self):
+ voice_id = self.get_maybe_exist_named_child ('voice')
+ if voice_id:
+ return voice_id.get_text ()
+ else:
+ return None
- output_func ('%s%s%s' % (self.get_step ().lower(), alt_str, oct_str))
-
-class Note (Music_xml_node):
+ def is_first (self):
+ cn = self._parent.get_typed_children (self.__class__)
+ cn = [c for c in cn if c.get_voice_id () == self.get_voice_id ()]
+ return cn[0] == self
+
+class Attributes (Measure_element):
+ def __init__ (self):
+ Measure_element.__init__ (self)
+ self._dict = {}
+
+ def set_attributes_from_previous (self, dict):
+ self._dict.update (dict)
+ def read_self (self):
+ for c in self.get_all_children ():
+ self._dict[c.get_name()] = c
+
+ def get_named_attribute (self, name):
+ return self._dict[name]
+
+class Note (Measure_element):
def get_duration_log (self):
ch = self.get_maybe_exist_typed_child (class_dict[u'type'])
def get_pitches (self):
return self.get_typed_children (class_dict[u'pitch'])
- def to_ly (self, func):
- ps = self.get_pitches ()
-
- if len (ps) == 0:
- func ('r')
- else:
- func ('<')
- for p in ps:
- p.to_ly (func)
- func ('>')
-
- func ('%d ' % (1 << self.get_duration_log ()))
-
-
class Measure(Music_xml_node):
def get_notes (self):
return self.get_typed_children (class_dict[u'note'])
- def to_ly (self, func):
- func (' { % measure \n ')
- for c in self._children:
- c.to_ly (func)
- func (' } \n ')
class Part (Music_xml_node):
- def to_ly (self, func):
- func (' { %% part %s \n ' % self.name)
- for c in self._children:
- c.to_ly (func)
- func (' } \n ')
-
def interpret (self):
"""Set durations and starting points."""
measures = part.get_typed_children (Measure)
elements = []
for m in measures:
- elements.extend (m.get_typed_children (Note))
+ elements.extend (m.get_all_children ())
+ start_attr = None
for n in elements:
voice_id = n.get_maybe_exist_typed_child (class_dict['voice'])
- if not voice_id:
+ if not (voice_id or isinstance (n, Attributes)):
+ continue
+
+ if isinstance (n, Attributes) and not start_attr:
+ start_attr = n
+ continue
+
+ if isinstance (n, Attributes):
+ for v in voices.values ():
+ v.append (n)
continue
id = voice_id.get_text ()
voices[id] = []
voices[id].append (n)
-
+
+ if start_attr:
+ for (k,v) in voices.items ():
+ v.insert (0, start_attr)
+
part._voices = voices
def get_voices (self):
return self._voices
-
+
+class Notations (Music_xml_node):
+ def get_tuplet (self):
+ return self.get_maybe_exist_typed_child (Tuplet)
+ def get_slur (self):
+ slurs = self.get_typed_children (Slur)
+
+ if not slurs:
+ return None
+
+ if len (slurs) > 1:
+ print "More than one slur?!"
+
+ return slurs[0]
+
+class Time_modification(Music_xml_node):
+ def get_fraction (self):
+ b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
+ a = self.get_maybe_exist_typed_child (class_dict['normal-notes'])
+ return (string.atoi(a.get_text ()), string.atoi (b.get_text ()))
+
+
+
+class Tuplet(Music_xml_node):
+ pass
+class Slur (Music_xml_node):
+ pass
+
class Chord (Music_xml_node):
pass
class Dot (Music_xml_node):
pass
class_dict = {
+ 'notations': Notations,
+ 'time-modification': Time_modification,
'alter': Alter,
'grace': Grace,
'rest':Rest,
'part': Part,
'measure': Measure,
'type': Type,
+ 'slur': Slur,
+ 'tuplet': Tuplet,
'#comment': Hash_comment,
}
py_node = klass()
py_node._name = name
py_node._children = [demarshal_node (cn) for cn in node.childNodes]
+ for c in py_node._children:
+ c._parent = py_node
+
if node.attributes:
- for (name, value) in node.attributes.items():
- py_node.name = value
+
+ for (nm, value) in node.attributes.items():
+ py_node.__dict__[nm] = value
py_node._data = None
if node.nodeType == node.TEXT_NODE and node.data:
create_classes (names, class_dict)
return demarshal_node (node)
-
-def oldtest ():
- n = tree._children[-2]._children[-1]._children[0]
- print n
- print n.get_duration_log()
- print n.get_pitches()
- print n.get_pitches()[0].get_alteration()
-
def test_musicxml (tree):
m = tree._children[-2]
print m
- m.to_ly (lambda str: sys.stdout.write (str))
-
def read_musicxml (name):
tree = create_tree (name)
strip_white_space (tree)
tree = read_musicxml ('BeetAnGeSample.xml')
test_musicxml (tree)
+
depth = ..
-SEXECUTABLES=convert-ly lilypond-book abc2ly etf2ly mup2ly midi2ly lilypond-invoke-editor
+SEXECUTABLES=convert-ly lilypond-book abc2ly etf2ly mup2ly midi2ly lilypond-invoke-editor musicxml2ly
STEPMAKE_TEMPLATES=script help2man po
LOCALSTEPMAKE_TEMPLATES = lilypond
+#!@PYTHON@
+
+import optparse
import sys
import re
import os
+import string
+from gettext import gettext as _
datadir = '@local_lilypond_datadir@'
if not os.path.isdir (datadir):
sys.path.insert (0, os.path.join (datadir, 'python'))
+
import musicxml
import musicexp
from rational import Rational
if mxl_note.get_maybe_exist_typed_child (musicxml.Type):
d.duration_log = mxl_note.get_duration_log ()
else:
- d.factor = mxl_note._duration
d.duration_log = 0
d.dots = len (mxl_note.get_typed_children (musicxml.Dot))
d.factor = mxl_note._duration / d.get_length ()
return d
+
+span_event_dict = {
+ 'start': -1,
+ 'stop': 1
+}
+
+def group_tuplets (music_list, events):
+ indices = []
+
+ j = 0
+ for (ev_chord, tuplet_elt, fraction) in events:
+ while (j < len (music_list)):
+ if music_list[j]== ev_chord:
+ break
+ j += 1
+ if tuplet_elt.type == 'start':
+ indices.append ((j, None, fraction))
+ elif tuplet_elt.type == 'stop':
+ indices[-1] = (indices[-1][0], j, indices[-1][2])
+
+ new_list = []
+ last = 0
+ for (i1, i2, frac) in indices:
+ if i1 >= i2:
+ continue
+
+ new_list.extend (music_list[last:i1])
+ seq = musicexp.SequentialMusic ()
+ last = i2 + 1
+ seq.elements = music_list[i1:last]
+
+ tsm = musicexp.TimeScaledMusic ()
+ tsm.element = seq
+
+ tsm.numerator = frac[0]
+ tsm.denominator = frac[1]
+
+ new_list.append (tsm)
+
+ new_list.extend (music_list[last:])
+ return new_list
+
+def musicxml_clef_to_lily (mxl):
+ sign = mxl.get_maybe_exist_named_child ('sign')
+ change = musicexp.ClefChange ()
+ if sign:
+ change.type = sign.get_text ()
+ return change
+
+
+def musicxml_time_to_lily (mxl):
+ beats = mxl.get_maybe_exist_named_child ('beats')
+ type = mxl.get_maybe_exist_named_child ('beat-type')
+ change = musicexp.TimeSignatureChange()
+ change.fraction = (string.atoi(beats.get_text ()),
+ string.atoi(type.get_text ()))
+
+ return change
+
+def musicxml_key_to_lily (mxl):
+ mode = mxl.get_maybe_exist_named_child ('mode').get_text ()
+ fifths = string.atoi (mxl.get_maybe_exist_named_child ('fifths').get_text ())
+
+ fifth = musicexp.Pitch()
+ fifth.step = 4
+ if fifths < 0:
+ fifths *= -1
+ fifth.step *= -1
+ fifth.normalize ()
+
+ c = musicexp.Pitch()
+ for x in range (fifths):
+ c = c.transposed (fifth)
+
+ c.octave = 0
+
+ change = musicexp.KeySignatureChange()
+ change.mode = mode
+ change.tonic = c
+ return change
+
+def musicxml_attributes_to_lily (attrs):
+ elts = []
+ attr_dispatch = {
+ 'clef': musicxml_clef_to_lily,
+ 'time': musicxml_time_to_lily,
+ 'key': musicxml_key_to_lily
+ }
+ for (k, func) in attr_dispatch.items ():
+ childs = attrs.get_named_children (k)
+
+ ## ugh: you get clefs spread over staves for piano
+ if childs:
+ elts.append (func (childs[0]))
+
+ return elts
+
+def insert_measure_start_comments (ly_voice, indices):
+ idxs = indices[:]
+ idxs.reverse ()
+ for i in idxs:
+ c = musicexp.Comment()
+ c.text = ''
+ ly_voice.insert (i, c)
+
+ return ly_voice
def musicxml_voice_to_lily_voice (voice):
ly_voice = []
ly_now = Rational (0)
+
+ tuplet_events = []
+
+ measure_start_indices = []
for n in voice:
+ if n.is_first ():
+ measure_start_indices.append (len (ly_voice))
+
+ if isinstance (n, musicxml.Attributes):
+ ly_voice.extend (musicxml_attributes_to_lily (n))
+ continue
+
if not n.__class__.__name__ == 'Note':
- print 'not a note?'
+ print 'not a Note or Attributes?'
continue
-
+
+
pitch = None
duration = None
mxl_pitch = n.get_maybe_exist_typed_child (musicxml.Pitch)
event = None
-
+
+ notations = n.get_maybe_exist_typed_child (musicxml.Notations)
+ tuplet_event = None
+ slur_event = None
+ if notations:
+ tuplet_event = notations.get_tuplet ()
+ slur_event = notations.get_slur ()
+
if mxl_pitch:
pitch = musicxml_pitch_to_lily (mxl_pitch)
event = musicexp.NoteEvent()
if diff < Rational (0):
print 'huh: negative skip', n._when, ly_now, n._duration
diff = Rational (1,314159265)
-
skip = musicexp.SkipEvent()
skip.duration.duration_log = 0
ly_voice.append (musicexp.EventChord())
else:
pass
- #print 'append'
+
ev_chord = ly_voice[-1]
ev_chord.elements.append (event)
+ if tuplet_event:
+ mod = n.get_maybe_exist_typed_child (musicxml.Time_modification)
+ frac = (1,1)
+ if mod:
+ frac = mod.get_fraction ()
+
+ tuplet_events.append ((ev_chord, tuplet_event, frac))
+
+ if slur_event:
+ sp = musicexp.SlurEvent()
+ try:
+ sp.span_direction = span_event_dict[slur_event.type]
+ ev_chord.elements.append (sp)
+ except KeyError:
+ pass
+
+ ly_voice = insert_measure_start_comments (ly_voice, measure_start_indices)
+ ly_voice = group_tuplets (ly_voice, tuplet_events)
seq_music = musicexp.SequentialMusic()
-
seq_music.elements = ly_voice
return seq_music
for (id, voice) in voice_dict.items ():
m = musicxml_voice_to_lily_voice (voice)
- m_name = 'Part' + p.name + 'Voice' + id
+ m_name = 'Part' + p.id + 'Voice' + id
m_name = musicxml_id_to_lily (m_name)
all_voices[m_name] = m
return all_voices
-printer = musicexp.Output_printer()
+class NonDentedHeadingFormatter (optparse.IndentedHelpFormatter):
+ def format_heading(self, heading):
+ if heading:
+ return heading[0].upper() + heading[1:] + ':\n'
+ return ''
+ def format_option_strings(self, option):
+ sep = ' '
+ if option._short_opts and option._long_opts:
+ sep = ','
+
+ metavar = ''
+ if option.takes_value():
+ metavar = '=' + option.metavar or option.dest.upper()
+
+ return "%3s%s %s%s" % (" ".join (option._short_opts),
+ sep,
+ " ".join (option._long_opts),
+ metavar)
+
+ def format_usage(self, usage):
+ return _("Usage: %s\n") % usage
+
+ def format_description(self, description):
+ return description
+
+def option_parser ():
+ p = optparse.OptionParser(usage='musicxml2ly FILE.xml',
+ version = """%prog (LilyPond) @TOPLEVEL_VERSION@
-tree = musicxml.read_musicxml (sys.argv[1])
-parts = tree.get_typed_children (musicxml.Part)
+This program is free software. It is covered by the GNU General Public
+License and you are welcome to change it and/or distribute copies of it
+under certain conditions. Invoke as `lilypond --warranty' for more
+information.
-voices = get_all_voices (parts)
-for (k,v) in voices.items():
- print '%s = \n' % k
- v.print_ly (printer.dump)
- printer.newline()
+Copyright (c) 2005 by
+ Han-Wen Nienhuys <hanwen@xs4all.nl> and
+ Jan Nieuwenhuizen <janneke@gnu.org>
+""",
+
+ description =
+ """Convert MusicXML file to LilyPond input.
+"""
+ )
+ p.add_option ('-v', '--verbose',
+ action = "store_true",
+ dest = 'verbose',
+ help = 'be verbose')
+ p.add_option ('-o', '--output',
+ metavar = 'FILE',
+ action = "store",
+ default = None,
+ type = 'string',
+ dest = 'output',
+ help = 'set output file')
+
+ p.add_option_group ('', description = '''Report bugs via http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs
+''')
+ p.formatter = NonDentedHeadingFormatter ()
+ return p
+
+
+def convert (filename, output_name):
+ printer = musicexp.Output_printer()
+ tree = musicxml.read_musicxml (filename)
+ parts = tree.get_typed_children (musicxml.Part)
+
+ voices = get_all_voices (parts)
+
+
+ if output_name:
+ printer.file = open (output_name,'w')
+
+ for (k,v) in voices.items():
+ printer.dump ('%s = ' % k)
+ v.print_ly (printer)
+ printer.newline()
+
+ return voices
+
+
+opt_parser = option_parser()
+
+(options, args) = opt_parser.parse_args ()
+if options.version:
+ opt_parser.print_version()
+ sys.exit (0)
+if not args:
+ opt_parser.print_usage()
+ sys.exit (2)
+voices = convert (args[0], options.output)