]> git.donarmstrong.com Git - lilypond.git/commitdiff
* scripts/musicxml2ly.py (print_voice_definitions): new function
authorHan-Wen Nienhuys <hanwen@xs4all.nl>
Fri, 30 Dec 2005 19:08:31 +0000 (19:08 +0000)
committerHan-Wen Nienhuys <hanwen@xs4all.nl>
Fri, 30 Dec 2005 19:08:31 +0000 (19:08 +0000)
(print_score_setup): new function
(convert): read part definition to output staves properly.

* python/musicxml.py (Measure.get_notes): new class. Wrap lists of
Music_xml notes.

* python/musicexp.py (Output_printer.__init__): use _ in data members.
(Output_printer.unformatted_output): new function: no formatting,
but count braces.
(Duration.get_length): remove isinstance() checks for Output_printer everywhere.

* input/regression/lyrics-bar.ly (texidoc): add Separating_line_group_engraver.

ChangeLog
input/regression/lyrics-bar.ly
lily/relocate.cc
python/musicexp.py
python/musicxml.py
scripts/musicxml2ly.py

index 4f05fcde60f70ccdc019539d1a5c5191577ae9c3..8d1afa7e48653ff1e3ee3733046e1b2e48565772 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2005-12-30  Han-Wen Nienhuys  <hanwen@xs4all.nl>
+
+
+       * scripts/musicxml2ly.py (print_voice_definitions): new function 
+       (print_score_setup): new function
+       (convert): read part definition to output staves properly.
+
+       * python/musicxml.py (Measure.get_notes): new class. Wrap lists of
+       Music_xml notes.
+
+       * python/musicexp.py (Output_printer.__init__): use _ in data members.
+       (Output_printer.unformatted_output): new function: no formatting,
+       but count braces.
+       (Duration.get_length): remove isinstance() checks for Output_printer everywhere.
+
+       * input/regression/lyrics-bar.ly (texidoc): add Separating_line_group_engraver.
+
 2005-12-29  Jan Nieuwenhuizen  <janneke@gnu.org>
 
        * lily/relocate.cc (framework_relocation): New function,
@@ -7,7 +24,8 @@
 
 2005-12-30  Han-Wen Nienhuys  <hanwen@xs4all.nl>
 
-       * scm/*.scm: use LEFT/RIGHT/UP/DOWN etc. iso. magical numbers.
+       * scm/*.scm: use LEFT/RIGHT/UP/DOWN etc. iso. magical
+       numbers. Patch by Erlend Aasland.
 
 2005-12-29  Han-Wen Nienhuys  <hanwen@xs4all.nl>
 
index c14ed991c2e75f8f9df25769a2931ff18a672d65..0c34ee3d2395d35da2f7b2f349913d8ab836f00b 100644 (file)
@@ -21,7 +21,8 @@ lyrics do not collide with barlines.
     }
     \context Lyrics \with {
        \consists "Bar_engraver"
-       \override BarLine #'bar-size = #4 
+       \consists "Separating_line_group_engraver"
+       \override BarLine #'bar-size = #4 
     } \lyricmode {
          looooooooooooooooooooooooooooooooooong1 syllable
       }
index 753557dc0b848ebc9d573e23cd0f136ae3bfbad7..ecc8a787ea20d0d73f15ef24c12cb662f3cbc740 100644 (file)
@@ -41,10 +41,10 @@ sane_putenv (char const *key, String value, bool overwrite)
 }
 
 static int
-set_env_file (char const *key, String value)
+set_env_file (char const *key, String value, bool overwrite = false)
 {
   if (is_file (value))
-    return sane_putenv (key, value, false);
+    return sane_putenv (key, value, overwrite);
   else if (be_verbose_global)
     warning (_f ("no such file: %s for %s", value, key));
   return -1;
@@ -132,7 +132,7 @@ framework_relocation (String prefix)
   /* need otherwise dynamic .so's aren't found.   */
   prepend_env_path ("DYLD_LIBRARY_PATH", libdir);
   
-  set_env_file ("FONTCONFIG_FILE", sysconfdir + "/fonts/fonts.conf");
+  set_env_file ("FONTCONFIG_FILE", sysconfdir + "/fonts/fonts.conf", true);
   set_env_dir ("FONTCONFIG_PATH", sysconfdir + "/fonts");
 
 #ifdef __MINGW32__
@@ -156,7 +156,7 @@ framework_relocation (String prefix)
 
   set_env_file ("PANGO_RC_FILE", sysconfdir + "/pango/pangorc");
   set_env_dir ("PANGO_PREFIX", prefix);
-
+  
   prepend_env_path ("PATH", bindir);
 }
 
index 43c75c870fbd29b5bcf67cf77090ad8c9d2ed634..5698c9f699ac870a06b3fba59929903bd13cd07f 100644 (file)
@@ -14,67 +14,77 @@ class Output_stack_element:
                return o
 
 class Output_printer:
+
+       """A class that takes care of formatting (eg.: indenting) a
+       Music expression as a .ly file.
+       
+       """
        ## TODO: support for \relative.
        
        def __init__ (self):
-               self.line = ''
-               self.indent = 4
-               self.nesting = 0
-               self.file = sys.stdout
-               self.line_len = 72
-               self.output_state_stack = [Output_stack_element()]
+               self._line = ''
+               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
+               self._last_duration = None
 
+       def set_file (self, file):
+               self._file = file
+               
        def dump_version (self):
                self.newline ()
                self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
                self.newline ()
                
        def get_indent (self):
-               return self.nesting * self.indent
+               return self._nesting * self._indent
        
        def override (self):
-               last = self.output_state_stack[-1]
-               self.output_state_stack.append (last.copy())
+               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
+               self._output_state_stack[-1].factor *=  factor
 
        def revert (self):
-               del self.output_state_stack[-1]
-               if not self.output_state_stack:
+               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
+               return self._output_state_stack[-1].factor
 
        def print_verbatim (self, str):
-               self.line += str
+               self._line += str
+
+       def unformatted_output (self, str):
+               self._nesting += str.count ('<') + str.count ('{')
+               self._nesting -= str.count ('>') + str.count ('}')
+               self.print_verbatim (str)
                
        def print_duration_string (self, str):
-               if self.last_duration == str:
+               if self._last_duration == str:
                        return
                
-               self.print_verbatim (str)
+               self.unformatted_output (str)
                                     
        def add_word (self, str):
-               if (len (str) + 1 + len (self.line) > self.line_len):
+               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 ('}')
-
                if not self._skipspace:
-                       self.line += ' '
-               self.line += str
+                       self._line += ' '
+               self.unformatted_output (str)
                self._skipspace = False
                
        def newline (self):
-               self.file.write (self.line + '\n')
-               self.line = ' ' * self.indent * self.nesting
+               self._file.write (self._line + '\n')
+               self._line = ' ' * self._indent * self._nesting
                self._skipspace = True
 
        def skipspace (self):
@@ -84,9 +94,10 @@ class Output_printer:
                self.dump (arg)
        
        def dump (self, str):
+               
                if self._skipspace:
                        self._skipspace = False
-                       self.print_verbatim (str)
+                       self.unformatted_output (str)
                else:
                        words = string.split (str)
                        for w in words:
@@ -117,11 +128,8 @@ class Duration:
                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 ())
+               str = self.ly_expression (self.factor / outputter.duration_factor ())
+               outputter.print_duration_string (str)
                
        def __repr__(self):
                return self.ly_expression()
@@ -218,7 +226,8 @@ class Music:
                self.parent = None
                self.start = Rational (0)
                self.comment = ''
-
+               self.identifier = None
+               
        def get_length(self):
                return Rational (0)
        
@@ -260,21 +269,22 @@ class Music:
                        return
 
                        
-               if isinstance (printer, Output_printer):
-                       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% ', text))
-                       printer ('\n')
+               if text == '\n':
+                       printer.newline ()
+                       return
+               lines = string.split (text, '\n')
+               for l in lines:
+                       if l:
+                               printer.dump ('% ' + l)
+                       printer.newline ()
                        
 
+       def print_with_identifier (self, printer):
+               if self.identifier: 
+                       printer ("\\%s" % self.identifier)
+               else:
+                       self.print_ly (printer)
+
        def print_ly (self, printer):
                printer (self.ly_expression ())
 
@@ -287,15 +297,11 @@ class MusicWrapper (Music):
 
 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)
+               func ('\\times %d/%d ' %
+                     (self.numerator, self.denominator))
+               func.add_factor (Rational (self.numerator, self.denominator))
+               MusicWrapper.print_ly (self, func)
+               func.revert ()
 
 class NestedMusic(Music):
        def __init__ (self):
@@ -373,16 +379,14 @@ class SequentialMusic (NestedMusic):
                printer ('{')
                if self.comment:
                        self.print_comment (printer)
-               elif isinstance (printer, Output_printer):
-                       printer.newline()
-               else:
-                       printer ('\n')
-                       
+
+               printer.newline()
                for e in self.elements:
                        e.print_ly (printer)
 
                printer ('}')
-
+               printer.newline()
+                       
        def lisp_sub_expression (self, pred):
                name = self.name()
 
@@ -593,6 +597,37 @@ def test_pitch ():
        print bflat.transposed (down).transposed (down)
        print bflat.transposed (down).transposed (down).transposed (down)
 
+
+
+def test_printer ():
+       def make_note ():
+               evc = EventChord()
+               n = NoteEvent()
+               evc.append (n)
+               return n
+
+       def make_tup ():
+               m = SequentialMusic()
+               m.append (make_note ())
+               m.append (make_note ())
+               m.append (make_note ())
+
+               
+               t = TimeScaledMusic ()
+               t.numerator = 2
+               t.denominator = 3
+               t.element = m
+               return t
+
+       m = SequentialMusic ()
+       m.append (make_tup ())
+       m.append (make_tup ())
+       m.append (make_tup ())
+       
+       printer = Output_printer()
+       m.print_ly (printer)
+       printer.newline ()
+       
 def test_expr ():
        m = SequentialMusic()
        l = 2  
@@ -617,14 +652,18 @@ def test_expr ():
        evc.insert_around (None, n, 0)
        m.insert_around (None, evc, 0)
 
-       evc = ClefChange("G")
+       evc = ClefChange()
+       evc.type = 'treble'
        m.insert_around (None, evc, 0)
 
        evc = EventChord()
        tonic = Pitch ()
        tonic.step = 2
        tonic.alteration = -2
-       n = KeySignatureEvent(tonic, [0, 0, -2, 0, 0,-2,-2]  )
+       n = KeySignatureChange()
+       n.tonic=tonic.copy()
+       n.scale = [0, 0, -2, 0, 0,-2,-2]
+       
        evc.insert_around (None, n, 0)
        m.insert_around (None, evc, 0)
 
@@ -632,8 +671,10 @@ def test_expr ():
 
 
 if __name__ == '__main__':
+       test_printer ()
+       raise 'bla'
        test_pitch()
-       raise 1
+       
        expr = test_expr()
        expr.set_start (Rational (0))
        print expr.ly_expression()
index 8886ecead00d2dcc2fb9674a9674bc7018992354..78d68dcd0bf2e878abe05d283fa9ee4d19dfd37e 100644 (file)
@@ -16,7 +16,7 @@ class Xml_node:
                self._parent = None
 
        def is_first (self):
-               return self._parent.get_typed_children (self.__class__)[0] == self
+               return self._parent.get_typed_children (self.__class__)[0] == self
 
        def original (self):
                return self._original 
@@ -38,6 +38,9 @@ class Xml_node:
        def get_named_children (self, nm):
                return self.get_typed_children (class_dict[nm])
 
+       def get_named_child (self, nm):
+               return self.get_maybe_exist_named_child (nm)
+
        def get_children (self, predicate):
                return [c for c in self._children if predicate(c)]
 
@@ -147,13 +150,39 @@ class Note (Measure_element):
        def get_pitches (self):
                return self.get_typed_children (class_dict[u'pitch'])
 
-
+class Part_list (Music_xml_node):
+       pass
                
 class Measure(Music_xml_node):
        def get_notes (self):
                return self.get_typed_children (class_dict[u'note'])
 
+
+class Musicxml_voice:
+       def __init__ (self):
+               self._elements = []
+               self._staves = {}
+               self._start_staff = None
+               
+       def add_element (self, e):
+               self._elements.append (e)
+               if (isinstance (e, Note)
+                   and e.get_maybe_exist_typed_child (Staff)):
+                       name = e.get_maybe_exist_typed_child (Staff).get_text ()
+
+                       if not self._start_staff:
+                               self._start_staff = name
+                       self._staves[name] = True
+
+       def insert (self, idx, e):
+               self._elements.insert (idx, e)
+
+       
+
 class Part (Music_xml_node):
+       def __init__ (self):
+               self._voices = []
+               
        def interpret (self):
                """Set durations and starting points."""
                
@@ -207,31 +236,36 @@ class Part (Music_xml_node):
 
                        if isinstance (n, Attributes):
                                for v in voices.values ():
-                                       v.append (n)
+                                       v.add_element (n)
                                continue
                        
                        id = voice_id.get_text ()
                        if not voices.has_key (id):
-                               voices[id] = []
+                               voices[id] = Musicxml_voice()
 
-                       voices[id].append (n)
+                       voices[id].add_element (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_tie (self):
-               return self.get_maybe_exist_named_child ('tied')
+               ts = self.get_named_children ('tied')
+               starts = [t for t in ts if t.type == 'start']
+               if starts:
+                       return starts[0]
+               else:
+                       return None
        
        def get_tuplet (self):
                return self.get_maybe_exist_typed_child (Tuplet)
        
-
 class Time_modification(Music_xml_node):
        def get_fraction (self):
                b = self.get_maybe_exist_typed_child (class_dict['actual-notes'])
@@ -269,11 +303,15 @@ class Rest (Music_xml_node):
        pass
 class Mode (Music_xml_node):
        pass
-
+class Tied (Music_xml_node):
+       pass
+       
 class Type (Music_xml_node):
        pass
 class Grace (Music_xml_node):
        pass
+class Staff (Music_xml_node):
+       pass
 
 class_dict = {
        '#comment': Hash_comment,
@@ -293,9 +331,12 @@ class_dict = {
        'pitch': Pitch,
        'rest':Rest,
        'slur': Slur,
+       'tied': Tied,
        'time-modification': Time_modification,
        'tuplet': Tuplet,
        'type': Type,
+       'part-list': Part_list,
+       'staff': Staff,
 }
 
 def name2class_name (name):
index dddafd08e3ceab105e7d0fcb126958bb82d72547..b5fd27f154df7d5ef5881ba05f2551321d01d10a 100644 (file)
@@ -220,14 +220,13 @@ def musicxml_note_to_lily_main_event (n):
        return event
 
 def musicxml_voice_to_lily_voice (voice):
-       
        ly_voice = []
        ly_now = Rational (0)
        pending_skip = Rational (0) 
 
        tuplet_events = []
 
-       for n in voice:
+       for n in voice._elements:
                if n.get_name () == 'forward':
                        continue
                
@@ -275,6 +274,7 @@ def musicxml_voice_to_lily_voice (voice):
                span_events = []
                if notations:
                        if notations.get_tuplet():
+                               tuplet_event = notations.get_tuplet()
                                mod = n.get_maybe_exist_typed_child (musicxml.Time_modification)
                                frac = (1,1)
                                if mod:
@@ -338,30 +338,34 @@ def musicxml_pitch_to_lily (mxl_pitch):
        p.octave = mxl_pitch.get_octave () - 4
        return p
 
-def synchronize_musicxml (parts):
-       progress ("Synchronizing MusicXML...")
-       
-       all_voices = {} 
-       for p in parts:
-               p.interpret ()
-               p.extract_voices ()             
-               voice_dict = p.get_voices ()
-               
-               for (id, voice) in voice_dict.items ():
-                       m_name = 'Part' + p.id + 'Voice' + id
-                       m_name = musicxml_id_to_lily (m_name)
-                       all_voices[m_name] = voice
-       return all_voices
+
+
+def voices_in_part (part):
+       """Return a Name -> Voice dictionary for PART"""
+       part.interpret ()
+       part.extract_voices ()          
+       voice_dict = part.get_voices ()
+
+       return voice_dict
+
+def voices_in_part_in_parts (parts):
+       """return a Part -> Name -> Voice dictionary"""
+       return dict([(p, voices_in_part (p)) for p in parts])
+
 
 def get_all_voices (parts):
+       all_voices = voices_in_part_in_parts (parts)
 
-       all_voices = synchronize_musicxml (parts)
-       
-       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)
+       for p, name_voice in all_voices.items ():
 
+               part_ly_voices = {}
+               for n, v in name_voice.items ():
+                       progress ("Converting to LilyPond expressions...")
+                       part_ly_voices[n] = (musicxml_voice_to_lily_voice (v), v)
+
+               all_ly_voices[p] = part_ly_voices
+               
        return all_ly_voices
 
 class NonDentedHeadingFormatter (optparse.IndentedHelpFormatter):
@@ -425,6 +429,80 @@ Copyright (c) 2005 by
        p.formatter = NonDentedHeadingFormatter () 
        return p
 
+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):
+       for (part, nv_dict) in voices.items():
+               for (name, (voice, mxlvoice)) in nv_dict.items ():
+                       k = music_xml_voice_name_to_lily_name (part, name)
+                       printer.dump ('%s = ' % k)
+                       voice.print_ly (printer)
+                       printer.newline()
+def uniq_list (l):
+       return dict ([(elt,1) for elt in l]).keys ()
+       
+def print_score_setup (printer, part_list, voices):
+       part_dict = dict ([(p.id, p) for p in voices.keys ()]) 
+
+       printer ('<<')
+       printer.newline ()
+       for part_definition in part_list:
+               part_name = part_definition.id
+               try:
+                       part = part_dict[part_name]
+               except KeyError:
+                       print 'unknown part in part-list:', part_name
+                       continue
+
+               nv_dict = voices[part]
+               staves = reduce (lambda x,y: x+ y,
+                                [mxlvoice._staves.keys ()
+                                 for (v, mxlvoice) in nv_dict.values ()],
+                                [])
+
+               if len (staves) > 1:
+                       staves = uniq_list (staves)
+                       staves.sort ()
+                       printer ('\\context PianoStaff << ')
+                       printer.newline ()
+                       
+                       for s in staves:
+                               staff_voices = [music_xml_voice_name_to_lily_name (part, voice_name)
+                                               for (voice_name, (v, mxlvoice)) in nv_dict.items ()
+                                               if mxlvoice._start_staff == s]
+                               
+                               printer ('\\context Staff = "%s" << ' % s)
+                               printer.newline ()
+                               for v in staff_voices:
+                                       printer ('\\context Voice = "%s"  \\%s' % (v,v))
+                                       printer.newline ()
+                               printer ('>>')
+                               printer.newline ()
+                               
+                       printer ('>>')
+                       printer.newline ()
+                       
+               else:
+                       printer ('\\new Staff <<')
+                       printer.newline ()
+                       for (n,v) in nv_dict.items ():
+
+                               n = music_xml_voice_name_to_lily_name (part, n) 
+                               printer ('\\context Voice = "%s"  \\%s' % (n,n))
+                       printer ('>>')
+                       printer.newline ()
+                       
+
+       printer ('>>')
+       printer.newline ()
+
+                               
+
+def print_ly_preamble (printer, filename):
+       printer.dump_version ()
+       printer.print_verbatim ('%% converted from %s\n' % filename)
 
 def convert (filename, output_name):
        printer = musicexp.Output_printer()
@@ -432,21 +510,28 @@ def convert (filename, output_name):
        
        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')
+       part_list = []
+       if tree.get_maybe_exist_typed_child (musicxml.Part_list):
+               pl = tree.get_maybe_exist_typed_child (musicxml.Part_list)
+               part_list = pl.get_named_children ("score-part")
                
-       progress ("Printing as .ly...")
+       if not output_name:
+               output_name = os.path.basename (filename)
+               output_name = os.path.splitext (output_name)[0] + '.ly'
 
-       printer.dump_version ()
-       for  (k,v) in voices.items():
-               printer.print_verbatim ('%% converted from %s\n' % filename) 
-               printer.dump ('%s = ' % k)
-               v.print_ly (printer)
-               printer.newline()
+               
+       if output_name:
+               progress ("Output to `%s'" % output_name)
+               printer.set_file (open (output_name, 'w'))
+       
+       progress ("Printing as .ly...")
 
+       print_ly_preamble (printer, filename)
+       print_voice_definitions (printer,  voices)
+       print_score_setup (printer, part_list, voices)
+       printer.newline ()
        return voices