]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/musicxml2ly.py
Merge branch 'lilypond/translation'
[lilypond.git] / scripts / musicxml2ly.py
index b0f8284d864cb511d3903c637051666261a01d42..2b630029b6223bbb061580201fa54f3874d66c31 100644 (file)
@@ -7,6 +7,7 @@ import os
 import string
 import codecs
 import zipfile
+import tempfile
 import StringIO
 
 """
@@ -67,109 +68,8 @@ additional_definitions = {
     (let* ((ev (event-cause grob))
            (den (if denominator denominator (ly:event-property ev 'denominator)))
            (num (if numerator numerator (ly:event-property ev 'numerator))))
-       (format "~a:~a" den num)))
+       (format #f "~a:~a" den num)))
 """,
-
-  "compound-time-signature": """%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% Formatting of (possibly complex) compound time signatures
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-#(define-public (insert-markups l m)
-  (let* ((ll (reverse l)))
-    (let join-markups ((markups (list (car ll)))
-                       (remaining (cdr ll)))
-      (if (pair? remaining)
-        (join-markups (cons (car remaining) (cons m markups)) (cdr remaining))
-        markups))))
-
-% Use a centered-column inside a left-column, because the centered column
-% moves its reference point to the center, which the left-column undoes.
-% The center-column also aligns its contented centered, which is not undone...
-#(define-public (format-time-fraction time-sig-fraction)
-  (let* ((revargs (reverse (map number->string time-sig-fraction)))
-         (den (car revargs))
-         (nums (reverse (cdr revargs))))
-    (make-override-markup '(baseline-skip . 0)
-      (make-number-markup
-        (make-left-column-markup (list
-          (make-center-column-markup (list
-            (make-line-markup (insert-markups nums "+"))
-            den))))))))
-
-#(define-public (format-complex-compound-time time-sig)
-  (let* ((sigs (map format-time-fraction time-sig)))
-    (make-override-markup '(baseline-skip . 0)
-      (make-number-markup
-        (make-line-markup
-          (insert-markups sigs (make-vcenter-markup "+")))))))
-
-#(define-public (format-compound-time time-sig)
-  (cond
-    ((not (pair? time-sig)) (null-markup))
-    ((pair? (car time-sig)) (format-complex-compound-time time-sig))
-    (else (format-time-fraction time-sig))))
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% Measure length calculation of (possibly complex) compound time signatures
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-#(define-public (calculate-time-fraction time-sig-fraction)
-  (let* ((revargs (reverse time-sig-fraction))
-         (den (car revargs))
-         (nums (cdr revargs)))
-    (ly:make-moment (apply + nums) den)))
-
-#(define-public (calculate-complex-compound-time time-sig)
-  (let* ((sigs (map calculate-time-fraction time-sig)))
-    (let add-moment ((moment ZERO-MOMENT)
-                     (remaining sigs))
-      (if (pair? remaining)
-        (add-moment (ly:moment-add moment (car remaining)) (cdr remaining))
-        moment))))
-
-#(define-public (calculate-compound-measure-length time-sig)
-  (cond
-    ((not (pair? time-sig)) (ly:make-moment 4 4))
-    ((pair? (car time-sig)) (calculate-complex-compound-time time-sig))
-    (else (calculate-time-fraction time-sig))))
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% Base beat lenth
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-#(define-public (calculate-compound-base-beat-full time-sig)
-  (let* ((den (map last time-sig)))
-    (apply max den)))
-
-#(define-public (calculate-compound-base-beat time-sig)
-  (ly:make-moment 1 (cond
-    ((not (pair? time-sig)) 4)
-    ((pair? (car time-sig)) (calculate-compound-base-beat-full time-sig))
-    (else (calculate-compound-base-beat-full (list time-sig))))))
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% The music function to set the complex time signature
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-compoundMeter =
-#(define-music-function (parser location args) (pair?)
-  (let ((mlen (calculate-compound-measure-length args))
-        (beat (calculate-compound-base-beat args)))
-  #{
-\once \override Staff.TimeSignature #'stencil = #ly:text-interface::print
-\once \override Staff.TimeSignature #'text = #(format-compound-time $args)
-% \set Staff.beatGrouping = #(reverse (cdr (reverse $args)))
-\set Timing.measureLength = $mlen
-\set Timing.timeSignatureFraction = #(cons (ly:moment-main-numerator $mlen)
-                                           (ly:moment-main-denominator $mlen))
-\set Timing.beatLength = $beat
-
-% TODO: Implement beatGrouping and auto-beam-settings!!!
-#} ))
-"""
 }
 
 def round_to_two_digits (val):
@@ -342,6 +242,13 @@ class PartGroupInfo:
         error_message (_ ("Unprocessed PartGroupInfo %s encountered") % self)
         return ''
 
+def musicxml_step_to_lily (step):
+    if step:
+        return (ord (step) - ord ('A') + 7 - 2) % 7
+    else:
+        return None
+
+
 def staff_attributes_to_string_tunings (mxl_attr):
     details = mxl_attr.get_maybe_exist_named_child ('staff-details')
     if not details:
@@ -351,7 +258,7 @@ def staff_attributes_to_string_tunings (mxl_attr):
     if staff_lines:
         lines = string.atoi (staff_lines.get_text ())
 
-    tunings = [0]*lines
+    tunings = [musicexp.Pitch()]*lines
     staff_tunings = details.get_named_children ('staff-tuning')
     for i in staff_tunings:
         p = musicexp.Pitch()
@@ -401,6 +308,8 @@ def staff_attributes_to_lily_staff (mxl_attr):
         if staff_lines:
             lines = string.atoi (staff_lines.get_text ())
 
+    # TODO: Handle other staff attributes like staff-space, etc.
+
     staff = None
     if clef_sign == "percussion" and lines == 1:
         staff = musicexp.RhythmicStaff ()
@@ -412,8 +321,11 @@ def staff_attributes_to_lily_staff (mxl_attr):
         staff.string_tunings = staff_attributes_to_string_tunings (attributes)
         # staff.tablature_format = ???
     else:
-        # TODO: Handle case with lines <> 5!
         staff = musicexp.Staff ()
+        # TODO: Handle case with lines <> 5!
+        if (lines != 5):
+            staff.add_context_modification ("\\override StaffSymbol #'line-count = #%s" % lines)
+
 
     return staff
 
@@ -827,8 +739,6 @@ def musicxml_time_to_lily (attributes):
         return None
     change = musicexp.TimeSignatureChange()
     change.fractions = sig
-    if (len(sig) != 2) or isinstance (sig[0], list):
-        needed_additional_definitions.append ("compound-time-signature")
 
     time_elm = attributes.get_maybe_exist_named_child ('time')
     if time_elm and hasattr (time_elm, 'symbol'):
@@ -891,7 +801,12 @@ def musicxml_key_to_lily (attributes):
 
     else:
         # Non-standard key signature of the form [[step,alter<,octave>],...]
-        change.non_standard_alterations = key_sig
+        # MusicXML contains C,D,E,F,G,A,B as steps, lily uses 0-7, so convert
+        alterations = []
+        for k in key_sig:
+            k[0] = musicxml_step_to_lily (k[0])
+            alterations.append (k)
+        change.non_standard_alterations = alterations
     return change
 
 def musicxml_transpose_to_lily (attributes):
@@ -926,6 +841,22 @@ def musicxml_transpose_to_lily (attributes):
     transposition.pitch = musicexp.Pitch ().transposed (shift)
     return transposition
 
+def musicxml_staff_details_to_lily (attributes):
+    details = attributes.get_maybe_exist_named_child ('staff-details')
+    if not details:
+        return None
+
+    ## TODO: Handle staff-type, staff-lines, staff-tuning, capo, staff-size
+    ret = []
+
+    stafflines = details.get_maybe_exist_named_child ('staff-lines')
+    if stafflines:
+        lines = string.atoi (stafflines.get_text ());
+        lines_event = musicexp.StaffLinesEvent (lines);
+        ret.append (lines_event);
+
+    return ret;
+
 
 def musicxml_attributes_to_lily (attrs):
     elts = []
@@ -934,12 +865,16 @@ def musicxml_attributes_to_lily (attrs):
         'time': musicxml_time_to_lily,
         'key': musicxml_key_to_lily,
         'transpose': musicxml_transpose_to_lily,
+        'staff-details': musicxml_staff_details_to_lily,
     }
     for (k, func) in attr_dispatch.items ():
         children = attrs.get_named_children (k)
         if children:
             ev = func (attrs)
-            if ev:
+            if isinstance (ev, list):
+              for e in ev:
+                elts.append (e)
+            elif ev:
                 elts.append (ev)
 
     return elts
@@ -1384,7 +1319,7 @@ def musicxml_accordion_to_markup (mxl_event):
     if high:
         commandname += "H"
         command += """\\combine
-          \\raise #2.5 \\musicglyph #\"accordion.accDot\"
+          \\raise #2.5 \\musicglyph #\"accordion.dot\"
           """
     middle = mxl_event.get_maybe_exist_named_child ('accordion-middle')
     if middle:
@@ -1398,32 +1333,32 @@ def musicxml_accordion_to_markup (mxl_event):
         if txt == 3:
             commandname += "MMM"
             command += """\\combine
-          \\raise #1.5 \\musicglyph #\"accordion.accDot\"
+          \\raise #1.5 \\musicglyph #\"accordion.dot\"
           \\combine
-          \\raise #1.5 \\translate #(cons 1 0) \\musicglyph #\"accordion.accDot\"
+          \\raise #1.5 \\translate #(cons 1 0) \\musicglyph #\"accordion.dot\"
           \\combine
-          \\raise #1.5 \\translate #(cons -1 0) \\musicglyph #\"accordion.accDot\"
+          \\raise #1.5 \\translate #(cons -1 0) \\musicglyph #\"accordion.dot\"
           """
         elif txt == 2:
             commandname += "MM"
             command += """\\combine
-          \\raise #1.5 \\translate #(cons 0.5 0) \\musicglyph #\"accordion.accDot\"
+          \\raise #1.5 \\translate #(cons 0.5 0) \\musicglyph #\"accordion.dot\"
           \\combine
-          \\raise #1.5 \\translate #(cons -0.5 0) \\musicglyph #\"accordion.accDot\"
+          \\raise #1.5 \\translate #(cons -0.5 0) \\musicglyph #\"accordion.dot\"
           """
         elif not txt <= 0:
             commandname += "M"
             command += """\\combine
-          \\raise #1.5 \\musicglyph #\"accordion.accDot\"
+          \\raise #1.5 \\musicglyph #\"accordion.dot\"
           """
     low = mxl_event.get_maybe_exist_named_child ('accordion-low')
     if low:
         commandname += "L"
         command += """\\combine
-          \\raise #0.5 \musicglyph #\"accordion.accDot\"
+          \\raise #0.5 \musicglyph #\"accordion.dot\"
           """
 
-    command += "\musicglyph #\"accordion.accDiscant\""
+    command += "\musicglyph #\"accordion.discant\""
     command = "\\markup { \\normalsize %s }" % command
     # Define the newly built command \accReg[H][MMM][L]
     additional_definitions[commandname] = "%s = %s" % (commandname, command)
@@ -1574,6 +1509,7 @@ def musicxml_direction_to_lily (n):
         if ev:
             # TODO: set the correct direction! Unfortunately, \mark in ly does
             #       not seem to support directions!
+            ev.force_direction = dir
             res.append (ev)
             continue
 
@@ -1842,14 +1778,16 @@ def musicxml_note_to_lily_main_event (n):
         acc = n.get_maybe_exist_named_child ('accidental')
         if acc:
             # let's not force accs everywhere.
-            event.cautionary = acc.editorial
+            event.cautionary = acc.cautionary
+            # TODO: Handle editorial accidentals
+            # TODO: Handle the level-display setting for displaying brackets/parentheses
 
     elif n.get_maybe_exist_typed_child (musicxml.Unpitched):
-       # Unpitched elements have display-step and can also have
-       # display-octave.
-       unpitched = n.get_maybe_exist_typed_child (musicxml.Unpitched)
-       event = musicexp.NoteEvent ()
-       event.pitch = musicxml_unpitched_to_lily (unpitched)
+        # Unpitched elements have display-step and can also have
+        # display-octave.
+        unpitched = n.get_maybe_exist_typed_child (musicxml.Unpitched)
+        event = musicexp.NoteEvent ()
+        event.pitch = musicxml_unpitched_to_lily (unpitched)
 
     elif n.get_maybe_exist_typed_child (musicxml.Rest):
         # rests can have display-octave and display-step, which are
@@ -2088,12 +2026,6 @@ class VoiceData:
         self.lyrics_dict = {}
         self.lyrics_order = []
 
-def musicxml_step_to_lily (step):
-    if step:
-       return (ord (step) - ord ('A') + 7 - 2) % 7
-    else:
-       return None
-
 def measure_length_from_attributes (attr, current_measure_length):
     len = attr.get_measure_length ()
     if not len:
@@ -2558,11 +2490,11 @@ def musicxml_unpitched_to_lily (mxl_unpitched):
     p = None
     step = mxl_unpitched.get_step ()
     if step:
-       p = musicexp.Pitch ()
-       p.step = musicxml_step_to_lily (step)
+        p = musicexp.Pitch ()
+        p.step = musicxml_step_to_lily (step)
     octave = mxl_unpitched.get_octave ()
     if octave and p:
-       p.octave = octave - 4
+        p.octave = octave - 4
     return p
 
 def musicxml_restdisplay_to_lily (mxl_rest):
@@ -2632,7 +2564,7 @@ If the given filename is -, musicxml2ly reads from the command line.
 
     p.version = ('''%prog (LilyPond) @TOPLEVEL_VERSION@\n\n'''
 +
-_ ("""Copyright (c) 2005--2010 by
+_ ("""Copyright (c) 2005--2011 by
     Han-Wen Nienhuys <hanwen@xs4all.nl>,
     Jan Nieuwenhuizen <janneke@gnu.org> and
     Reinhold Kainhofer <reinhold@kainhofer.com>
@@ -2679,7 +2611,7 @@ information.""") % 'lilypond')
     p.add_option ('-l', '--language',
                   metavar = _ ("LANG"),
                   action = "store",
-                  help = _ ("use a different language file 'LANG.ly' and corresponding pitch names, e.g. 'deutsch' for deutsch.ly"))
+                  help = _ ("use LANG for pitch names, e.g. 'deutsch' for note names in German"))
 
     p.add_option ('--nd', '--no-articulation-directions',
                   action = "store_false",
@@ -2857,7 +2789,17 @@ def read_musicxml (filename, compressed, use_lxml):
     if compressed:
         if filename == "-":
              progress (_ ("Input is compressed, extracting raw MusicXML data from stdin") )
-             z = zipfile.ZipFile (sys.stdin)
+             # unfortunately, zipfile.ZipFile can't read directly from
+             # stdin, so copy everything from stdin to a temp file and read
+             # that. TemporaryFile() will remove the file when it is closed.
+             tmp = tempfile.TemporaryFile()
+             sys.stdin = os.fdopen(sys.stdin.fileno(), 'rb', 0) # Make sys.stdin binary
+             bytes_read = sys.stdin.read (8192)
+             while bytes_read:
+                 for b in bytes_read:
+                     tmp.write(b)
+                 bytes_read = sys.stdin.read (8192)
+             z = zipfile.ZipFile (tmp, "r")
         else:
             progress (_ ("Input file %s is compressed, extracting raw MusicXML data") % filename)
             z = zipfile.ZipFile (filename, "r")
@@ -2972,14 +2914,14 @@ def main ():
     if options.language:
         musicexp.set_pitch_language (options.language)
         needed_additional_definitions.append (options.language)
-        additional_definitions[options.language] = "\\include \"%s.ly\"\n" % options.language
+        additional_definitions[options.language] = "\\language \"%s\"\n" % options.language
     conversion_settings.ignore_beaming = not options.convert_beaming
     conversion_settings.convert_page_layout = options.convert_page_layout
 
     # Allow the user to leave out the .xml or xml on the filename
     basefilename = args[0].decode('utf-8')
     if basefilename == "-": # Read from stdin
-        basefilename = "-"
+        filename = "-"
     else:
         filename = get_existing_filename_with_extension (basefilename, "xml")
         if not filename: