]> git.donarmstrong.com Git - lilypond.git/commitdiff
* scripts/musicxml2ly.py (progress): new function
authorHan-Wen Nienhuys <hanwen@xs4all.nl>
Wed, 7 Dec 2005 12:38:18 +0000 (12:38 +0000)
committerHan-Wen Nienhuys <hanwen@xs4all.nl>
Wed, 7 Dec 2005 12:38:18 +0000 (12:38 +0000)
(musicxml_key_to_lily): don't barf on modeless keys.
(create_skip_music): new function.
(musicxml_spanner_to_lily_event): new function. Handle beams too.
(musicxml_note_to_lily_main_event): new function.

* python/musicexp.py (Music.__init__): add comment field.
(NestedMusic.append): new routine.
(SequentialMusic.print_ly): print comment.
(ArpeggioEvent.ly_expression): new class
(BeamEvent.ly_expression): new class
(NoteEvent.__init__): support for cautionary/forced accs.

* lily/lookup.cc (slur): normal order for array loop.

ChangeLog
THANKS
lily/lookup.cc
python/musicexp.py
python/musicxml.py
scripts/musicxml2ly.py

index 984add0693e1b73a4434ecb36cf2cb03fa9642e6..82d1a7ecb7c10e440de7aca58f9505a56740c0d3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
 2005-12-07  Han-Wen Nienhuys  <hanwen@xs4all.nl>
 
+       * scripts/musicxml2ly.py (progress): new function
+       (musicxml_key_to_lily): don't barf on modeless keys.
+       (create_skip_music): new function.
+       (musicxml_spanner_to_lily_event): new function. Handle beams too.
+       (musicxml_note_to_lily_main_event): new function.
+
+       * python/musicexp.py (Music.__init__): add comment field.
+       (NestedMusic.append): new routine.
+       (SequentialMusic.print_ly): print comment.
+       (ArpeggioEvent.ly_expression): new class
+       (BeamEvent.ly_expression): new class
+       (NoteEvent.__init__): support for cautionary/forced accs.
+
+       * lily/lookup.cc (slur): normal order for array loop.
+
+       * scm/framework-ps.scm (dump-stencil-as-EPS): set left X of bbox
+       to 0.0.
+
        * ly/engraver-init.ly: set bar-size, so bar-lines aren't
        collapsed.
 
diff --git a/THANKS b/THANKS
index d99c42c56e35b168cec08caab8615886262bba26..6bbc21da9f98766728453d58a9be3d363d215100 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -24,6 +24,7 @@ Nicolas Sceaux
 SPONSORS
 
 Aaron Mehl
+Christian Ebert
 Henrik Frisk
 Jay Hamilton
 Jamie Bullock
@@ -45,6 +46,7 @@ BUG HUNTERS/SUGGESTIONS
 
 Bob Broadus
 Chris Sawer
+Christian Ebert
 Darius Blasband
 Donald Axel
 Edward Neeman
@@ -52,6 +54,7 @@ Eduardo Vieira
 Erlend Aasland
 Hans Forbrich
 Jukka Akkanen
+Lambros Lambrou 
 Matevž Jekovec
 Michael Welsh Duggan
 Milan Zamazal
index 42c6ad9d57a1885773a98d3112ac611522bbc429..9465463a0c425228b2264e9d4774b16b6c9f8cf4 100644 (file)
@@ -365,9 +365,9 @@ Lookup::slur (Bezier curve, Real curvethick, Real linethick)
 
   SCM scontrols[8];
 
-  for (int i = 4; i--;)
-    scontrols[ i ] = ly_offset2scm (back.control_[i]);
-  for (int i = 4; i--;)
+  for (int i = 0; i < 4; i++)
+    scontrols[i] = ly_offset2scm (back.control_[i]);
+  for (int i = 0; i < 4; i++)
     scontrols[i + 4] = ly_offset2scm (curve.control_[i]);
 
   /*
index 717a9a8a41989138847e2d36926921464b9f953e..b2b5846a6f81afd32c065e68affa0c16113030b8 100644 (file)
@@ -12,8 +12,8 @@ class Output_stack_element:
                o = Output_stack_element()
                o.factor = self.factor
                return o
-class Output_printer:
 
+class Output_printer:
        ## TODO: support for \relative.
        
        def __init__ (self):
@@ -92,7 +92,7 @@ class Duration:
                self.duration_log = 0
                self.dots = 0
                self.factor = Rational (1)
-
+               
        def lisp_expression (self):
                return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
                                                           self.dots,
@@ -147,7 +147,7 @@ class Pitch:
                self.alteration = 0
                self.step = 0
                self.octave = 0
-
+               
        def __repr__(self):
                return self.ly_expression()
 
@@ -212,12 +212,11 @@ class Music:
        def __init__ (self):
                self.parent = None
                self.start = Rational (0)
-               pass
+               self.comment = ''
 
        def get_length(self):
                return Rational (0)
        
-       
        def get_properties (self):
                return ''
        
@@ -248,25 +247,31 @@ class Music:
                        return self
                return None
 
-       def print_ly (self, printer):
-               printer (self.ly_expression ())
+       def print_comment (self, printer, text = None):
+               if not text:
+                       text = self.comment
 
+               if not text:
+                       return
 
-class Comment (Music):
-       def __name__ (self):
-               self.text = ''
-       def print_ly (self, printer):
+                       
                if isinstance (printer, Output_printer):
-                       lines = string.split (self.text, '\n')
+                       if text == '\n':
+                               printer.newline ()
+                               return
+
+                       lines = string.split (text, '\n')
                        for l in lines:
                                if l:
                                        printer.print_verbatim ('% ' + l)
                                printer.newline ()
                else:
-                       printer ('% ' + re.sub ('\n', '\n% ', self.text))
+                       printer ('% ' + re.sub ('\n', '\n% ', text))
                        printer ('\n')
                        
-       
+
+       def print_ly (self, printer):
+               printer (self.ly_expression ())
 
 class MusicWrapper (Music):
        def __init__ (self):
@@ -290,11 +295,15 @@ class TimeScaledMusic (MusicWrapper):
 class NestedMusic(Music):
        def __init__ (self):
                Music.__init__ (self)
-               self.elements = [] 
+               self.elements = []
+
+       def append (self, what):
+               if what:
+                       self.elements.append (what)
+                       
        def has_children (self):
                return self.elements
 
-
        def insert_around (self, succ, elt, dir):
                assert elt.parent == None
                assert succ == None or succ in self.elements
@@ -357,8 +366,16 @@ class NestedMusic(Music):
 class SequentialMusic (NestedMusic):
        def print_ly (self, printer):
                printer ('{')
+               if self.comment:
+                       self.print_comment (printer)
+               elif isinstance (printer, Output_printer):
+                       printer.newline()
+               else:
+                       printer ('\n')
+                       
                for e in self.elements:
                        e.print_ly (printer)
+
                printer ('}')
 
        def lisp_sub_expression (self, pred):
@@ -406,7 +423,8 @@ class EventChord(NestedMusic):
                #       print  'huh', rest_events, note_events, other_events
                for e in other_events:
                        e.print_ly (printer)
-               
+
+               self.print_comment (printer)
                        
 class Event(Music):
        pass
@@ -417,15 +435,28 @@ class SpanEvent (Event):
                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):
+class BeamEvent (SpanEvent):
+       def ly_expression (self):
+               return {-1: '[',
+                       0:'',
+                       1:']'}[self.span_direction]
+
+class ArpeggioEvent(Event):
        def ly_expression (self):
                return ('\\arpeggio')
+
+
+class TieEvent(Event):
+       def ly_expression (self):
+               return '~'
+
        
 class RhythmicEvent(Event):
        def __init__ (self):
@@ -445,8 +476,6 @@ class RestEvent (RhythmicEvent):
        
        def print_ly (self, printer):
                printer('r')
-               if isinstance(printer, Output_printer):
-                       printer.skipspace()
                self.duration.print_ly (printer)
 
 class SkipEvent (RhythmicEvent):
@@ -457,18 +486,31 @@ class NoteEvent(RhythmicEvent):
        def  __init__ (self):
                RhythmicEvent.__init__ (self)
                self.pitch = Pitch()
-
+               self.cautionary = False
+               self.forced_accidental = False
+               
        def get_properties (self):
                return ("'pitch %s\n 'duration %s"
                        % (self.pitch.lisp_expression (),
                           self.duration.lisp_expression ()))
 
+       def pitch_mods (self):
+               excl_question = ''
+               if self.cautionary:
+                       excl_question += '?'
+               if self.forced_accidental:
+                       excl_question += '!'
+
+               return excl_question
+       
        def ly_expression (self):
-               return '%s%s' % (self.pitch.ly_expression (),
-                                self.duration.ly_expression ())
+               return '%s%s%s' % (self.pitch.ly_expression (),
+                                  self.pitch_mods(),
+                                  self.duration.ly_expression ())
 
        def print_ly (self, printer):
                self.pitch.print_ly (printer)
+               printer (self.pitch_mods ())  
                self.duration.print_ly (printer)
 
 class KeySignatureChange (Music):
index 34de577a7cfe979ea44fb1dafef3b2e882709d53..a1107f6bf09a055c4f60cdf051981d4d003a8da7 100644 (file)
@@ -225,18 +225,12 @@ class Part (Music_xml_node):
                return self._voices
 
 class Notations (Music_xml_node):
+       def get_tie (self):
+               return self.get_maybe_exist_named_child ('tied')
+       
        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):
@@ -248,18 +242,28 @@ class Time_modification(Music_xml_node):
                
 class Tuplet(Music_xml_node):
        pass
+
 class Slur (Music_xml_node):
-       pass
+       def get_type (self):
+               return self.type
+
+class Beam (Music_xml_node):
+       def get_type (self):
+               return self.get_text ()
 
 class Chord (Music_xml_node):
        pass
+
 class Dot (Music_xml_node):
        pass
+
 class Alter (Music_xml_node):
        pass
 
 class Rest (Music_xml_node):
        pass
+class Mode (Music_xml_node):
+       pass
 
 class Type (Music_xml_node):
        pass
@@ -267,23 +271,25 @@ class Grace (Music_xml_node):
        pass
 
 class_dict = {
-       'notations': Notations,
-       'time-modification': Time_modification,
+       '#comment': Hash_comment,
        'alter': Alter,
-       'grace': Grace,
-       'rest':Rest,
-       'dot': Dot,
+       'attributes': Attributes,
+       'beam' : Beam,
        'chord': Chord,
+       'dot': Dot,
        'duration': Duration,
-       'attributes': Attributes,
+       'grace': Grace,
+       'mode' : Mode,
+       'measure': Measure,
+       'notations': Notations,
        'note': Note,
+       'part': Part,
        'pitch': Pitch,
-       'part': Part, 
-       'measure': Measure,
-       'type': Type,
+       'rest':Rest,
        'slur': Slur,
+       'time-modification': Time_modification,
        'tuplet': Tuplet,
-       '#comment': Hash_comment,
+       'type': Type,
 }
 
 def name2class_name (name):
index 31bab22dee7b2ca640c293cc9d9e50567a7d1786..4e5a39b2d838158aff34ddd4bae5e867fa673241 100644 (file)
@@ -27,6 +27,12 @@ import musicxml
 import musicexp
 from rational import Rational
 
+
+def progress (str):
+       sys.stderr.write (str + '\n')
+       sys.stderr.flush ()
+       
+
 def musicxml_duration_to_lily (mxl_note):
        d = musicexp.Duration ()
        if mxl_note.get_maybe_exist_typed_child (musicxml.Type):
@@ -39,12 +45,14 @@ def musicxml_duration_to_lily (mxl_note):
 
        return d        
 
-span_event_dict = {
-       'start': -1,
-       'stop': 1
-}
-
 def group_tuplets (music_list, events):
+
+
+       """Collect Musics from
+       MUSIC_LIST demarcated by EVENTS_LIST in TimeScaledMusic objects.
+       """
+
+       
        indices = []
 
        j = 0
@@ -80,6 +88,7 @@ def group_tuplets (music_list, events):
        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 ()
@@ -98,7 +107,22 @@ def musicxml_time_to_lily (mxl):
        return change
 
 def musicxml_key_to_lily (mxl):
-       mode = mxl.get_maybe_exist_named_child ('mode').get_text ()
+       start_pitch  = musicexp.Pitch ()
+       try:
+               mode = mxl.get_maybe_exist_named_child ('mode')
+               if mode:
+                       mode = mode.get_text ()
+               else:
+                       mode = 'major'
+                       
+               (n,a) = { 'major' : (0,0),
+                         'minor' : (6,0),
+                       }[mode]
+               start_pitch.step = n
+               start_pitch.alteration = a
+       except  KeyError:
+               print 'unknown mode', mode
+               
        fifths = string.atoi (mxl.get_maybe_exist_named_child ('fifths').get_text ())
 
        fifth = musicexp.Pitch()
@@ -108,15 +132,15 @@ def musicxml_key_to_lily (mxl):
                fifth.step *= -1
                fifth.normalize ()
        
-       c = musicexp.Pitch()
+       start_pitch = musicexp.Pitch()
        for x in range (fifths):
-               c = c.transposed (fifth)
+               start_pitch = start_pitch.transposed (fifth)
 
-       c.octave = 0
+       start_pitch.octave = 0
 
        change = musicexp.KeySignatureChange()
        change.mode = mode
-       change.tonic = c
+       change.tonic = start_pitch
        return change
        
 def musicxml_attributes_to_lily (attrs):
@@ -135,16 +159,67 @@ def musicxml_attributes_to_lily (attrs):
        
        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)
+def create_skip_music (duration):
+       skip = musicexp.SkipEvent()
+       skip.duration.duration_log = 0
+       skip.duration.factor = duration
+
+       evc = musicexp.EventChord ()
+       evc.append (skip)
+       return evc
+
+spanner_event_dict = {
+       'slur' : musicexp.SlurEvent,
+       'beam' : musicexp.BeamEvent,
+}      
+spanner_type_dict = {
+       'start': -1,
+       'begin': -1,
+       'stop': 1,
+       'end' : 1
+}
 
-       return ly_voice
+def musicxml_spanner_to_lily_event (mxl_event):
+       ev = None
        
+       name = mxl_event.get_name()
+       try:
+               func = spanner_event_dict[name]
+               ev = func()
+       except KeyError:
+               print 'unknown span event ', mxl_event
+
+       try:
+               key = mxl_event.get_type ()
+               ev.span_direction = spanner_type_dict[key]
+       except KeyError:
+               print 'unknown span type', key, 'for', name
+
+       return ev
+
+def musicxml_note_to_lily_main_event (n):
+       pitch  = None
+       duration = None
+               
+       mxl_pitch = n.get_maybe_exist_typed_child (musicxml.Pitch)
+       event = None
+       if mxl_pitch:
+               pitch = musicxml_pitch_to_lily (mxl_pitch)
+               event = musicexp.NoteEvent()
+               event.pitch = pitch
+
+               acc = n.get_maybe_exist_named_child ('accidental')
+               if acc:
+                       # let's not force accs everywhere. 
+                       event.cautionary = acc.editorial
+                       print event, event.cautionary, event.ly_expression()
+               
+       elif n.get_maybe_exist_typed_child (musicxml.Rest):
+               event = musicexp.RestEvent()
+
+       event.duration = musicxml_duration_to_lily (n)
+       return event
+
 def musicxml_voice_to_lily_voice (voice):
        
        ly_voice = []
@@ -152,68 +227,76 @@ def musicxml_voice_to_lily_voice (voice):
 
        tuplet_events = []
 
-       measure_start_indices = []
        for n in voice:
-               if n.is_first ():
-                       measure_start_indices.append (len (ly_voice))
-                       
+               if n.get_name () == 'forward':
+                       continue
+               
                if isinstance (n, musicxml.Attributes):
                        ly_voice.extend (musicxml_attributes_to_lily (n))
                        continue
                
                if not n.__class__.__name__ == 'Note':
-                       print 'not a Note or Attributes?'
+                       print 'not a Note or Attributes?', n
                        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()
-                       event.pitch = pitch
-               elif n.get_maybe_exist_typed_child (musicxml.Rest):
-                       event = musicexp.RestEvent()
-
-               event.duration = musicxml_duration_to_lily (n)
+               if n.is_first () and ly_voice:
+                       ly_voice[-1].comment += '\n'
+               
                ev_chord = None
                if None ==  n.get_maybe_exist_typed_child (musicxml.Chord):
                        if ly_voice:
                                ly_now += ly_voice[-1].get_length ()
 
                        if ly_now <> n._when:
-                               diff = n._when - ly_now 
+                               diff = n._when - ly_now
                                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
-                               skip.duration.factor = diff
 
-                               evc = musicexp.EventChord ()
-                               evc.elements.append (skip)
-                               ly_voice.append (evc)
+                               ly_voice.append (create_skip_music (diff))
                                ly_now = n._when
                                
                        ly_voice.append (musicexp.EventChord())
                else:
                        pass
-
+               
                ev_chord = ly_voice[-1]
-               ev_chord.elements.append (event)
 
+               main_event = musicxml_note_to_lily_main_event (n)
+               ev_chord.append (main_event)
+                       
+               notations = n.get_maybe_exist_typed_child (musicxml.Notations)
+               tuplet_event = None
+               span_events = []
+               if notations:
+                       if notations.get_tuplet():
+                               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))
+
+                       slurs = [s for s in notations.get_named_children ('slur')
+                                if s.get_type () in ('start','stop')]
+                       if slurs:
+                               if len (slurs) > 1:
+                                       print 'more than 1 slur?'
+
+                               lily_ev = musicxml_spanner_to_lily_event (slurs[0])
+                               ev_chord.append (lily_ev)
+
+                       mxl_tie = notations.get_tie ()
+                       if mxl_tie and mxl_tie.type == 'start':
+                               ev_chord.append (musicexp.TieEvent ())
+
+               mxl_beams = [b for b in n.get_named_children ('beam')
+                            if b.get_type () in ('begin', 'end')] 
+               if mxl_beams:
+                       beam_ev = musicxml_spanner_to_lily_event (mxl_beams[0])
+                       if beam_ev:
+                               ev_chord.append (beam_ev)
+                       
                if tuplet_event:
                        mod = n.get_maybe_exist_typed_child (musicxml.Time_modification)
                        frac = (1,1)
@@ -221,16 +304,7 @@ def musicxml_voice_to_lily_voice (voice):
                                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()
@@ -260,6 +334,8 @@ def musicxml_pitch_to_lily (mxl_pitch):
        return p
 
 def get_all_voices (parts):
+       progress ("Synchronizing MusicXML...")
+       
        all_voices = {} 
        for p in parts:
                p.interpret ()
@@ -267,12 +343,17 @@ def get_all_voices (parts):
                voice_dict = p.get_voices ()
                
                for (id, voice) in voice_dict.items ():
-                       m = musicxml_voice_to_lily_voice (voice)
                        m_name = 'Part' + p.id + 'Voice' + id
                        m_name = musicxml_id_to_lily (m_name)
-                       all_voices[m_name] = m
+                       all_voices[m_name] = voice
+
+
+       progress ("Converting to LilyPond expressions...")
+       all_ly_voices = {}
+       for (k, v) in all_voices.items():
+               all_ly_voices[k] = musicxml_voice_to_lily_voice (v)
 
-       return all_voices
+       return all_ly_voices
 
 class NonDentedHeadingFormatter (optparse.IndentedHelpFormatter):
     def format_heading(self, heading):
@@ -339,15 +420,18 @@ Copyright (c) 2005 by
 def convert (filename, output_name):
        
        printer = musicexp.Output_printer()
+
+       progress ("Reading MusicXML...")
+       
        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')
                
+       progress ("Printing as .ly...")
        for  (k,v) in voices.items():
                printer.dump ('%s = ' % k)
                v.print_ly (printer)
@@ -359,9 +443,6 @@ def convert (filename, output_name):
 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)