]> git.donarmstrong.com Git - lilypond.git/commitdiff
MusicXML: Implement relative mode for pitches
authorReinhold Kainhofer <reinhold@kainhofer.com>
Thu, 15 Nov 2007 20:04:06 +0000 (21:04 +0100)
committerReinhold Kainhofer <reinhold@kainhofer.com>
Thu, 15 Nov 2007 20:04:06 +0000 (21:04 +0100)
Relative mode for pitches is now triggered by an -r or --relative
command-line switch.

In this case, each voice is wrapped inside a RelativeMusic wrapper
object, and the previously converted pitch is stored in a global state
variable. The case of chords needs to be handled with special care,
as the whole chord uses the pitch of the first note as its base pitch.
So, after converting all notes in the chord, we have to reset the
global state variable to the pitch of the first note.

Also added myselfs to the copyright output.

python/musicexp.py
scripts/musicxml2ly.py

index 168be1a33077a20bb639e37f15f0f1a0d1ecc208..8fb4e5c31976c972e3544b182b15ccb14104b2a6 100644 (file)
@@ -5,6 +5,9 @@ import re
 
 from rational import Rational
 
+# Store previously converted pitch for \relative conversion as a global state variable
+previous_pitch = None
+relative_pitches = False
 
 def escape_instrument_string (input_string):
     retstring = string.replace (input_string, "\"", "\\\"")
@@ -295,14 +298,38 @@ class Pitch:
     
     def ly_step_expression (self): 
         return pitch_generating_function (self)
-    
-    def ly_expression (self):
-        str = self.ly_step_expression ()
+
+    def absolute_pitch (self):
         if self.octave >= 0:
-            str += "'" * (self.octave + 1) 
+            return "'" * (self.octave + 1)
         elif self.octave < -1:
-            str += "," * (-self.octave - 1) 
-            
+            return "," * (-self.octave - 1)
+        else:
+            return ''
+
+    def relative_pitch (self):
+        global previous_pitch
+        if not previous_pitch:
+            previous_pitch = self
+            return self.absolute_pitch ()
+        previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step
+        this_pitch_steps = self.octave * 7 + self.step
+        pitch_diff = (this_pitch_steps - previous_pitch_steps)
+        previous_pitch = self
+        if pitch_diff > 3:
+            return "'" * ((pitch_diff + 3) / 7)
+        elif pitch_diff < -3:
+            return "," * ((-pitch_diff + 3) / 7)
+        else:
+            return ""
+
+    def ly_expression (self):
+        str = self.ly_step_expression ()
+        if relative_pitches:
+            str += self.relative_pitch ()
+        else:
+            str += self.absolute_pitch ()
+
         return str
     
     def print_ly (self, outputter):
@@ -390,6 +417,24 @@ class ModeChangingMusicWrapper (MusicWrapper):
         func ('\\%s' % self.mode)
         MusicWrapper.print_ly (self, func)
 
+class RelativeMusic (MusicWrapper):
+    def __init__ (self):
+        MusicWrapper.__init__ (self)
+        self.basepitch = None
+
+    def print_ly (self, func):
+        global previous_pitch
+        global relative_pitches
+        prev_relative_pitches = relative_pitches
+        relative_pitches = True
+        previous_pitch = self.basepitch
+        if not previous_pitch:
+            previous_pitch = Pitch ()
+        func ('\\relative %s%s' % (pitch_generating_function (previous_pitch), 
+                                   previous_pitch.absolute_pitch ()))
+        MusicWrapper.print_ly (self, func)
+        relative_pitches = prev_relative_pitches
+
 class TimeScaledMusic (MusicWrapper):
     def print_ly (self, func):
         func ('\\times %d/%d ' %
@@ -658,8 +703,15 @@ class EventChord (NestedMusic):
         elif len (note_events) == 1:
             note_events[0].print_ly (printer)
         elif note_events:
-            pitches = [x.pitch.ly_expression () for x in note_events]
+            global previous_pitch
+            pitches = []
+            basepitch = None
+            for x in note_events:
+                pitches.append (x.pitch.ly_expression ())
+                if not basepitch:
+                    basepitch = previous_pitch
             printer ('<%s>' % string.join (pitches))
+            previous_pitch = basepitch
             note_events[0].duration.print_ly (printer)
         else:
             pass
index 8f3b42a3967aaf8cc376f6e95b2ae5468e30e1f8..01f02cfeac9a137f3779eb82775401acb8728b02 100644 (file)
@@ -1238,6 +1238,9 @@ def musicxml_voice_to_lily_voice (voice):
     lyrics = {}
     return_value = VoiceData ()
     return_value.voicedata = voice
+    
+    # First pitch needed for relative mode (if selected in command-line options)
+    first_pitch = None
 
     # Needed for melismata detection (ignore lyrics on those notes!):
     inside_slur = False
@@ -1338,6 +1341,8 @@ def musicxml_voice_to_lily_voice (voice):
                 voice_builder.add_bar_check (num)
 
         main_event = musicxml_note_to_lily_main_event (n)
+        if main_event and not first_pitch:
+            first_pitch = main_event.pitch
         ignore_lyrics = inside_slur or is_tied or is_chord
 
         if hasattr (main_event, 'drum_type') and main_event.drum_type:
@@ -1527,6 +1532,12 @@ def musicxml_voice_to_lily_voice (voice):
     
     if len (modes_found) > 1:
        error_message ('Too many modes found %s' % modes_found.keys ())
+       
+    if options.relative:
+        v = musicexp.RelativeMusic ()
+        v.element = seq_music
+        v.basepitch = first_pitch
+        seq_music = v
 
     return_value.ly_voice = seq_music
     for mode in modes_found.keys ():
@@ -1602,7 +1613,7 @@ def get_all_voices (parts):
 
 
 def option_parser ():
-    p = ly.get_option_parser(usage=_ ("musicxml2ly FILE.xml"),
+    p = ly.get_option_parser(usage=_ ("musicxml2ly [options] FILE.xml"),
                              version=('''%prog (LilyPond) @TOPLEVEL_VERSION@\n\n'''
                                       +
 _ ("""This program is free software.  It is covered by the GNU General Public
@@ -1611,8 +1622,9 @@ under certain conditions.  Invoke as `%s --warranty' for more
 information.""") % 'lilypond'
 + """
 Copyright (c) 2005--2007 by
-    Han-Wen Nienhuys <hanwen@xs4all.nl> and
-    Jan Nieuwenhuizen <janneke@gnu.org>
+    Han-Wen Nienhuys <hanwen@xs4all.nl>,
+    Jan Nieuwenhuizen <janneke@gnu.org> and
+    Reinhold Kainhofer <reinhold@kainhofer.com>
 """),
                              description=_ ("Convert %s to LilyPond input.") % 'MusicXML' + "\n")
     p.add_option ('-v', '--verbose',
@@ -1632,6 +1644,11 @@ Copyright (c) 2005--2007 by
                   default = False,
                   help = _ ("Input file is a zip-compressed MusicXML file."))
 
+    p.add_option ('-r', '--relative',
+                  action = "store_true",
+                  dest = "relative",
+                  help = _ ("Convert pitches in relative mode."))
+
     p.add_option ('-l', '--language',
                   action = "store",
                   help = _ ("Use a different language file, e.g. 'deutsch' for deutsch.ly."))