def metadata_to_lily(metad, setts=None): """ Convert a :class:`Metadata` object into the LilyPond string. :param metad: The :class:`Metadata` object to convert. :type metad: :class:`music21.metadata.Metadata` :param setts: An optional settings object. :type setts: :class:`~settings.LilyPondSettings` :returns: The LilyPond-format notation for this metadata object. :rtype: unicode string """ if setts is None: setts = settings.LilyPondSettings() post = [u"\\header {\n"] if metad.composer is not None: post.extend([u'\tcomposer = \\markup{ "', metad.composer, u'" }\n']) if u'None' != text(metad.date): post.extend([u'\tdate = "', text(metad.date), u'"\n']) if metad.movementName is not None: if None != metad.movementNumber: post.extend([ u'\tsubtitle = \\markup{ "', text(metad.movementNumber), u': ', metad.movementName, u'" }\n' ]) else: post.extend( [u'\tsubtitle = \\markup{ "', metad.movementName, u'" }\n']) if metad.opusNumber is not None: post.extend([u'\topus = "', text(metad.opusNumber), u'"\n']) if metad.title is not None: if metad.alternativeTitle is not None: post.extend([ u'\ttitle = \\markup{ \"', metad.title, u'(\\"' + metad.alternativeTitle + u'\\")', u'" }\n' ]) else: post.extend([u'\ttitle = \\markup{ \"', metad.title, u'" }\n']) # Extra Formatting Options if setts.get_property('tagline') is None: post.append(u'\ttagline = ""\n') else: post.extend([u'\ttagline = "', setts.get_property('tagline'), '"\n']) # close the \header{} block, join, and return! post.append(u"}\n") return u''.join(post)
def metadata_to_lily(metad, setts=None): """ Convert a :class:`Metadata` object into the LilyPond string. :param metad: The :class:`Metadata` object to convert. :type metad: :class:`music21.metadata.Metadata` :param setts: An optional settings object. :type setts: :class:`~settings.LilyPondSettings` :returns: The LilyPond-format notation for this metadata object. :rtype: unicode string """ if setts is None: setts = settings.LilyPondSettings() post = [u"\\header {\n"] if metad.composer is not None: post.extend([u'\tcomposer = \\markup{ "', metad.composer, u'" }\n']) if u'None' != text(metad.date): post.extend([u'\tdate = "', text(metad.date), u'"\n']) if metad.movementName is not None: if None != metad.movementNumber: post.extend([u'\tsubtitle = \\markup{ "', text(metad.movementNumber), u': ', metad.movementName, u'" }\n']) else: post.extend([u'\tsubtitle = \\markup{ "', metad.movementName, u'" }\n']) if metad.opusNumber is not None: post.extend([u'\topus = "', text(metad.opusNumber), u'"\n']) if metad.title is not None: if metad.alternativeTitle is not None: post.extend([u'\ttitle = \\markup{ \"', metad.title, u'(\\"' + metad.alternativeTitle + u'\\")', u'" }\n']) else: post.extend([u'\ttitle = \\markup{ \"', metad.title, u'" }\n']) # Extra Formatting Options if setts.get_property('tagline') is None: post.append(u'\ttagline = ""\n') else: post.extend([u'\ttagline = "', setts.get_property('tagline'), '"\n']) # close the \header{} block, join, and return! post.append(u"}\n") return u''.join(post)
def octave_num_to_lily(num): """ Convert an octave number into the LilyPond string. :param num: The octave number to convert. As expected, ``4`` is the "middle C" register. :type num: int :returns: The string to append to a pitch to put it in the specified octave. :rtype: unicode string ** Examples ** >>> octave_num_to_lily(1) u",," >>> octave_num_to_lily(6) u"'''" """ try: return OCTAVE_DICT[num] except KeyError: raise problems.UnidentifiedObjectError('Octave out of range: ' + text(num))
def clef_to_lily(the_clef, append=u'\n', invisible=False): """ Convert a :class:`music21.clef.Clef` object into the LilyPond string. :param the_clef: The :class:`Clef` to convert. It must be recognized in :const:`CLEF_DICT`. :type the_clef: :class:`~music21.clef.Clef` subclass. :param append: An optional string to append to the clef string; the default is ``u'\n'``, representing a newline character. :type append: string :param invisible: Whether to override the ``#'transparent`` property to ``##t``; the default is ``False``. :type invisible: boolean :returns: The LilyPond-format notation for this clef. :rtype: unicode string :raises: :exc:`UnidentifiedObjectError` if the :class:`Clef` is not of a known type. """ post = u"\\once \\override Staff.Clef #'transparent = ##t" + append if invisible else u'' try: return post + CLEF_DICT[type(the_clef)] + append except KeyError: raise problems.UnidentifiedObjectError('Clef type not recognized: ' + text(the_clef))
def measure_to_lily(meas, incomplete=False): """ Convert a :class:`Measure` into the LilyPond string. :param meas: The :class:`Measure` to convert. :type mease: :class:`music21.stream.Measure` :param incomplete: Whether to check whether the :class:`Measure` is (durationally) incomplete. The default is ``False``. :type incomplete: boolean :returns: The LilyPond-format notation for this measure. :rtype: unicode string ** Special Attributes ** Attach this attribute to the :class:`Measure` itself: * ``lily_invisible`` (boolean): Make the :class:`Measure` and its contents invisible. """ post = [u"\t"] barcheck_included = False # Hold whether this Measure is supposed to be "invisible" invisible = False if hasattr(meas, 'lily_invisible'): invisible = meas.lily_invisible # Add the first requirement of invisibility if invisible: post.append(u'\\stopStaff\n\t') # first check if it's a partial (pick-up) measure if incomplete: bar_dur = meas.barDuration.quarterLength my_dur = meas.duration.quarterLength if round(my_dur, 2) < bar_dur: if meas.duration.components is not None: rounded = duration.Duration(round(my_dur, 2)) post.extend( [u"\\partial ", duration_to_lily(rounded), u"\n\t"]) else: post.extend( [u"\\partial ", duration_to_lily(meas.duration), u"\n\t"]) # Make meas an iterable, so we can pull in multiple elements when we need to deal # with tuplets. bar_iter = iter(meas) # This holds \markup{} blocks that happened before a Note/Rest, and should be appended # to the next Note/Rest that happens. attach_this_markup = u'' # And fill in all the stuff for obj in bar_iter: # Note or Rest if isinstance(obj, note.Note) or isinstance(obj, note.Rest): # TODO: is there a situation where I'll ever need to deal with # multiple-component durations for a single Note/Rest? # ANSWER: yes, sometimes # Is it a full-measure rest? if isinstance(obj, note.Rest) and \ bar_iter.srcStream.barDuration.quarterLength == obj.quarterLength: if invisible: post.extend(['s', duration_to_lily(obj.duration), u' ']) else: post.extend([u'R', duration_to_lily(obj.duration), u' ']) # Is it the start of a tuplet? elif obj.duration.tuplets is not None and len( obj.duration.tuplets) > 0: number_of_tuplet_components = obj.duration.tuplets[ 0].numberNotesActual in_the_space_of = obj.duration.tuplets[0].numberNotesNormal post.extend([ u'\\times ', text(in_the_space_of), u'/', text(number_of_tuplet_components), u' { ', note_to_lily(obj, True), u" " ]) # For every tuplet component... for _ in repeat(None, number_of_tuplet_components - 1): post.extend([note_to_lily(next(bar_iter), True), u' ']) post.append(u'} ') # It's just a regular note or rest else: post.extend([note_to_lily(obj), u' ']) # Is there a \markup{} block to append? if attach_this_markup != '': post.append(attach_this_markup) attach_this_markup = '' # Chord elif isinstance(obj, chord.Chord): post.extend([note_to_lily(obj), u' ']) # Clef elif isinstance(obj, clef.Clef): post.extend( [clef_to_lily(obj, append=u'\n\t', invisible=invisible)]) # Time Signature elif isinstance(obj, meter.TimeSignature): if invisible: post.append( u"\\once \\override Staff.TimeSignature #'transparent = ##t\n\t" ) post.extend([ u"\\time ", text(obj.beatCount), "/", text(obj.denominator), u"\n\t" ]) # Key Signature elif isinstance(obj, key.KeySignature): pitch_and_mode = obj.pitchAndMode if invisible: post.append( u"\\once \\override Staff.KeySignature #'transparent = ##t\n\t" ) if 2 == len(pitch_and_mode) and pitch_and_mode[1] is not None: post.extend([ u"\\key ", pitch_to_lily(pitch_and_mode[0], include_octave=False), u" \\" + pitch_and_mode[1] + "\n\t" ]) else: # We'll have to assume it's \major, because music21 does that. post.extend([ u"\\key ", pitch_to_lily(pitch_and_mode[0], include_octave=False), u" \\major\n\t" ]) # Barline elif isinstance(obj, bar.Barline): # We don't need to write down a regular barline, but either way, we definitely # should include a bar-check symbol barcheck_included = True if 'regular' != obj.style: post.extend([u'|\n', u'\t', barline_to_lily(obj), u'\n']) else: post.append(u'|\n') # PageLayout and SystemLayout elif isinstance(obj, layout.SystemLayout) or isinstance( obj, layout.PageLayout): # I don't know what to do with these undocumented features. # NB: They now have documentation, so I could check up on this... pass # **kern importer garbage... well, it's only garbage to us elif isinstance(obj, humdrum.spineParser.MiscTandem): # http://mit.edu/music21/doc/html/moduleHumdrumSpineParser.html # Is there really nothing we can use this for? Seems like these # exist only to help music21 developers. pass elif isinstance(obj, humdrum.spineParser.SpineComment): # http://mit.edu/music21/doc/html/moduleHumdrumSpineParser.html # These contain at least part names, and maybe also other interesting metadata(?) pass # Written expression marks (like "con fuoco" or something) elif isinstance(obj, expressions.TextExpression): the_marker = None # store the local thing if obj.positionVertical > 0: # above staff the_marker = [u"^\\markup{ "] elif obj.positionVertical < 0: # below staff the_marker = [u"_\\markup{ "] else: # LilyPond can decide above or below the_marker = [u"-\\markup{ "] if obj.enclosure is not None: # put a shape around the text? pass # TODO the_marker.extend([u'"', obj.content, u'" }']) if obj.enclosure is not None: # must close the enclosure, if necessary the_marker.append(u'}') the_marker.append(u' ') # Find out whether there's a previous Note or Rest to attach to previous_element = meas.getElementBeforeOffset(obj.offset) if not isinstance(previous_element, note.Note) and \ not isinstance(previous_element, note.Rest): # this variable holds text to append to the next Note/Rest attach_this_markup = u''.join([attach_this_markup, the_marker]) else: # There was a previous Note/Rest, so we're good post.append(u''.join(the_marker)) del the_marker elif isinstance(obj, layout.StaffLayout): # as in the Lassus duos # TODO: something more useful pass # We don't know what it is, and should probably figure out! else: pass #msg = u'Unknown object in m.%s of type %s.' % (meas.number, type(obj)) #print(msg) # DEBUG #raise UnidentifiedObjectError(msg) # Append a bar-check symbol, if relevant if len(post) > 1 and not barcheck_included: post.append(u"|\n") # Append a note if we couldn't include a \markup{} block if attach_this_markup != '': post.extend(u'% Could not include this markup: ', attach_this_markup) # The final requirement of invisibility if invisible: post.append(u'\t\\startStaff\n') return u''.join(post)
def note_to_lily(the_note, known_tuplet=False): """ Convert a :class:`Chord`, :class:`Note`, or :class:`Rest` into the LilyPond string. :param the_note: The note or rest to convert. :type known_note: :class:`music21.chord.Chord`, :class:`music21.note.Note`, or :class:`music21.note.Rest` :param known_tuplet: Whether we already know this note is part of a tuplet. (Default is ``False``). :type known_tuplet: boolean :returns: The LilyPond-format notation for this note. :rtype: unicode string ** Special Attributes ** Set these two attributes on the :class:`Chord`, :class:`Note`, or :class:`Rest` itself: * ``lily_invisible`` (boolean): Print the object as an invisible spacer (with ``u's'`` as the pitch). * ``lily_markup`` (string): This string will be appended after (to the right of) the rest of the note/rest/spacer's code. The intended purpose is to use this with a ``\\markup`` command, but you may also use it, for example, to add beam-start and beam-end characters. """ post = [] # Find the letter: either 's', '<whatever>' for a chord, 'r' for a rest, etc. if hasattr(the_note, 'lily_invisible') and the_note.lily_invisible is True: post.append(u's') elif isinstance(the_note, chord.Chord): # Add the pitch letters and register numbers the_pitches = [u'<'] for each_pitch in the_note.pitches: the_pitches.extend([pitch_to_lily(each_pitch), u' ']) the_pitches = the_pitches[:-1] # remove trailing space the_pitches.append(u'>') # add chord-end symbol post.append(u''.join(the_pitches)) elif the_note.isRest: post.append(u'r') else: post.append(pitch_to_lily(the_note.pitch)) # Find the duration(s) if len(the_note.duration.components) > 1: post.extend([ duration_to_lily(the_note.duration.components[0], known_tuplet), u'~ ' ]) # We have a multiple-part duration for component in the_note.duration.components[1:]: post.extend( [post[0], duration_to_lily(component, known_tuplet), u'~ ']) post = post[:-1] # remove the final tilde symbol else: # Just a straightforward duration post.append(duration_to_lily(the_note.duration, known_tuplet)) # Add a tie if necessary if the_note.tie is not None: if u'start' == the_note.tie.type: post.append(u'~') if hasattr(the_note, 'lily_markup'): post.append(text(the_note.lily_markup)) return u''.join(post)
def duration_to_lily(dur, known_tuplet=False): """ Convert a :class:`Duration` into the LilyPond string. :param dur: The duration to convert. :type dur: :class:`music21.duration.Duration` :param known_tuplet: Whether we already know this duration is part of a tuplet. (Default is ``False``). :type known_tuplet: boolean :returns: The LilyPond-format notation for this duration. :rtype: unicode string :raises: :exc:`ImpossibleToProcessError` if ``dur`` is part of a tuplet but ``known_tuplet`` is ``False``, since this requires a ``\\tuplet`` command in LilyPond. :raises: :exc:`ImpossibleToProcessError` if ``dur.quarterLength == 0.0`` is ``True``, since LilyPond has no zero-duration duration. """ # Every Duration should actually have some duration. if 0.0 == dur.quarterLength: msg = u'_duration_to_lily(): Cannot process quarterLength of 0.0' raise problems.ImpossibleToProcessError(msg) # First of all, we can't deal with tuplets or multiple-component durations # in this method. We need process_measure() to help. if dur.tuplets is not (): # We know either there are multiple components or we have part of a # tuplet, we we need to find out which. if len(dur.components) > 1: # We have multiple components raise problems.ImpossibleToProcessError( 'Cannot process durations with ' + 'multiple components (received ' + text(dur.components) + ' for quarterLength of ' + text(dur.quarterLength) + ')') elif known_tuplet: # We have part of a tuple. This isn't necessarily a problem; we'll # assume we are given this by process_measure() and that it knows # what's going on. But, in tuplets, the quarterLength doesn't match # the type of written note, so we'll make a new Duration with an # adjusted quarterLength dur = duration.Duration(dur.type) else: msg = 'duration_to_lily(): Cannot process tuplet components' raise problems.ImpossibleToProcessError(msg) dur_ql = dur.quarterLength # If there are no dots, the value should be in the dictionary, and we can simply return it. if dur_ql in DURATION_DICT: return DURATION_DICT[dur_ql] else: # We have to figure out the largest value that will fit, then append some dots. post = None for durat in sorted(DURATION_DICT.keys(), reverse=True): # we need largest-to-smallest qLs if (dur_ql - durat) > 0.0: post = DURATION_DICT[durat] break # For every dot in this Duration, append a '.' to "post" for _ in repeat(None, dur.dots): post += u'.' return post
def measure_to_lily(meas, incomplete=False): """ Convert a :class:`Measure` into the LilyPond string. :param meas: The :class:`Measure` to convert. :type mease: :class:`music21.stream.Measure` :param incomplete: Whether to check whether the :class:`Measure` is (durationally) incomplete. The default is ``False``. :type incomplete: boolean :returns: The LilyPond-format notation for this measure. :rtype: unicode string ** Special Attributes ** Attach this attribute to the :class:`Measure` itself: * ``lily_invisible`` (boolean): Make the :class:`Measure` and its contents invisible. """ post = [u"\t"] barcheck_included = False # Hold whether this Measure is supposed to be "invisible" invisible = False if hasattr(meas, 'lily_invisible'): invisible = meas.lily_invisible # Add the first requirement of invisibility if invisible: post.append(u'\\stopStaff\n\t') # first check if it's a partial (pick-up) measure if incomplete: bar_dur = meas.barDuration.quarterLength my_dur = meas.duration.quarterLength if round(my_dur, 2) < bar_dur: if meas.duration.components is not None: rounded = duration.Duration(round(my_dur, 2)) post.extend([u"\\partial ", duration_to_lily(rounded), u"\n\t"]) else: post.extend([u"\\partial ", duration_to_lily(meas.duration), u"\n\t"]) # Make meas an iterable, so we can pull in multiple elements when we need to deal # with tuplets. bar_iter = iter(meas) # This holds \markup{} blocks that happened before a Note/Rest, and should be appended # to the next Note/Rest that happens. attach_this_markup = u'' # And fill in all the stuff for obj in bar_iter: # Note or Rest if isinstance(obj, note.Note) or isinstance(obj, note.Rest): # TODO: is there a situation where I'll ever need to deal with # multiple-component durations for a single Note/Rest? # ANSWER: yes, sometimes # Is it a full-measure rest? if isinstance(obj, note.Rest) and \ bar_iter.srcStream.barDuration.quarterLength == obj.quarterLength: if invisible: post.extend(['s', duration_to_lily(obj.duration), u' ']) else: post.extend([u'R', duration_to_lily(obj.duration), u' ']) # Is it the start of a tuplet? elif obj.duration.tuplets: number_of_tuplet_components = obj.duration.tuplets[0].numberNotesActual in_the_space_of = obj.duration.tuplets[0].numberNotesNormal post.extend([u'\\times ', text(in_the_space_of), u'/', text(number_of_tuplet_components), u' { ', note_to_lily(obj, True), u" "]) # For every tuplet component... for _ in repeat(None, number_of_tuplet_components - 1): post.extend([note_to_lily(next(bar_iter), True), u' ']) post.append(u'} ') # It's just a regular note or rest else: post.extend([note_to_lily(obj), u' ']) # Is there a \markup{} block to append? if attach_this_markup != '': post.append(attach_this_markup) attach_this_markup = '' # Chord elif isinstance(obj, chord.Chord): post.extend([note_to_lily(obj), u' ']) # Clef elif isinstance(obj, clef.Clef): post.extend([clef_to_lily(obj, append=u'\n\t', invisible=invisible)]) # Time Signature elif isinstance(obj, meter.TimeSignature): if invisible: post.append(u"\\once \\override Staff.TimeSignature #'transparent = ##t\n\t") post.extend([u"\\time ", text(obj.beatCount), "/", text(obj.denominator), u"\n\t"]) # Key Signature elif isinstance(obj, key.KeySignature): pitch_and_mode = obj.pitchAndMode if invisible: post.append(u"\\once \\override Staff.KeySignature #'transparent = ##t\n\t") if 2 == len(pitch_and_mode) and pitch_and_mode[1] is not None: post.extend([u"\\key ", pitch_to_lily(pitch_and_mode[0], include_octave=False), u" \\" + pitch_and_mode[1] + "\n\t"]) else: # We'll have to assume it's \major, because music21 does that. post.extend([u"\\key ", pitch_to_lily(pitch_and_mode[0], include_octave=False), u" \\major\n\t"]) # Barline elif isinstance(obj, bar.Barline): # We don't need to write down a regular barline, but either way, we definitely # should include a bar-check symbol barcheck_included = True if 'regular' != obj.style: post.extend([u'|\n', u'\t', barline_to_lily(obj), u'\n']) else: post.append(u'|\n') # PageLayout and SystemLayout elif isinstance(obj, layout.SystemLayout) or isinstance(obj, layout.PageLayout): # I don't know what to do with these undocumented features. # NB: They now have documentation, so I could check up on this... pass # **kern importer garbage... well, it's only garbage to us elif isinstance(obj, humdrum.spineParser.MiscTandem): # http://mit.edu/music21/doc/html/moduleHumdrumSpineParser.html # Is there really nothing we can use this for? Seems like these # exist only to help music21 developers. pass elif isinstance(obj, humdrum.spineParser.SpineComment): # http://mit.edu/music21/doc/html/moduleHumdrumSpineParser.html # These contain at least part names, and maybe also other interesting metadata(?) pass # Written expression marks (like "con fuoco" or something) elif isinstance(obj, expressions.TextExpression): the_marker = None # store the local thing if obj.positionVertical > 0: # above staff the_marker = [u"^\\markup{ "] elif obj.positionVertical < 0: # below staff the_marker = [u"_\\markup{ "] else: # LilyPond can decide above or below the_marker = [u"-\\markup{ "] if obj.enclosure is not None: # put a shape around the text? pass # TODO the_marker.extend([u'"', obj.content, u'" }']) if obj.enclosure is not None: # must close the enclosure, if necessary the_marker.append(u'}') the_marker.append(u' ') # Find out whether there's a previous Note or Rest to attach to previous_element = meas.getElementBeforeOffset(obj.offset) if not isinstance(previous_element, note.Note) and \ not isinstance(previous_element, note.Rest): # this variable holds text to append to the next Note/Rest attach_this_markup = u''.join([attach_this_markup, the_marker]) else: # There was a previous Note/Rest, so we're good post.append(u''.join(the_marker)) del the_marker elif isinstance(obj, layout.StaffLayout): # as in the Lassus duos # TODO: something more useful pass # We don't know what it is, and should probably figure out! else: pass #msg = u'Unknown object in m.%s of type %s.' % (meas.number, type(obj)) #print(msg) # DEBUG #raise UnidentifiedObjectError(msg) # Append a bar-check symbol, if relevant if len(post) > 1 and not barcheck_included: post.append(u"|\n") # Append a note if we couldn't include a \markup{} block if attach_this_markup != '': post.extend(u'% Could not include this markup: ', attach_this_markup) # The final requirement of invisibility if invisible: post.append(u'\t\\startStaff\n') return u''.join(post)
def note_to_lily(the_note, known_tuplet=False): """ Convert a :class:`Chord`, :class:`Note`, or :class:`Rest` into the LilyPond string. :param the_note: The note or rest to convert. :type known_note: :class:`music21.chord.Chord`, :class:`music21.note.Note`, or :class:`music21.note.Rest` :param known_tuplet: Whether we already know this note is part of a tuplet. (Default is ``False``). :type known_tuplet: boolean :returns: The LilyPond-format notation for this note. :rtype: unicode string ** Special Attributes ** Set these two attributes on the :class:`Chord`, :class:`Note`, or :class:`Rest` itself: * ``lily_invisible`` (boolean): Print the object as an invisible spacer (with ``u's'`` as the pitch). * ``lily_markup`` (string): This string will be appended after (to the right of) the rest of the note/rest/spacer's code. The intended purpose is to use this with a ``\\markup`` command, but you may also use it, for example, to add beam-start and beam-end characters. """ post = [] # Find the letter: either 's', '<whatever>' for a chord, 'r' for a rest, etc. if hasattr(the_note, 'lily_invisible') and the_note.lily_invisible is True: post.append(u's') elif isinstance(the_note, chord.Chord): # Add the pitch letters and register numbers the_pitches = [u'<'] for each_pitch in the_note.pitches: the_pitches.extend([pitch_to_lily(each_pitch), u' ']) the_pitches = the_pitches[:-1] # remove trailing space the_pitches.append(u'>') # add chord-end symbol post.append(u''.join(the_pitches)) elif the_note.isRest: post.append(u'r') else: post.append(pitch_to_lily(the_note.pitch)) # Find the duration(s) if the_note.duration.isComplex: post.extend([duration_to_lily(the_note.duration.components[0], known_tuplet), u'~ ']) # We have a multiple-part duration for component in the_note.duration.components[1:]: post.extend([post[0], duration_to_lily(component, known_tuplet), u'~ ']) post = post[:-1] # remove the final tilde symbol else: # Just a straightforward duration post.append(duration_to_lily(the_note.duration, known_tuplet)) # Add a tie if necessary if the_note.tie is not None: if u'start' == the_note.tie.type: post.append(u'~') if hasattr(the_note, 'lily_markup'): post.append(text(the_note.lily_markup)) return u''.join(post)