import string
import codecs
import zipfile
+import tempfile
import StringIO
"""
from rational import Rational
-# Store command-line options in a global variable, so we can access them everythwere
+# Store command-line options in a global variable, so we can access them everywhere
options = None
class Conversion_Settings:
conversion_settings = Conversion_Settings ()
# Use a global variable to store the setting needed inside a \layout block.
-# whenever we need to change a setting or add/remove an engraver, we can access
+# whenever we need to change a setting or add/remove an engraver, we can access
# this layout and add the corresponding settings
layout_information = musicexp.Layout ()
-def progress (str):
- ly.stderr_write (str + '\n')
- sys.stderr.flush ()
-
-def error_message (str):
- ly.stderr_write (str + '\n')
- sys.stderr.flush ()
-
needed_additional_definitions = []
additional_definitions = {
- "snappizzicato": """#(define-markup-command (snappizzicato layout props) ()
- (interpret-markup layout props
- (markup #:stencil
- (ly:stencil-translate-axis
- (ly:stencil-add
- (make-circle-stencil 0.7 0.1 #f)
- (ly:make-stencil
- (list 'draw-line 0.1 0 0.1 0 1)
- '(-0.1 . 0.1) '(0.1 . 1)))
- 0.7 X))))""",
-
- "eyeglasses": """eyeglassesps = #"0.15 setlinewidth
- -0.9 0 translate
- 1.1 1.1 scale
- 1.2 0.7 moveto
- 0.7 0.7 0.5 0 361 arc
- stroke
- 2.20 0.70 0.50 0 361 arc
- stroke
- 1.45 0.85 0.30 0 180 arc
- stroke
- 0.20 0.70 moveto
- 0.80 2.00 lineto
- 0.92 2.26 1.30 2.40 1.15 1.70 curveto
- stroke
- 2.70 0.70 moveto
- 3.30 2.00 lineto
- 3.42 2.26 3.80 2.40 3.65 1.70 curveto
- stroke"
-eyeglasses = \markup { \with-dimensions #'(0 . 4.4) #'(0 . 2.5) \postscript #eyeglassesps }""",
-
- "tuplet-note-wrapper": """ % a formatter function, which is simply a wrapper around an existing
+ "tuplet-note-wrapper": """ % a formatter function, which is simply a wrapper around an existing
% tuplet formatter function. It takes the value returned by the given
- % function and appends a note of given length.
+ % function and appends a note of given length.
#(define-public ((tuplet-number::append-note-wrapper function note) grob)
(let* ((txt (if function (function grob) #f)))
- (if txt
+ (if txt
(markup txt #:fontsize -5 #:note note UP)
(markup #:fontsize -5 #:note note UP)
)
)""",
"tuplet-non-default-denominator": """#(define ((tuplet-number::non-default-tuplet-denominator-text denominator) grob)
- (number->string (if denominator
- denominator
+ (number->string (if denominator
+ denominator
(ly:event-property (event-cause grob) 'denominator))))
""",
(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):
work = tree.get_maybe_exist_named_child ('work')
if work:
- set_if_exists ('title', work.get_work_title ())
set_if_exists ('worknumber', work.get_work_number ())
set_if_exists ('opus', work.get_opus ())
- else:
- movement_title = tree.get_maybe_exist_named_child ('movement-title')
- if movement_title:
+
+ movement_title = tree.get_maybe_exist_named_child ('movement-title')
+
+ # use either work-title or movement-title as title.
+ # if both exist use movement-title as subtitle.
+ # if there is only a movement-title (or work-title is empty or missing) the movement-title should be typeset as a title
+ if work:
+ work_title = work.get_work_title ()
+ set_if_exists ('title', work_title)
+ if work_title == '':
set_if_exists ('title', movement_title.get_text ())
-
+ elif movement_title:
+ set_if_exists ('subtitle', movement_title.get_text ())
+ elif movement_title:
+ set_if_exists ('title', movement_title.get_text ())
+
identifications = tree.get_named_children ('identification')
for ids in identifications:
set_if_exists ('copyright', ids.get_rights ())
set_if_exists ('arranger', ids.get_arranger ())
set_if_exists ('editor', ids.get_editor ())
set_if_exists ('poet', ids.get_poet ())
-
- set_if_exists ('tagline', ids.get_encoding_software ())
+
set_if_exists ('encodingsoftware', ids.get_encoding_software ())
set_if_exists ('encodingdate', ids.get_encoding_date ())
set_if_exists ('encoder', ids.get_encoding_person ())
set_if_exists ('encodingdescription', ids.get_encoding_description ())
-
+
+ set_if_exists ('source', ids.get_source ())
+
+ # miscellaneous --> texidoc
set_if_exists ('texidoc', ids.get_file_description ());
# Finally, apply the required compatibility modes
- # Some applications created wrong MusicXML files, so we need to
+ # Some applications created wrong MusicXML files, so we need to
# apply some compatibility mode, e.g. ignoring some features/tags
# in those files
software = ids.get_encoding_software_list ()
# Case 1: "Sibelius 5.1" with the "Dolet 3.4 for Sibelius" plugin
# is missing all beam ends => ignore all beaming information
- if "Dolet 3.4 for Sibelius" in software:
- conversion_settings.ignore_beaming = True
- progress (_ ("Encountered file created by Dolet 3.4 for Sibelius, containing wrong beaming information. All beaming information in the MusicXML file will be ignored"))
- # ditto for Dolet 3.5
- if "Dolet 3.5 for Sibelius" in software:
- conversion_settings.ignore_beaming = True
- progress (_ ("Encountered file created by Dolet 3.5 for Sibelius, containing wrong beaming information. All beaming information in the MusicXML file will be ignored"))
- if "Noteworthy Composer" in software:
- conversion_settings.ignore_beaming = True
- progress (_ ("Encountered file created by Noteworthy Composer's nwc2xml, containing wrong beaming information. All beaming information in the MusicXML file will be ignored"))
- # TODO: Check for other unsupported features
-
+ ignore_beaming_software = {
+ "Dolet 4 for Sibelius, Beta 2": "Dolet 4 for Sibelius, Beta 2",
+ "Dolet 3.5 for Sibelius": "Dolet 3.5 for Sibelius",
+ "Dolet 3.4 for Sibelius": "Dolet 3.4 for Sibelius",
+ "Dolet 3.3 for Sibelius": "Dolet 3.3 for Sibelius",
+ "Dolet 3.2 for Sibelius": "Dolet 3.2 for Sibelius",
+ "Dolet 3.1 for Sibelius": "Dolet 3.1 for Sibelius",
+ "Dolet for Sibelius 1.3": "Dolet for Sibelius 1.3",
+ "Noteworthy Composer": "Noteworthy Composer's nwc2xm[",
+ }
+ for s in software:
+ app_description = ignore_beaming_software.get (s, False);
+ if app_description:
+ conversion_settings.ignore_beaming = True
+ ly.warning (_ ("Encountered file created by %s, containing "
+ "wrong beaming information. All beaming "
+ "information in the MusicXML file will be "
+ "ignored") % app_description)
+
+ # TODO: Check for other unsupported features
return header
class PartGroupInfo:
def add_end (self, g):
self.end[getattr (g, 'number', "1")] = g
def print_ly (self, printer):
- error_message (_ ("Unprocessed PartGroupInfo %s encountered") % self)
+ ly.warning (_ ("Unprocessed PartGroupInfo %s encountered") % self)
def ly_expression (self):
- error_message (_ ("Unprocessed PartGroupInfo %s encountered") % self)
+ ly.warning (_ ("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:
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()
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 ()
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
score = musicexp.Score ()
structure = musicexp.StaffGroup (None)
score.set_contents (structure)
-
+
if not part_list:
return structure
# Finale gives unnamed parts the name "MusicXML Part" automatically!
if partname and partname.get_text() != "MusicXML Part":
staff.instrument_name = partname.get_text ()
- if el.get_maybe_exist_named_child ('part-abbreviation'):
- staff.short_instrument_name = el.get_maybe_exist_named_child ('part-abbreviation').get_text ()
+ # part-name-display overrides part-name!
+ partname = el.get_maybe_exist_named_child ("part-name-display")
+ if partname:
+ staff.instrument_name = extract_display_text (partname)
+
+ partdisplay = el.get_maybe_exist_named_child ('part-abbreviation')
+ if partdisplay:
+ staff.short_instrument_name = partdisplay.get_text ()
+ # part-abbreviation-display overrides part-abbreviation!
+ partdisplay = el.get_maybe_exist_named_child ("part-abbreviation-display")
+ if partdisplay:
+ staff.short_instrument_name = extract_display_text (partdisplay)
# TODO: Read in the MIDI device / instrument
+
return staff
def read_score_group (el):
group_starts.append (pos)
pos += 1
- if len (staves) == 1:
- return staves[0]
for i in staves:
structure.append_staff (i)
return score
d = musicexp.Duration ()
d.duration_log = dur[0]
d.dots = dur[1]
- # Grace notes by specification have duration 0, so no time modification
+ # Grace notes by specification have duration 0, so no time modification
# factor is possible. It even messes up the output with *0/1
if not mxl_note.get_maybe_exist_typed_child (musicxml.Grace):
d.factor = mxl_note._duration / d.get_length ()
d_log = {1: 0, 2: 1, 4:2, 8:3, 16:4, 32:5, 64:6, 128:7, 256:8, 512:9}.get (rational_len.denominator (), -1)
# Duration of the form 1/2^n or 3/2^n can be converted to a simple lilypond duration
- if (d_log >= 0 and rational_len.numerator() in (1,3,5,7) ):
+ dots = {1: 0, 3: 1, 7: 2, 15: 3, 31: 4, 63: 5, 127: 6}.get (rational_len.numerator(), -1)
+ if ( d_log >= dots >= 0 ):
# account for the dots!
- d.dots = (rational_len.numerator()-1)/2
- d.duration_log = d_log - d.dots
+ d.duration_log = d_log - dots
+ d.dots = dots
elif (d_log >= 0):
d.duration_log = d_log
d.factor = Rational (rational_len.numerator ())
else:
- error_message (_ ("Encountered rational duration with denominator %s, "
+ ly.warning (_ ("Encountered rational duration with denominator %s, "
"unable to convert to lilypond duration") %
- rational_len.denominator ())
+ rational_len.denominator ())
# TODO: Test the above error message
return None
p.partial = rational_to_lily_duration (partial_len)
return p
else:
- return Null
+ return None
# Detect repeats and alternative endings in the chord event list (music_list)
# and convert them to the corresponding musicexp objects, containing nested
r.repeat_count = repeat_times
# don't erase the first element for "implicit" repeats (i.e. no
# starting repeat bars at the very beginning)
- start = repeat_start+1
+ start = repeat_start + 1
if repeat_start == music_start:
start = music_start
r.set_music (music_list[start:repeat_end])
for (start, end) in endings:
s = musicexp.SequentialMusic ()
- s.elements = music_list[start+1:end]
+ s.elements = music_list[start + 1:end]
r.add_ending (s)
- del music_list[repeat_start:final_marker+1]
+ del music_list[repeat_start:final_marker + 1]
music_list.insert (repeat_start, r)
repeat_replaced = True
pos += 1
return music_list
-# Extract the settings for tuplets from the <notations><tuplet> and the
+# Extract the settings for tuplets from the <notations><tuplet> and the
# <time-modification> elements of the note:
def musicxml_tuplet_to_lily (tuplet_elt, time_modification):
tsm = musicexp.TimeScaledMusic ()
- fraction = (1,1)
+ fraction = (1, 1)
if time_modification:
fraction = time_modification.get_fraction ()
tsm.numerator = fraction[0]
- tsm.denominator = fraction[1]
+ tsm.denominator = fraction[1]
normal_type = tuplet_elt.get_normal_type ()
actual_type = tuplet_elt.get_actual_type ()
if actual_type:
actual_note = musicexp.Duration ()
- (actual_note.duration_log, actual_note.dots) = normal_type
+ (actual_note.duration_log, actual_note.dots) = actual_type
tsm.actual_type = actual_note
# Obtain non-default nrs of notes from the tuplet object!
display_values = {"none": None, "actual": "actual", "both": "both"}
if hasattr (tuplet_elt, "show-number"):
tsm.display_number = display_values.get (getattr (tuplet_elt, "show-number"), "actual")
- if tsm.display_number == "actual" and tsm.display_denominator:
- needed_additional_definitions.append ("tuplet-non-default-denominator")
- elif tsm.display_number == "both" and (tsm.display_numerator or tsm.display_denominator):
- needed_additional_definitions.append ("tuplet-non-default-fraction")
if hasattr (tuplet_elt, "show-type"):
- if getattr (tuplet_elt, "show-type") == "actual":
- needed_additional_definitions.append ("tuplet-note-wrapper")
tsm.display_type = display_values.get (getattr (tuplet_elt, "show-type"), None)
return tsm
MUSIC_LIST demarcated by EVENTS_LIST in TimeScaledMusic objects.
"""
-
+
indices = []
brackets = {}
change = musicexp.ClefChange ()
(change.type, change.position, change.octave) = attributes.get_clef_information ()
return change
-
+
def musicxml_time_to_lily (attributes):
sig = attributes.get_time_signature ()
if not sig:
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'):
# TODO: Handle senza-misura measures
# TODO: Handle hidden time signatures (print-object="no")
- # TODO: What shall we do if the symbol clashes with the sig? e.g. "cut"
+ # TODO: What shall we do if the symbol clashes with the sig? e.g. "cut"
# with 3/8 or "single-number" with (2+3)/8 or 3/8+2/4?
return change
def musicxml_key_to_lily (attributes):
- key_sig = attributes.get_key_signature ()
+ key_sig = attributes.get_key_signature ()
if not key_sig or not (isinstance (key_sig, list) or isinstance (key_sig, tuple)):
- error_message (_ ("Unable to extract key signature!"))
+ ly.warning (_ ("Unable to extract key signature!"))
return None
-
+
change = musicexp.KeySignatureChange()
-
+
if len (key_sig) == 2 and not isinstance (key_sig[0], list):
# standard key signature, (fifths, mode)
(fifths, mode) = key_sig
change.mode = mode
- start_pitch = musicexp.Pitch ()
+ start_pitch = musicexp.Pitch ()
start_pitch.octave = 0
try:
- (n,a) = {
- 'major' : (0,0),
- 'minor' : (5,0),
- 'ionian' : (0,0),
- 'dorian' : (1,0),
- 'phrygian' : (2,0),
- 'lydian' : (3,0),
- 'mixolydian': (4,0),
- 'aeolian' : (5,0),
- 'locrian' : (6,0),
+ (n, a) = {
+ 'major' : (0, 0),
+ 'minor' : (5, 0),
+ 'ionian' : (0, 0),
+ 'dorian' : (1, 0),
+ 'phrygian' : (2, 0),
+ 'lydian' : (3, 0),
+ 'mixolydian': (4, 0),
+ 'aeolian' : (5, 0),
+ 'locrian' : (6, 0),
}[mode]
start_pitch.step = n
start_pitch.alteration = a
except KeyError:
- error_message (_ ("unknown mode %s, expecting 'major' or 'minor' "
+ ly.warning (_ ("unknown mode %s, expecting 'major' or 'minor' "
"or a church mode!") % mode)
fifth = musicexp.Pitch()
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):
chromatic_shift = string.atoi (transpose.get_named_child ('chromatic').get_text ())
chromatic_shift_normalized = chromatic_shift % 12;
(shift.step, shift.alteration) = [
- (0,0), (0,1), (1,0), (2,-1), (2,0),
- (3,0), (3,1), (4,0), (5,-1), (5,0),
- (6,-1), (6,0)][chromatic_shift_normalized];
-
+ (0, 0), (0, 1), (1, 0), (2, -1), (2, 0),
+ (3, 0), (3, 1), (4, 0), (5, -1), (5, 0),
+ (6, -1), (6, 0)][chromatic_shift_normalized];
+
shift.octave += (chromatic_shift - chromatic_shift_normalized) / 12
diatonic = transpose.get_maybe_exist_named_child ('diatonic')
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 = []
- attr_dispatch = {
+ attr_dispatch = {
'clef': musicxml_clef_to_lily,
'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
+
+def extract_display_text (el):
+ child = el.get_maybe_exist_named_child ("display-text")
+ if child:
+ return child.get_text ()
+ else:
+ return False
+
+
+def musicxml_print_to_lily (el):
+ # TODO: Implement other print attributes
+ # <!ELEMENT print (page-layout?, system-layout?, staff-layout*,
+ # measure-layout?, measure-numbering?, part-name-display?,
+ # part-abbreviation-display?)>
+ # <!ATTLIST print
+ # staff-spacing %tenths; #IMPLIED
+ # new-system %yes-no; #IMPLIED
+ # new-page %yes-no-number; #IMPLIED
+ # blank-page NMTOKEN #IMPLIED
+ # page-number CDATA #IMPLIED
+ # >
+ elts = []
+ if (hasattr (el, "new-system") and conversion_settings.convert_page_layout):
+ val = getattr (el, "new-system")
+ if (val == "yes"):
+ elts.append (musicexp.Break ("break"))
+ if (hasattr (el, "new-page") and conversion_settings.convert_page_layout):
+ val = getattr (el, "new-page")
+ if (val == "yes"):
+ elts.append (musicexp.Break ("pageBreak"))
+ child = el.get_maybe_exist_named_child ("part-name-display")
+ if child:
+ elts.append (musicexp.SetEvent ("Staff.instrumentName",
+ "\"%s\"" % extract_display_text (child)))
+ child = el.get_maybe_exist_named_child ("part-abbreviation-display")
+ if child:
+ elts.append (musicexp.SetEvent ("Staff.shortInstrumentName",
+ "\"%s\"" % extract_display_text (child)))
return elts
+
class Marker (musicexp.Music):
def __init__ (self):
self.direction = 0
self.event = None
def print_ly (self, printer):
- ly.stderr_write (_ ("Encountered unprocessed marker %s\n") % self)
+ ly.warning (_ ("Encountered unprocessed marker %s\n") % self)
pass
def ly_expression (self):
return ""
def musicxml_spanner_to_lily_event (mxl_event):
ev = None
-
+
name = mxl_event.get_name()
func = spanner_event_dict.get (name)
if func:
ev = func()
else:
- error_message (_ ('unknown span event %s') % mxl_event)
+ ly.warning (_ ('unknown span event %s') % mxl_event)
type = mxl_event.get_type ()
if span_direction != None:
ev.span_direction = span_direction
else:
- error_message (_ ('unknown span type %s for %s') % (type, name))
+ ly.warning (_ ('unknown span type %s for %s') % (type, name))
ev.set_span_type (type)
ev.line_type = getattr (mxl_event, 'line-type', 'solid')
ev.type = mxl_event.get_text ()
return ev
-def musicxml_snappizzicato_event (mxl_event):
- needed_additional_definitions.append ("snappizzicato")
- ev = musicexp.MarkupEvent ()
- ev.contents = "\\snappizzicato"
- return ev
-
def musicxml_string_event (mxl_event):
ev = musicexp.NoDirectionArticulationEvent ()
ev.type = mxl_event.get_text ()
#"schleifer": "?",
#"scoop": "?",
#"shake": "?",
- "snap-pizzicato": musicxml_snappizzicato_event,
+ "snap-pizzicato": "snappizzicato",
#"spiccato": "?",
"staccatissimo": (musicexp.ShortArticulationEvent, "|"), # or "staccatissimo"
"staccato": (musicexp.ShortArticulationEvent, "."), # or "staccato"
def musicxml_dynamics_to_lily_event (dynentry):
dynamics_available = (
- "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf",
+ "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf",
"f", "ff", "fff", "ffff", "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" )
dynamicsname = dynentry.get_name ()
if dynamicsname == "other-dynamics":
dynamicsname = dynentry.get_text ()
- if not dynamicsname or dynamicsname=="#text":
+ if not dynamicsname or dynamicsname == "#text":
return
if not dynamicsname in dynamics_available:
def hex_to_color (hex_val):
res = re.match (r'#([0-9a-f][0-9a-f]|)([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$', hex_val, re.IGNORECASE)
if res:
- return map (lambda x: hexcolorval_to_nr (x), res.group (2,3,4))
+ return map (lambda x: hexcolorval_to_nr (x), res.group (2, 3, 4))
else:
return None
# TODO: How should I best convert the font-family attribute?
# TODO: How can I represent the underline, overline and line-through
- # attributes in Lilypond? Values of these attributes indicate
+ # attributes in LilyPond? Values of these attributes indicate
# the number of lines
return 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:
- # By default, use one dot (when no or invalid content is given). The
+ # By default, use one dot (when no or invalid content is given). The
# MusicXML spec is quiet about this case...
txt = 1
try:
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)
def musicxml_eyeglasses_to_ly (mxl_event):
needed_additional_definitions.append ("eyeglasses")
- return musicexp.MarkEvent ("\\eyeglasses")
+ return musicexp.MarkEvent ("\\markup { \\eyeglasses }")
def next_non_hash_index (lst, pos):
pos += 1
index = -1
index = next_non_hash_index (children, index)
- if isinstance (children[index], musicxml.BeatUnit):
+ if isinstance (children[index], musicxml.BeatUnit):
# first form of metronome-mark, using unit and beats/min or other unit
ev = musicexp.TempoMark ()
if hasattr (mxl_event, 'parentheses'):
except ValueError:
pass
else:
- error_message (_ ("Unknown metronome mark, ignoring"))
+ ly.warning (_ ("Unknown metronome mark, ignoring"))
return
return ev
else:
#TODO: Implement the other (more complex) way for tempo marks!
- error_message (_ ("Metronome marks with complex relations (<metronome-note> in MusicXML) are not yet implemented."))
+ ly.warning (_ ("Metronome marks with complex relations (<metronome-note> in MusicXML) are not yet implemented."))
return
# translate directions into Events, possible values:
if hasattr (n, 'placement') and options.convert_directions:
dir = musicxml_direction_to_indicator (n.placement)
dirtype_children = []
- # TODO: The direction-type is used for grouping (e.g. dynamics with text),
+ # TODO: The direction-type is used for grouping (e.g. dynamics with text),
# so we can't simply flatten them out!
for dt in n.get_typed_children (musicxml.DirType):
dirtype_children += dt.get_all_children ()
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
'diminished': 'dim5',
# Sevenths:
'dominant': '7',
+ 'dominant-seventh': '7',
'major-seventh': 'maj7',
'minor-seventh': 'm7',
'diminished-seventh': 'dim7',
res = chordkind_dict.get (kind, None)
# Check for None, since a major chord is converted to ''
if res == None:
- error_message (_ ("Unable to convert chord type %s to lilypond.") % kind)
+ ly.warning (_ ("Unable to convert chord type %s to lilypond.") % kind)
return res
def musicxml_harmony_to_lily_chordname (n):
ev.bass = musicxml_chordpitch_to_lily (bass)
inversion = n.get_maybe_exist_named_child ('inversion')
if inversion:
- # TODO: Lilypond does not support inversions, does it?
+ # TODO: LilyPond does not support inversions, does it?
# Mail from Carl Sorensen on lilypond-devel, June 11, 2008:
- # 4. LilyPond supports the first inversion in the form of added
- # bass notes. So the first inversion of C major would be c:/g.
- # To get the second inversion of C major, you would need to do
- # e:6-3-^5 or e:m6-^5. However, both of these techniques
- # require you to know the chord and calculate either the fifth
- # pitch (for the first inversion) or the third pitch (for the
+ # 4. LilyPond supports the first inversion in the form of added
+ # bass notes. So the first inversion of C major would be c:/g.
+ # To get the second inversion of C major, you would need to do
+ # e:6-3-^5 or e:m6-^5. However, both of these techniques
+ # require you to know the chord and calculate either the fifth
+ # pitch (for the first inversion) or the third pitch (for the
# second inversion) so they may not be helpful for musicxml2ly.
inversion_count = string.atoi (inversion.get_text ())
if inversion_count == 1:
d.step = deg.get_value ()
d.alteration = deg.get_alter ()
ev.add_modification (d)
- #TODO: convert the user-symbols attribute:
+ #TODO: convert the user-symbols attribute:
#major: a triangle, like Unicode 25B3
#minor: -, like Unicode 002D
#augmented: +, like Unicode 002B
def musicxml_figured_bass_note_to_lily (n):
res = musicexp.FiguredBassNote ()
- suffix_dict = { 'sharp' : "+",
- 'flat' : "-",
- 'natural' : "!",
- 'double-sharp' : "++",
- 'flat-flat' : "--",
- 'sharp-sharp' : "++",
+ suffix_dict = { 'sharp' : "+",
+ 'flat' : "-",
+ 'natural' : "!",
+ 'double-sharp' : "++",
+ 'flat-flat' : "--",
+ 'sharp-sharp' : "++",
'slash' : "/" }
prefix = n.get_maybe_exist_named_child ('prefix')
if prefix:
if suffix:
res.set_suffix (suffix_dict.get (suffix.get_text (), ""))
if n.get_maybe_exist_named_child ('extend'):
- # TODO: Implement extender lines (unfortunately, in lilypond you have
+ # TODO: Implement extender lines (unfortunately, in lilypond you have
# to use \set useBassFigureExtenders = ##t, which turns them on
# globally, while MusicXML has a property for each note...
# I'm not sure there is a proper way to implement this cleanly
dur = n.get_maybe_exist_named_child ('duration')
if dur:
# apply the duration to res
- length = Rational(int(dur.get_text()), n._divisions)*Rational(1,4)
+ length = Rational(int(dur.get_text()), n._divisions) * Rational(1, 4)
res.set_real_duration (length)
duration = rational_to_lily_duration (length)
if duration:
}
def musicxml_note_to_lily_main_event (n):
- pitch = None
+ pitch = None
duration = None
event = None
acc = n.get_maybe_exist_named_child ('accidental')
if acc:
- # let's not force accs everywhere.
- event.cautionary = acc.editorial
+ # AccidentalCautionary in lily has parentheses
+ # so treat accidental explicitly in parentheses as cautionary
+ if hasattr(acc, 'parentheses') and acc.parentheses == "yes":
+ event.cautionary = True
+ else:
+ 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
# treated like an ordinary note pitch
self.end_moment = self.begin_moment + duration
def current_duration (self):
return self.end_moment - self.begin_moment
-
- def add_music (self, music, duration):
+
+ def add_music (self, music, duration, relevant=True):
assert isinstance (music, musicexp.Music)
if self.pending_multibar > Rational (0):
self._insert_multibar ()
- self.has_relevant_elements = True
+ self.has_relevant_elements = self.has_relevant_elements or relevant
self.elements.append (music)
self.begin_moment = self.end_moment
self.set_duration (duration)
-
+
# Insert all pending dynamics right after the note/rest:
if isinstance (music, musicexp.ChordEvent) and self.pending_dynamics:
for d in self.pending_dynamics:
self.pending_dynamics = []
# Insert some music command that does not affect the position in the measure
- def add_command (self, command):
+ def add_command (self, command, relevant=True):
assert isinstance (command, musicexp.Music)
if self.pending_multibar > Rational (0):
self._insert_multibar ()
- self.has_relevant_elements = True
+ self.has_relevant_elements = self.has_relevant_elements or relevant
self.elements.append (command)
- def add_barline (self, barline):
- # TODO: Implement merging of default barline and custom bar line
- self.add_music (barline, Rational (0))
+ def add_barline (self, barline, relevant=False):
+ # Insert only if we don't have a barline already
+ # TODO: Implement proper merging of default barline and custom bar line
+ has_relevant = self.has_relevant_elements
+ if (not (self.elements) or
+ not (isinstance (self.elements[-1], musicexp.BarLine)) or
+ (self.pending_multibar > Rational (0))):
+ self.add_music (barline, Rational (0))
+ self.has_relevant_elements = has_relevant or relevant
def add_partial (self, command):
self.ignore_skips = True
+ # insert the partial, but restore relevant_elements (partial is not relevant)
+ relevant = self.has_relevant_elements
self.add_command (command)
+ self.has_relevant_elements = relevant
def add_dynamics (self, dynamic):
# store the dynamic item(s) until we encounter the next note/rest:
def add_bar_check (self, number):
# re/store has_relevant_elements, so that a barline alone does not
# trigger output for figured bass, chord names
- has_relevant = self.has_relevant_elements
b = musicexp.BarLine ()
b.bar_number = number
self.add_barline (b)
- self.has_relevant_elements = has_relevant
def jumpto (self, moment):
current_end = self.end_moment + self.pending_multibar
diff = moment - current_end
-
+
if diff < Rational (0):
- error_message (_ ('Negative skip %s (from position %s to %s)') %
- (diff, current_end, moment))
+ ly.warning (_ ('Negative skip %s (from position %s to %s)')
+ % (diff, current_end, moment))
diff = Rational (0)
if diff > Rational (0) and not (self.ignore_skips and moment == 0):
duration_factor = 1
duration_log = {1: 0, 2: 1, 4:2, 8:3, 16:4, 32:5, 64:6, 128:7, 256:8, 512:9}.get (diff.denominator (), -1)
duration_dots = 0
- # TODO: Use the time signature for skips, too. Problem: The skip
+ # TODO: Use the time signature for skips, too. Problem: The skip
# might not start at a measure boundary!
if duration_log > 0: # denominator is a power of 2...
if diff.numerator () == 3:
evc = musicexp.ChordEvent ()
evc.elements.append (skip)
- self.add_music (evc, diff)
+ self.add_music (evc, diff, False)
if diff > Rational (0) and moment == 0:
self.ignore_skips = False
value = None
# if the position matches, find the last ChordEvent, do not cross a bar line!
- at = len( self.elements ) - 1
+ at = len(self.elements) - 1
while (at >= 0 and
not isinstance (self.elements[at], musicexp.ChordEvent) and
not isinstance (self.elements[at], musicexp.BarLine)):
self.jumpto (starting_at)
value = None
return value
-
+
def correct_negative_skip (self, goto):
self.end_moment = goto
self.begin_moment = goto
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:
lyrics = {}
return_value = VoiceData ()
return_value.voicedata = voice
-
+
# First pitch needed for relative mode (if selected in command-line options)
first_pitch = None
ignore_lyrics = False
current_staff = None
-
+
pending_figured_bass = []
pending_chordnames = []
voice_builder.set_measure_length (current_measure_length)
for n in voice._elements:
+ tie_started = False
if n.get_name () == 'forward':
continue
staff = n.get_maybe_exist_named_child ('staff')
a = musicxml_partial_to_lily (n.partial)
if a:
voice_builder.add_partial (a)
+ figured_bass_builder.add_partial (a)
+ chordnames_builder.add_partial (a)
continue
is_chord = n.get_maybe_exist_named_child ('chord')
if not is_chord and not is_after_grace:
try:
voice_builder.jumpto (n._when)
+ figured_bass_builder.jumpto (n._when)
+ chordnames_builder.jumpto (n._when)
except NegativeSkip, neg:
voice_builder.correct_negative_skip (n._when)
+ figured_bass_builder.correct_negative_skip (n._when)
+ chordnames_builder.correct_negative_skip (n._when)
n.message (_ ("Negative skip found: from %s to %s, difference is %s") % (neg.here, neg.dest, neg.dest - neg.here))
if isinstance (n, musicxml.Barline):
for a in barlines:
if isinstance (a, musicexp.BarLine):
voice_builder.add_barline (a)
+ figured_bass_builder.add_barline (a, False)
+ chordnames_builder.add_barline (a, False)
elif isinstance (a, RepeatMarker) or isinstance (a, EndingMarker):
voice_builder.add_command (a)
+ figured_bass_builder.add_barline (a, False)
+ chordnames_builder.add_barline (a, False)
+ continue
+
+
+ if isinstance (n, musicxml.Print):
+ for a in musicxml_print_to_lily (n):
+ voice_builder.add_command (a, False)
continue
# Continue any multimeasure-rests before trying to add bar checks!
if not n.__class__.__name__ == 'Note':
n.message (_ ('unexpected %s; expected %s or %s or %s') % (n, 'Note', 'Attributes', 'Barline'))
continue
-
+
main_event = musicxml_note_to_lily_main_event (n)
if main_event and not first_pitch:
first_pitch = main_event.pitch
modes_found['drummode'] = True
ev_chord = voice_builder.last_event_chord (n._when)
- if not ev_chord:
+ if not ev_chord:
ev_chord = musicexp.ChordEvent()
voice_builder.add_music (ev_chord, n._duration)
grace_chord = None
# after-graces and other graces use different lists; Depending on
- # whether we have a chord or not, obtain either a new ChordEvent or
+ # whether we have a chord or not, obtain either a new ChordEvent or
# the previous one to create a chord
if is_after_grace:
if ev_chord.after_grace_elements and n.get_maybe_exist_typed_child (musicxml.Chord):
# with duration 0. The following correct this when we hit the real note!
if voice_builder.current_duration () == 0 and n._duration > 0:
voice_builder.set_duration (n._duration)
-
+
# if we have a figured bass, set its voice builder to the correct position
# and insert the pending figures
if pending_figured_bass:
fb.duration = ev_chord.get_duration ()
figured_bass_builder.add_music (fb, dur)
pending_figured_bass = []
-
+
if pending_chordnames:
try:
chordnames_builder.jumpto (n._when)
span_events = []
# The <notation> element can have the following children (+ means implemented, ~ partially, - not):
- # +tied | +slur | +tuplet | glissando | slide |
+ # +tied | +slur | +tuplet | glissando | slide |
# ornaments | technical | articulations | dynamics |
- # +fermata | arpeggiate | non-arpeggiate |
+ # +fermata | arpeggiate | non-arpeggiate |
# accidental-mark | other-notation
for notations in notations_children:
for tuplet_event in notations.get_tuplets():
if mxl_tie and mxl_tie.type == 'start':
ev_chord.append (musicexp.TieEvent ())
is_tied = True
+ tie_started = True
else:
is_tied = False
fermatas = notations.get_named_children ('fermata')
for a in fermatas:
ev = musicxml_fermata_to_lily_event (a)
- if ev:
+ if ev:
ev_chord.append (ev)
arpeggiate = notations.get_named_children ('arpeggiate')
# Articulations can contain the following child elements:
# accent | strong-accent | staccato | tenuto |
# detached-legato | staccatissimo | spiccato |
- # scoop | plop | doit | falloff | breath-mark |
+ # scoop | plop | doit | falloff | breath-mark |
# caesura | stress | unstress
# Technical can contain the following child elements:
# up-bow | down-bow | harmonic | open-string |
# toe | fingernails | other-technical
# Ornaments can contain the following child elements:
# trill-mark | turn | delayed-turn | inverted-turn |
- # shake | wavy-line | mordent | inverted-mordent |
+ # shake | wavy-line | mordent | inverted-mordent |
# schleifer | tremolo | other-ornament, accidental-mark
ornaments = notations.get_named_children ('ornaments')
ornaments += notations.get_named_children ('articulations')
mxl_beams = [b for b in n.get_named_children ('beam')
if (b.get_type () in ('begin', 'end')
- and b.is_primary ())]
+ and b.is_primary ())]
if mxl_beams and not conversion_settings.ignore_beaming:
beam_ev = musicxml_spanner_to_lily_event (mxl_beams[0])
if beam_ev:
if not lnr in note_lyrics_processed:
lyrics[lnr].append ("\skip4")
- ## force trailing mm rests to be written out.
+ # Assume that a <tie> element only lasts for one note.
+ # This might not be correct MusicXML interpretation, but works for
+ # most cases and fixes broken files, which have the end tag missing
+ if is_tied and not tie_started:
+ is_tied = False
+
+ ## force trailing mm rests to be written out.
voice_builder.add_music (musicexp.ChordEvent (), Rational (0))
-
+
ly_voice = group_tuplets (voice_builder.elements, tuplet_events)
ly_voice = group_repeats (ly_voice)
## \key <pitch> barfs in drummode.
ly_voice = [e for e in ly_voice
if not isinstance(e, musicexp.KeySignatureChange)]
-
+
seq_music.elements = ly_voice
for k in lyrics.keys ():
return_value.lyrics_dict[k] = musicexp.Lyrics ()
return_value.lyrics_dict[k].lyrics_syllables = lyrics[k]
-
-
+
+
if len (modes_found) > 1:
- error_message (_ ('cannot simultaneously have more than one mode: %s') % modes_found.keys ())
-
+ ly.warning (_ ('cannot simultaneously have more than one mode: %s') % modes_found.keys ())
+
if options.relative:
v = musicexp.RelativeMusic ()
v.element = seq_music
v.element = seq_music
v.mode = mode
return_value.ly_voice = v
-
+
# create \figuremode { figured bass elements }
if figured_bass_builder.has_relevant_elements:
fbass_music = musicexp.SequentialMusic ()
- fbass_music.elements = figured_bass_builder.elements
+ fbass_music.elements = group_repeats (figured_bass_builder.elements)
v = musicexp.ModeChangingMusicWrapper()
v.mode = 'figuremode'
v.element = fbass_music
return_value.figured_bass = v
-
+
# create \chordmode { chords }
if chordnames_builder.has_relevant_elements:
cname_music = musicexp.SequentialMusic ()
- cname_music.elements = chordnames_builder.elements
+ cname_music.elements = group_repeats (chordnames_builder.elements)
v = musicexp.ModeChangingMusicWrapper()
v.mode = 'chordmode'
v.element = cname_music
return_value.chordnames = v
-
+
return return_value
def musicxml_id_to_lily (id):
digits = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five',
'Six', 'Seven', 'Eight', 'Nine', 'Ten']
-
+
for digit in digits:
d = digits.index (digit)
id = re.sub ('%d' % d, digit, id)
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):
def voices_in_part_in_parts (parts):
"""return a Part -> Name -> Voice dictionary"""
- return dict([(p.id, voices_in_part (p)) for p in parts])
+ # don't crash if p doesn't have an id (that's invalid MusicXML,
+ # but such files are out in the wild!
+ dictionary = {}
+ for p in parts:
+ voices = voices_in_part (p)
+ if (hasattr (p, "id")):
+ dictionary[p.id] = voices
+ else:
+ # TODO: extract correct part id from other sources
+ dictionary[None] = voices
+ return dictionary;
def get_all_voices (parts):
part_ly_voices = {}
for n, v in name_voice.items ():
- progress (_ ("Converting to LilyPond expressions..."))
+ ly.progress (_ ("Converting to LilyPond expressions..."), True)
# musicxml_voice_to_lily_voice returns (lily_voice, {nr->lyrics, nr->lyrics})
part_ly_voices[n] = musicxml_voice_to_lily_voice (v)
p.version = ('''%prog (LilyPond) @TOPLEVEL_VERSION@\n\n'''
+
-_ ("""Copyright (c) 2005--2009 by
+_ ("""Copyright (c) 2005--2012 by
Han-Wen Nienhuys <hanwen@xs4all.nl>,
Jan Nieuwenhuizen <janneke@gnu.org> and
Reinhold Kainhofer <reinhold@kainhofer.com>
help=_ ("show version number and exit"))
p.add_option ('-v', '--verbose',
- action = "store_true",
- dest = 'verbose',
+ action="callback",
+ callback=ly.handle_loglevel_option,
+ callback_args=("DEBUG",),
help = _ ("be verbose"))
p.add_option ('', '--lxml',
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 ("--loglevel",
+ help=_ ("Print log messages according to LOGLEVEL "
+ "(NONE, ERROR, WARNING, PROGRESS (default), DEBUG)"),
+ metavar=_ ("LOGLEVEL"),
+ action='callback',
+ callback=ly.handle_loglevel_option,
+ type='string')
- p.add_option ('--nd', '--no-articulation-directions',
+ p.add_option ('--nd', '--no-articulation-directions',
action = "store_false",
default = True,
dest = "convert_directions",
help = _ ("do not convert directions (^, _ or -) for articulations, dynamics, etc."))
- p.add_option ('--nrp', '--no-rest-positions',
+ p.add_option ('--nrp', '--no-rest-positions',
action = "store_false",
default = True,
dest = "convert_rest_positions",
help = _ ("do not convert exact vertical positions of rests"))
- p.add_option ('--no-beaming',
+ p.add_option ('--npl', '--no-page-layout',
+ action = "store_false",
+ default = True,
+ dest = "convert_page_layout",
+ help = _ ("do not convert the exact page layout and breaks"))
+
+ p.add_option ('--no-beaming',
action = "store_false",
default = True,
dest = "convert_beaming",
type = 'string',
dest = 'output_name',
help = _ ("set output filename to FILE, stdout if -"))
+
+ p.add_option ('-m', '--midi',
+ action = "store_true",
+ default = False,
+ dest = "midi",
+ help = _("activate midi-block"))
+
p.add_option_group ('',
description = (
_ ("Report bugs via %s")
def music_xml_voice_name_to_lily_name (part_id, name):
str = "Part%sVoice%s" % (part_id, name)
- return musicxml_id_to_lily (str)
+ return musicxml_id_to_lily (str)
def music_xml_lyrics_name_to_lily_name (part_id, name, lyricsnr):
str = "Part%sVoice%sLyrics%s" % (part_id, name, lyricsnr)
- return musicxml_id_to_lily (str)
+ return musicxml_id_to_lily (str)
def music_xml_figuredbass_name_to_lily_name (part_id, voicename):
str = "Part%sVoice%sFiguredBass" % (part_id, voicename)
- return musicxml_id_to_lily (str)
+ return musicxml_id_to_lily (str)
def music_xml_chordnames_name_to_lily_name (part_id, voicename):
str = "Part%sVoice%sChords" % (part_id, voicename)
- return musicxml_id_to_lily (str)
+ return musicxml_id_to_lily (str)
def print_voice_definitions (printer, part_list, voices):
for part in part_list:
def uniq_list (l):
- return dict ([(elt,1) for elt in l]).keys ()
+ return dict ([(elt, 1) for elt in l]).keys ()
-# format the information about the staff in the form
+# format the information about the staff in the form
# [staffid,
# [
# [voiceid1, [lyricsid11, lyricsid12,...], figuredbassid1],
part_id = part_definition.id
nv_dict = voices.get (part_id)
if not nv_dict:
- error_message (_ ('unknown part in part-list: %s') % part_id)
+ ly.warning (_ ('unknown part in part-list: %s') % part_id)
continue
staves = reduce (lambda x,y: x+ y,
staves = uniq_list (staves)
staves.sort ()
for s in staves:
- thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames)
+ thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames)
for (voice_name, voice) in nv_dict.items ()
if voice.voicedata._start_staff == s]
staves_info.append (format_staff_info (part_id, s, thisstaff_raw_voices))
else:
- thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames)
+ thisstaff_raw_voices = [(voice_name, voice.lyrics_order, voice.figured_bass, voice.chordnames)
for (voice_name, voice) in nv_dict.items ()]
staves_info.append (format_staff_info (part_id, None, thisstaff_raw_voices))
score_structure.set_part_information (part_id, staves_info)
def print_ly_preamble (printer, filename):
printer.dump_version ()
- printer.print_verbatim ('%% automatically converted from %s\n' % filename)
+ printer.print_verbatim ('%% automatically converted by musicxml2ly from %s\n' % filename)
def print_ly_additional_definitions (printer, filename):
if needed_additional_definitions:
printer.newline ()
printer.newline ()
-# Read in the tree from the given I/O object (either file or string) and
+# Read in the tree from the given I/O object (either file or string) and
# demarshall it using the classes from the musicxml.py file
def read_xml (io_object, use_lxml):
if use_lxml:
raw_string = None
if compressed:
if filename == "-":
- progress (_ ("Input is compressed, extracting raw MusicXML data from stdin") )
- z = zipfile.ZipFile (sys.stdin)
+ ly.progress (_ ("Input is compressed, extracting raw MusicXML data from stdin"), True)
+ # 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)
+ ly.progress (_ ("Input file %s is compressed, extracting raw MusicXML data") % filename, True)
z = zipfile.ZipFile (filename, "r")
container_xml = z.read ("META-INF/container.xml")
if not container_xml:
def convert (filename, options):
if filename == "-":
- progress (_ ("Reading MusicXML from Standard input ...") )
+ ly.progress (_ ("Reading MusicXML from Standard input ..."), True)
else:
- progress (_ ("Reading MusicXML from %s ...") % filename)
+ ly.progress (_ ("Reading MusicXML from %s ...") % filename, True)
tree = read_musicxml (filename, options.compressed, options.use_lxml)
score_information = extract_score_information (tree)
update_layout_information ()
if not options.output_name:
- options.output_name = os.path.basename (filename)
+ options.output_name = os.path.basename (filename)
options.output_name = os.path.splitext (options.output_name)[0]
elif re.match (".*\.ly", options.output_name):
options.output_name = os.path.splitext (options.output_name)[0]
else:
output_ly_name = options.output_name + '.ly'
- progress (_ ("Output to `%s'") % output_ly_name)
+ ly.progress (_ ("Output to `%s'") % output_ly_name, True)
printer = musicexp.Output_printer()
- #progress (_ ("Output to `%s'") % defs_ly_name)
+ #ly.progress (_ ("Output to `%s'") % defs_ly_name, True)
if (options.output_name == "-"):
printer.set_file (codecs.getwriter ("utf-8")(sys.stdout))
else:
print_ly_additional_definitions (printer, filename)
if score_information:
score_information.print_ly (printer)
- if paper_information:
+ if paper_information and conversion_settings.convert_page_layout:
paper_information.print_ly (printer)
if layout_information:
layout_information.print_ly (printer)
print_voice_definitions (printer, part_list, voices)
-
+
printer.newline ()
printer.dump ("% The score definition")
printer.newline ()
opt_parser.print_usage()
sys.exit (2)
+ if options.midi:
+ musicexp.set_create_midi (options.midi)
+
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:
if filename and (filename == "-" or os.path.exists (filename)):
voices = convert (filename, options)
else:
- progress (_ ("Unable to find input file %s") % basefilename)
+ ly.error (_ ("Unable to find input file %s") % basefilename)
if __name__ == '__main__':
main()