]> git.donarmstrong.com Git - lilypond.git/commitdiff
Implement the conversion of lyrics from MusicXML to lilypond.
authorReinhold Kainhofer <reinhold@kainhofer.com>
Wed, 29 Aug 2007 22:48:58 +0000 (00:48 +0200)
committerHan-Wen Nienhuys <hanwen@xs4all.nl>
Sat, 1 Sep 2007 03:07:33 +0000 (00:07 -0300)
python/musicexp.py
python/musicxml.py
scripts/musicxml2ly.py

index 38f772c38ad6f8168b332c87d8ac6365c23fcea6..abe3125955437bd3e7352372c3a5f518aac9c95e 100644 (file)
@@ -413,7 +413,25 @@ class SequentialMusic (NestedMusic):
         for e in self.elements:
             e.set_start (start)
             start += e.get_length()
-            
+
+class Lyrics:
+    def __init__ (self):
+        self.lyrics_syllables = []
+
+    def print_ly (self, printer):
+        printer.dump ("\lyricmode {")
+        for l in self.lyrics_syllables:
+            printer.dump ( "%s " % l )
+        printer.dump ("}")
+
+    def ly_expression (self):
+        lstr = "\lyricmode {\n  "
+        for l in self.lyrics_syllables:
+            lstr += l + " "
+        lstr += "\n}"
+        return lstr
+
+
 class EventChord(NestedMusic):
     def get_length (self):
         l = Rational (0)
index 2b7d342f3b252eea674f44bc7f2cbedc15639e13..d394697c54eae5225a77d97b9d85d0d5da20b698 100644 (file)
@@ -299,16 +299,53 @@ class Part_list (Music_xml_node):
             print "Opps, couldn't find instrument for ID=", id
             return "Grand Piano"
         
-class Measure(Music_xml_node):
+class Measure (Music_xml_node):
     def get_notes (self):
        return self.get_typed_children (get_class (u'note'))
 
-    
+class Syllabic (Music_xml_node):
+    def continued (self):
+        text = self.get_text()
+        return (text == "begin") or (text == "middle")
+class Text (Music_xml_node):
+    pass
+
+class Lyric (Music_xml_node):
+    def get_number (self):
+        if hasattr (self, 'number'):
+            return self.number
+        else:
+            return -1
+
+    def lyric_to_text (self):
+        continued = False
+        syllabic = self.get_maybe_exist_typed_child (Syllabic)
+        if syllabic:
+            continued = syllabic.continued ()
+        text = self.get_maybe_exist_typed_child (Text)
+        
+        if text:
+            text = text.get_text()
+        if text == "-" and continued:
+            return "--"
+        elif text == "_" and continued:
+            return "__"
+        elif continued and text:
+            return text + " --"
+        elif continued:
+            return "--"
+        elif text:
+            return text
+        else:
+            return ""
+
 class Musicxml_voice:
     def __init__ (self):
        self._elements = []
        self._staves = {}
        self._start_staff = None
+        self._lyrics = []
+        self._has_lyrics = False
 
     def add_element (self, e):
        self._elements.append (e)
@@ -320,9 +357,25 @@ class Musicxml_voice:
                self._start_staff = name
            self._staves[name] = True
 
+        lyrics = e.get_typed_children (Lyric)
+        if not self._has_lyrics:
+          self.has_lyrics = len (lyrics) > 0
+
+        for l in lyrics:
+            nr = l.get_number()
+            if (nr > 0) and not (nr in self._lyrics):
+                self._lyrics.append (nr)
+
     def insert (self, idx, e):
        self._elements.insert (idx, e)
 
+    def get_lyrics_numbers (self):
+        if (len (self._lyrics) == 0) and self._has_lyrics:
+            #only happens if none of the <lyric> tags has a number attribute
+            return [1]
+        else:
+            return self._lyrics
+
 
 
 class Part (Music_xml_node):
@@ -533,6 +586,7 @@ class_dict = {
        'duration': Duration,
        'grace': Grace,
         'identification': Identification,
+        'lyric': Lyric,
        'measure': Measure,
        'notations': Notations,
        'note': Note,
@@ -541,6 +595,8 @@ class_dict = {
        'pitch': Pitch,
        'rest': Rest,
        'slur': Slur,
+        'syllabic': Syllabic,
+        'text': Text,
        'time-modification': Time_modification,
        'type': Type,
         'work': Work,
index 45ffbb8c88e497d4c482dc6143e0d85067bea066..ed57e29edb80f47b0bba66c229c42c55ce013613 100644 (file)
@@ -423,7 +423,7 @@ class LilyPondVoiceBuilder:
         self.elements.append (music)
         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:
@@ -478,6 +478,10 @@ class LilyPondVoiceBuilder:
 def musicxml_voice_to_lily_voice (voice):
     tuplet_events = []
     modes_found = {}
+    lyrics = {}
+        
+    for k in voice.get_lyrics_numbers ():
+        lyrics[k] = []
 
     voice_builder = LilyPondVoiceBuilder()
 
@@ -616,6 +620,22 @@ def musicxml_voice_to_lily_voice (voice):
                     if ev:
                         ev_chord.append (ev)
 
+        # Extract the lyrics
+        note_lyrics_processed = []
+        note_lyrics_elements = n.get_typed_children (musicxml.Lyric)
+        for l in note_lyrics_elements:
+            if l.get_number () < 0:
+                for k in lyrics.keys ():
+                    lyrics[k].append (l.lyric_to_text ())
+                    note_lyrics_processed.append (k)
+            else:
+                lyrics[l.number].append(l.lyric_to_text ())
+                note_lyrics_processed.append (l.number)
+        for lnr in lyrics.keys ():
+            if not lnr in note_lyrics_processed:
+                lyrics[lnr].append ("\"\"")
+
+
         mxl_beams = [b for b in n.get_named_children ('beam')
                      if (b.get_type () in ('begin', 'end')
                          and b.is_primary ())] 
@@ -637,7 +657,7 @@ def musicxml_voice_to_lily_voice (voice):
     
     ly_voice = group_tuplets (voice_builder.elements, tuplet_events)
 
-    seq_music = musicexp.SequentialMusic()
+    seq_music = musicexp.SequentialMusic ()
 
     if 'drummode' in modes_found.keys ():
         ## \key <pitch> barfs in drummode.
@@ -645,7 +665,10 @@ def musicxml_voice_to_lily_voice (voice):
                     if not isinstance(e, musicexp.KeySignatureChange)]
     
     seq_music.elements = ly_voice
-
+    lyrics_dict = {}
+    for k in lyrics.keys ():
+        lyrics_dict[k] = musicexp.Lyrics ()
+        lyrics_dict[k].lyrics_syllables = lyrics[k]
     
     
     if len (modes_found) > 1:
@@ -658,7 +681,7 @@ def musicxml_voice_to_lily_voice (voice):
         v.mode = mode
         return_value = v
     
-    return return_value
+    return (return_value, lyrics_dict)
 
 
 def musicxml_id_to_lily (id):
@@ -702,6 +725,7 @@ def get_all_voices (parts):
         part_ly_voices = {}
         for n, v in name_voice.items ():
             progress ("Converting to LilyPond expressions...")
+            # musicxml_voice_to_lily_voice returns (lily_voice, {nr->lyrics, nr->lyrics})
             part_ly_voices[n] = (musicxml_voice_to_lily_voice (v), v)
 
         all_ly_voices[p] = part_ly_voices
@@ -751,6 +775,10 @@ def music_xml_voice_name_to_lily_name (part, name):
     str = "Part%sVoice%s" % (part.id, name)
     return musicxml_id_to_lily (str) 
 
+def music_xml_lyrics_name_to_lily_name (part, name, lyricsnr):
+    str = "Part%sVoice%sLyrics%s" % (part.id, name, lyricsnr)
+    return musicxml_id_to_lily (str) 
+
 def print_voice_definitions (printer, part_list, voices):
     part_dict={}
     for (part, nv_dict) in voices.items():
@@ -758,11 +786,17 @@ def print_voice_definitions (printer, part_list, voices):
 
     for part in part_list:
         (part, nv_dict) = part_dict.get (part.id, (None, {}))
-        for (name, (voice, mxlvoice)) in nv_dict.items ():
+        for (name, ((voice, lyrics), 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()
+            
+            for l in lyrics.keys ():
+                lname = music_xml_lyrics_name_to_lily_name (part, name, l)
+                printer.dump ('%s = ' %lname )
+                lyrics[l].print_ly (printer)
+                printer.newline()
 
             
 def uniq_list (l):
@@ -780,6 +814,7 @@ def print_score_setup (printer, part_list, voices):
             print 'unknown part in part-list:', part_name
             continue
 
+        # TODO: Apparently this is broken! There is always only one staff...
         nv_dict = voices.get (part)
         staves = reduce (lambda x,y: x+ y,
                 [mxlvoice._staves.keys ()
@@ -793,15 +828,23 @@ def print_score_setup (printer, part_list, voices):
             printer.newline ()
             
             for s in staves:
-                staff_voices = [music_xml_voice_name_to_lily_name (part, voice_name)
+                staff_voices = [(music_xml_voice_name_to_lily_name (part, voice_name), voice_name, v)
                         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:
+                for (v, voice_name, (music, lyrics)) in staff_voices:
                     printer ('\\context Voice = "%s"  \\%s' % (v,v))
                     printer.newline ()
+                    
+                    # Assign the lyrics to that voice
+                    for l in lyrics.keys ():
+                        ll = music_xml_lyrics_name_to_lily_name (part, voice_name, l)
+                        printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v, ll))
+                        printer.newline()
+                        printer.newline()
+                    
                 printer ('>>')
                 printer.newline ()
                 
@@ -812,9 +855,16 @@ def print_score_setup (printer, part_list, voices):
             printer ('\\new Staff <<')
             printer.newline ()
             for (n,v) in nv_dict.items ():
+                ((music, lyrics), voice) = v
+                nn = music_xml_voice_name_to_lily_name (part, n) 
+                printer ('\\context Voice = "%s"  \\%s' % (nn,nn))
+
+                # Assign the lyrics to that voice
+                for l in lyrics.keys ():
+                    ll = music_xml_lyrics_name_to_lily_name (part, n, l)
+                    printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (nn, ll))
+                    printer.newline()
 
-                n = music_xml_voice_name_to_lily_name (part, n) 
-                printer ('\\context Voice = "%s"  \\%s' % (n,n))
             printer ('>>')
             printer.newline ()