# -*- coding: utf-8 -*- import inspect import sys import string import re import math import lilylib as ly import warnings import utilities _ = ly._ from rational import Rational # Store previously converted pitch for \relative conversion as a global state variable previous_pitch = None relative_pitches = False whatOrnament = "" ly_dur = None # stores lilypond durations def escape_instrument_string (input_string): retstring = string.replace (input_string, "\"", "\\\"") if re.match ('.*[\r\n]+.*', retstring): rx = re.compile (r'[\n\r]+') strings = rx.split (retstring) retstring = "\\markup { \\center-column { " for s in strings: retstring += "\\line {\"" + s + "\"} " retstring += "} }" else: retstring = "\"" + retstring + "\"" return retstring class Output_stack_element: def __init__ (self): self.factor = Rational (1) def copy (self): o = Output_stack_element() o.factor = self.factor return o class Output_printer(object): """ A class that takes care of formatting (eg.: indenting) a Music expression as a .ly file. """ def __init__ (self): self._line = '' self._indent = 4 self._nesting = 0 self._file = sys.stdout self._line_len = 72 self._output_state_stack = [Output_stack_element()] self._skipspace = False self._last_duration = None def set_file (self, file): self._file = file def dump_version (self, version): self.print_verbatim ('\\version "' + version + '"') self.newline () def get_indent (self): return self._nesting * self._indent def override (self): last = self._output_state_stack[-1] self._output_state_stack.append (last.copy()) def add_factor (self, factor): self.override() self._output_state_stack[-1].factor *= factor def revert (self): del self._output_state_stack[-1] if not self._output_state_stack: raise 'empty' def duration_factor (self): return self._output_state_stack[-1].factor def print_verbatim (self, str): self._line += str def unformatted_output (self, str): # don't indent on \< and indent only once on << self._nesting += (str.count ('<') - str.count ('\<') - str.count ('<<') + str.count ('{')) self._nesting -= (str.count ('>') - str.count ('\>') - str.count ('>>') - str.count ('->') - str.count ('_>') - str.count ('^>') + str.count ('}')) self.print_verbatim (str) def print_duration_string (self, str): if self._last_duration == str: return self.unformatted_output (str) # def print_note_color (self, object, rgb=None): # if rgb: # str = ("\\once\\override %s #'color = #(rgb-color %s # %s %s)" % (object, rgb[0], rgb[1], rgb[2])) # else: # str = "\\revert %s #'color" % object # self.newline() # self.add_word(str) # self.newline() def add_word (self, str): if (len (str) + 1 + len (self._line) > self._line_len): self.newline() self._skipspace = True if not self._skipspace: self._line += ' ' self.unformatted_output (str) self._skipspace = False def newline (self): self._file.write (self._line + '\n') self._line = ' ' * self._indent * self._nesting self._skipspace = True def skipspace (self): self._skipspace = True def __call__(self, arg): self.dump (arg) def dump (self, str): if self._skipspace: self._skipspace = False self.unformatted_output (str) else: # Avoid splitting quoted strings (e.g. "1. Wie") when indenting. words = utilities.split_string_and_preserve_doublequoted_substrings(str) for w in words: self.add_word (w) def close (self): self.newline () self._file.close () self._file = None class Duration: def __init__(self): self.duration_log = 0 self.dots = 0 self.factor = Rational(1) def lisp_expression(self): return '(ly:make-duration %d %d %d %d)' % (self.duration_log, self.dots, self.factor.numerator(), self.factor.denominator()) def ly_expression(self, factor=None, scheme_mode=False): global ly_dur # stores lilypond durations if not factor: factor = self.factor if self.duration_log < 0: if scheme_mode: longer_dict = {-1: "breve", -2: "longa"} else: longer_dict = {-1: "\\breve", -2: "\\longa"} dur_str = longer_dict.get(self.duration_log, "1") else: dur_str = '%d' % (1 << self.duration_log) dur_str += '.' * self.dots if factor <> Rational(1, 1): if factor.denominator() <> 1: dur_str += '*%d/%d' % (factor.numerator(), factor.denominator()) else: dur_str += '*%d' % factor.numerator() if dur_str.isdigit(): ly_dur = int(dur_str) # TODO: We need to deal with dotted notes and scaled durations # otherwise ly_dur won't work in combination with tremolos. return dur_str def print_ly(self, outputter): dur_str = self.ly_expression(self.factor / outputter.duration_factor()) outputter.print_duration_string(dur_str) def __repr__(self): return self.ly_expression() def copy(self): d = Duration() d.dots = self.dots d.duration_log = self.duration_log d.factor = self.factor return d def get_length(self): dot_fact = Rational((1 << (1 + self.dots)) - 1, 1 << self.dots) log = abs(self.duration_log) dur = 1 << log if self.duration_log < 0: base = Rational(dur) else: base = Rational(1, dur) return base * dot_fact * self.factor def set_create_midi(option): """ Implement the midi command line option '-m' and '--midi'. If True, add midi-block to .ly file (see L{musicexp.Score.print_ly}). @param option: Indicates whether the midi-block has to be added or not. @type option: boolean """ global midi_option midi_option = option def get_create_midi (): """ Return, if exists the state of the midi-option. @return: The state of the midi-option. @rtype: boolean """ try: return midi_option except: return False # implement the command line option '--transpose' def set_transpose(option): global transpose_option transpose_option = option def get_transpose(optType): try: if(optType == "string"): return '\\transpose c %s' % transpose_option elif(optType == "integer"): p = generic_tone_to_pitch(transpose_option) return p.semitones() except: if(optType == "string"): return "" elif(optType == "integer"): return 0 # implement the command line option '--tab-clef' def set_tab_clef(option): global tab_clef_option tab_clef_option = option def get_tab_clef(): try: return ("tab", tab_clef_option)[tab_clef_option == "tab" or tab_clef_option == "moderntab"] except: return "tab" # definitions of the command line option '--string-numbers' def set_string_numbers(option): global string_numbers_option string_numbers_option = option def get_string_numbers(): try: return ("t", string_numbers_option)[string_numbers_option == "t" or string_numbers_option == "f"] except: return "t" def generic_tone_to_pitch (tone): accidentals_dict = { "" : 0, "es" : -1, "s" : -1, "eses" : -2, "ses" : -2, "is" : 1, "isis" : 2 } p = Pitch () tone_ = tone.strip().lower() p.octave = tone_.count("'") - tone_.count(",") tone_ = tone_.replace(",","").replace("'","") p.step = ((ord (tone_[0]) - ord ('a') + 5) % 7) p.alteration = accidentals_dict.get(tone_[1:], 0) return p # Implement the different note names for the various languages def pitch_generic (pitch, notenames, accidentals): str = notenames[pitch.step] halftones = int (pitch.alteration) if halftones < 0: str += accidentals[0] * (-halftones) elif pitch.alteration > 0: str += accidentals[3] * (halftones) # Handle remaining fraction to pitch.alteration (for microtones) if (halftones != pitch.alteration): if None in accidentals[1:3]: ly.warning (_ ("Language does not support microtones contained in the piece")) else: try: str += {-0.5: accidentals[1], 0.5: accidentals[2]}[pitch.alteration - halftones] except KeyError: ly.warning (_ ("Language does not support microtones contained in the piece")) return str def pitch_general (pitch): str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['es', 'eh', 'ih', 'is']) return str.replace ('aes', 'as').replace ('ees', 'es') def pitch_nederlands (pitch): return pitch_general (pitch) def pitch_english (pitch): str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['f', 'qf', 'qs', 's']) return str.replace ('aes', 'as').replace ('ees', 'es') def pitch_deutsch (pitch): str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['es', 'eh', 'ih', 'is']) return str.replace ('hes', 'b').replace ('aes', 'as').replace ('ees', 'es') def pitch_norsk (pitch): return pitch_deutsch (pitch) def pitch_svenska (pitch): str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', None, None, 'iss']) return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es') def pitch_italiano (pitch): str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd']) return str def pitch_catalan (pitch): return pitch_italiano (pitch) def pitch_francais (pitch): str = pitch_generic (pitch, ['do', 'ré', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd']) return str def pitch_espanol (pitch): str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 's']) return str def pitch_vlaams (pitch): str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 'k']) return str def set_pitch_language (language): global pitch_generating_function function_dict = { "nederlands": pitch_nederlands, "english": pitch_english, "deutsch": pitch_deutsch, "norsk": pitch_norsk, "svenska": pitch_svenska, "italiano": pitch_italiano, "français": pitch_francais, "catalan": pitch_catalan, "espanol": pitch_espanol, "español": pitch_espanol, "vlaams": pitch_vlaams} pitch_generating_function = function_dict.get (language, pitch_general) # global variable to hold the formatting function. pitch_generating_function = pitch_general class Pitch: def __init__ (self): self.alteration = 0 self.step = 0 self.octave = 0 self._force_absolute_pitch = False def __repr__(self): return self.ly_expression() def transposed (self, interval): c = self.copy () c.alteration += interval.alteration c.step += interval.step c.octave += interval.octave c.normalize () target_st = self.semitones() + interval.semitones() c.alteration += target_st - c.semitones() return c def normalize (c): while c.step < 0: c.step += 7 c.octave -= 1 c.octave += c.step / 7 c.step = c.step % 7 def lisp_expression (self): return '(ly:make-pitch %d %d %d)' % (self.octave, self.step, self.alteration) def copy (self): p = Pitch () p.alteration = self.alteration p.step = self.step p.octave = self.octave p._force_absolute_pitch = self._force_absolute_pitch return p def steps (self): return self.step + self.octave * 7 def semitones (self): return self.octave * 12 + [0, 2, 4, 5, 7, 9, 11][self.step] + self.alteration def normalize_alteration (c): if(c.alteration < 0 and [True, False, False, True, False, False, False][c.step]): c.alteration += 1 c.step -= 1 elif(c.alteration > 0 and [False, False, True, False, False, False, True][c.step]): c.alteration -= 1 c.step += 1 c.normalize () def add_semitones (self, number): semi = number + self.alteration self.alteration = 0 if(semi == 0): return sign = (1,-1)[semi < 0] prev = self.semitones() while abs((prev + semi) - self.semitones ()) > 1: self.step += sign self.normalize() self.alteration += (prev + semi) - self.semitones () self.normalize_alteration () def ly_step_expression (self): return pitch_generating_function (self) def absolute_pitch (self): if self.octave >= 0: return "'" * (self.octave + 1) elif self.octave < -1: return "," * (-self.octave - 1) else: return '' def relative_pitch (self): global previous_pitch if not previous_pitch: previous_pitch = self return self.absolute_pitch () previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step this_pitch_steps = self.octave * 7 + self.step pitch_diff = (this_pitch_steps - previous_pitch_steps) previous_pitch = self if pitch_diff > 3: return "'" * ((pitch_diff + 3) / 7) elif pitch_diff < -3: return "," * ((-pitch_diff + 3) / 7) else: return "" def ly_expression (self): str = self.ly_step_expression () if relative_pitches and not self._force_absolute_pitch: str += self.relative_pitch () else: str += self.absolute_pitch () return str def print_ly (self, outputter): outputter (self.ly_expression()) class Music: def __init__ (self): self.parent = None self.start = Rational (0) self.comment = '' self.identifier = None def get_length(self): return Rational (0) def get_properties (self): return '' def has_children (self): return False def get_index (self): if self.parent: return self.parent.elements.index (self) else: return None def name (self): return self.__class__.__name__ def lisp_expression (self): name = self.name() props = self.get_properties () return "(make-music '%s %s)" % (name, props) def set_start (self, start): self.start = start def find_first (self, predicate): if predicate (self): return self return None def print_comment (self, printer, text=None): if not text: text = self.comment if not text: return if text == '\n': printer.newline () return lines = string.split (text, '\n') for l in lines: if l: printer.unformatted_output ('% ' + l) printer.newline () def print_with_identifier (self, printer): if self.identifier: printer ("\\%s" % self.identifier) else: self.print_ly (printer) def print_ly (self, printer): printer (self.ly_expression ()) class MusicWrapper (Music): def __init__ (self): Music.__init__(self) self.element = None def print_ly (self, func): self.element.print_ly (func) class ModeChangingMusicWrapper (MusicWrapper): def __init__ (self): MusicWrapper.__init__ (self) self.mode = 'notemode' def print_ly (self, func): func ('\\%s' % self.mode) MusicWrapper.print_ly (self, func) class RelativeMusic (MusicWrapper): def __init__ (self): MusicWrapper.__init__ (self) self.basepitch = None def print_ly (self, func): global previous_pitch global relative_pitches prev_relative_pitches = relative_pitches relative_pitches = True previous_pitch = self.basepitch if not previous_pitch: previous_pitch = Pitch () func ('\\relative %s%s' % (pitch_generating_function (previous_pitch), previous_pitch.absolute_pitch ())) MusicWrapper.print_ly (self, func) relative_pitches = prev_relative_pitches class TimeScaledMusic (MusicWrapper): def __init__ (self): MusicWrapper.__init__ (self) self.numerator = 1 self.denominator = 1 self.display_number = "actual" # valid values "actual" | "both" | None # Display the basic note length for the tuplet: self.display_type = None # value values "actual" | "both" | None self.display_bracket = "bracket" # valid values "bracket" | "curved" | None self.actual_type = None # The actually played unit of the scaling self.normal_type = None # The basic unit of the scaling self.display_numerator = None self.display_denominator = None def print_ly (self, func): if self.display_bracket == None: func ("\\once \\omit TupletBracket") func.newline () elif self.display_bracket == "curved": ly.warning (_ ("Tuplet brackets of curved shape are not correctly implemented")) func ("\\once \\override TupletBracket.stencil = #ly:slur::print") func.newline () base_number_function = {None: "#f", "actual": "tuplet-number::calc-denominator-text", "both": "tuplet-number::calc-fraction-text"}.get (self.display_number, None) # If we have non-standard numerator/denominator, use our custom function if self.display_number == "actual" and self.display_denominator: base_number_function = "(tuplet-number::non-default-tuplet-denominator-text %s)" % self.display_denominator elif self.display_number == "both" and (self.display_denominator or self.display_numerator): if self.display_numerator: num = self.display_numerator else: num = "#f" if self.display_denominator: den = self.display_denominator else: den = "#f" base_number_function = "(tuplet-number::non-default-tuplet-fraction-text %s %s)" % (den, num) if self.display_type == "actual" and self.normal_type: # Obtain the note duration in scheme-mode, i.e. \longa as \\longa base_duration = self.normal_type.ly_expression (None, True) func ("\\once \\override TupletNumber.text = #(tuplet-number::append-note-wrapper %s \"%s\")" % (base_number_function, base_duration)) func.newline () elif self.display_type == "both": # TODO: Implement this using actual_type and normal_type! if self.display_number == None: func ("\\once \\omit TupletNumber") func.newline () elif self.display_number == "both": den_duration = self.normal_type.ly_expression (None, True) # If we don't have an actual type set, use the normal duration! if self.actual_type: num_duration = self.actual_type.ly_expression (None, True) else: num_duration = den_duration if (self.display_denominator or self.display_numerator): func ("\\once \\override TupletNumber.text = #(tuplet-number::non-default-fraction-with-notes %s \"%s\" %s \"%s\")" % (self.display_denominator, den_duration, self.display_numerator, num_duration)) func.newline () else: func ("\\once \\override TupletNumber.text = #(tuplet-number::fraction-with-notes \"%s\" \"%s\")" % (den_duration, num_duration)) func.newline () else: if self.display_number == None: func ("\\once \\omit TupletNumber") func.newline () elif self.display_number == "both": func ("\\once \\override TupletNumber.text = #%s" % base_number_function) func.newline () func ('\\times %d/%d ' % (self.numerator, self.denominator)) func.add_factor (Rational (self.numerator, self.denominator)) MusicWrapper.print_ly (self, func) func.revert () class NestedMusic(Music): def __init__ (self): Music.__init__ (self) self.elements = [] def append (self, what): if what: self.elements.append (what) def has_children (self): return self.elements def insert_around (self, succ, elt, dir): assert elt.parent == None assert succ == None or succ in self.elements idx = 0 if succ: idx = self.elements.index (succ) if dir > 0: idx += 1 else: if dir < 0: idx = 0 elif dir > 0: idx = len (self.elements) self.elements.insert (idx, elt) elt.parent = self def get_properties (self): return ("'elements (list %s)" % string.join (map (lambda x: x.lisp_expression(), self.elements))) def get_subset_properties (self, predicate): return ("'elements (list %s)" % string.join (map (lambda x: x.lisp_expression(), filter (predicate, self.elements)))) def get_neighbor (self, music, dir): assert music.parent == self idx = self.elements.index (music) idx += dir idx = min (idx, len (self.elements) - 1) idx = max (idx, 0) return self.elements[idx] def delete_element (self, element): assert element in self.elements self.elements.remove (element) element.parent = None def set_start (self, start): self.start = start for e in self.elements: e.set_start (start) def find_first (self, predicate): r = Music.find_first (self, predicate) if r: return r for e in self.elements: r = e.find_first (predicate) if r: return r return None class SequentialMusic (NestedMusic): def get_last_event_chord (self): value = None at = len(self.elements) - 1 while (at >= 0 and not isinstance (self.elements[at], ChordEvent) and not isinstance (self.elements[at], BarLine)): at -= 1 if (at >= 0 and isinstance (self.elements[at], ChordEvent)): value = self.elements[at] return value def print_ly (self, printer, newline=True): printer ('{') if self.comment: self.print_comment (printer) if newline: printer.newline() for e in self.elements: e.print_ly (printer) printer ('}') if newline: printer.newline() def lisp_sub_expression (self, pred): name = self.name() props = self.get_subset_properties (pred) return "(make-music '%s %s)" % (name, props) def set_start (self, start): for e in self.elements: e.set_start (start) start += e.get_length() class RepeatedMusic: def __init__ (self): self.repeat_type = "volta" self.repeat_count = 2 self.endings = [] self.music = None def set_music (self, music): if isinstance (music, Music): self.music = music elif isinstance (music, list): self.music = SequentialMusic () self.music.elements = music else: ly.warning (_ ("unable to set the music %(music)s for the repeat %(repeat)s") % \ {'music':music, 'repeat':self}) def add_ending (self, music): self.endings.append (music) def print_ly (self, printer): printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count)) if self.music: self.music.print_ly (printer) else: ly.warning (_ ("encountered repeat without body")) printer.dump ('{}') if self.endings: printer.dump ('\\alternative {') for e in self.endings: e.print_ly (printer) printer.dump ('}') class Lyrics: def __init__ (self): self.lyrics_syllables = [] def print_ly (self, printer): printer.dump (self.ly_expression ()) printer.newline() printer.dump ('}') printer.newline() def ly_expression (self): lstr = "\lyricmode {\set ignoreMelismata = ##t" for l in self.lyrics_syllables: lstr += l #lstr += "\n}" return lstr class Header: def __init__ (self): self.header_fields = {} def set_field (self, field, value): self.header_fields[field] = value def format_header_strings(self, key, value, printer): printer.dump(key + ' = ') # If a header item contains a line break, it is segmented. The # substrings are formatted with the help of \markup, using # \column and \line. An exception, however, are texidoc items, # which should not contain LilyPond formatting commands. if (key != 'texidoc') and ('\n' in value): value = value.replace('"', '') printer.dump(r'\markup \column {') substrings = value.split('\n') for s in substrings: printer.newline() printer.dump(r'\line { "' + s + '"}') printer.dump('}') printer.newline() else: printer.dump(value) printer.newline() def print_ly(self, printer): printer.dump("\header {") printer.newline() for (k, v) in self.header_fields.items(): if v: self.format_header_strings(k, v, printer) #printer.newline() printer.dump("}") printer.newline() printer.newline() class Paper: def __init__(self): self.global_staff_size = -1 # page size self.page_width = -1 self.page_height = -1 # page margins self.top_margin = -1 self.bottom_margin = -1 self.left_margin = -1 self.right_margin = -1 self.system_left_margin = -1 self.system_right_margin = -1 self.system_distance = -1 self.top_system_distance = -1 self.indent = 0 self.short_indent = 0 self.instrument_names = [] def print_length_field (self, printer, field, value): if value >= 0: printer.dump ("%s = %s\\cm" % (field, value)) printer.newline () def get_longest_instrument_name(self): result = '' for name in self.instrument_names: lines = name.split('\n') for line in lines: if len(line) > len(result): result = line return result def print_ly (self, printer): if self.global_staff_size > 0: printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size) printer.newline () printer.dump ('\\paper {') printer.newline () printer.dump ("markup-system-spacing #'padding = #2") printer.newline () self.print_length_field (printer, "paper-width", self.page_width) self.print_length_field (printer, "paper-height", self.page_height) self.print_length_field (printer, "top-margin", self.top_margin) self.print_length_field (printer, "bottom-margin", self.bottom_margin) self.print_length_field (printer, "left-margin", self.left_margin) # TODO: maybe set line-width instead of right-margin? self.print_length_field (printer, "right-margin", self.right_margin) # TODO: What's the corresponding setting for system_left_margin and # system_right_margin in LilyPond? self.print_length_field (printer, "between-system-space", self.system_distance) self.print_length_field (printer, "page-top-space", self.top_system_distance) # TODO: Compute the indentation with the instrument name lengths # TODO: font width ? char_per_cm = (len(self.get_longest_instrument_name()) * 13) / self.page_width if (self.indent != 0): self.print_length_field (printer, "indent", self.indent/char_per_cm) if (self.short_indent != 0): self.print_length_field (printer, "short-indent", self.short_indent/char_per_cm) printer.dump ('}') printer.newline () class Layout: def __init__ (self): self.context_dict = {} def add_context (self, context): if not self.context_dict.has_key (context): self.context_dict[context] = [] def set_context_item (self, context, item): self.add_context (context) if not item in self.context_dict[context]: self.context_dict[context].append (item) def print_ly (self, printer): if self.context_dict.items (): printer.dump ('\\layout {') printer.newline () for (context, defs) in self.context_dict.items (): printer.dump ('\\context { \\%s' % context) printer.newline () for d in defs: printer.dump (d) printer.newline () printer.dump ('}') printer.newline () printer.dump ('}') printer.newline () class ChordEvent (NestedMusic): def __init__ (self): NestedMusic.__init__ (self) self.after_grace_elements = None self.grace_elements = None self.grace_type = None def append_grace (self, element): if element: if not self.grace_elements: self.grace_elements = SequentialMusic () self.grace_elements.append (element) def append_after_grace (self, element): if element: if not self.after_grace_elements: self.after_grace_elements = SequentialMusic () self.after_grace_elements.append (element) def has_elements (self): return [e for e in self.elements if isinstance (e, NoteEvent) or isinstance (e, RestEvent)] != [] def get_length (self): l = Rational (0) for e in self.elements: l = max(l, e.get_length()) return l def get_duration (self): note_events = [e for e in self.elements if isinstance (e, NoteEvent) or isinstance (e, RestEvent)] if note_events: return note_events[0].duration else: return None def print_ly (self, printer): note_events = [e for e in self.elements if isinstance (e, NoteEvent)] rest_events = [e for e in self.elements if isinstance (e, RhythmicEvent) and not isinstance (e, NoteEvent)] other_events = [e for e in self.elements if not isinstance (e, RhythmicEvent)] if self.after_grace_elements: printer ('\\afterGrace {') if self.grace_elements and self.elements: if self.grace_type: printer ('\\%s' % self.grace_type) else: printer ('\\grace') # don't print newlines after the { and } braces self.grace_elements.print_ly (printer, False) elif self.grace_elements: # no self.elements! ly.warning (_ ("Grace note with no following music: %s") % self.grace_elements) if self.grace_type: printer ('\\%s' % self.grace_type) else: printer ('\\grace') self.grace_elements.print_ly (printer, False) printer ('{}') # Print all overrides and other settings needed by the # articulations/ornaments before the note for e in other_events: e.print_before_note (printer) if rest_events: rest_events[0].print_ly (printer) elif len (note_events) == 1: note_events[0].print_ly (printer) elif note_events: global previous_pitch pitches = [] basepitch = None stem = None for x in note_events: if(x.associated_events): for aev in x.associated_events: if (isinstance(aev, StemEvent) and aev.value): stem = aev pitches.append (x.chord_element_ly ()) if not basepitch: basepitch = previous_pitch if stem: printer (stem.ly_expression ()) printer ('<%s>' % string.join (pitches)) previous_pitch = basepitch duration = self.get_duration () if duration: duration.print_ly (printer) else: pass for e in other_events: e.print_ly (printer) for e in other_events: e.print_after_note (printer) if self.after_grace_elements: printer ('}') self.after_grace_elements.print_ly (printer, False) self.print_comment (printer) class Partial (Music): def __init__ (self): Music.__init__ (self) self.partial = None def print_ly (self, printer): if self.partial: printer.dump ("\\partial %s" % self.partial.ly_expression ()) class BarLine (Music): def __init__ (self): Music.__init__ (self) self.bar_number = 0 self.type = None def print_ly (self, printer): bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': "dashed", 'heavy': "|", 'light-light': "||", 'light-heavy': "|.", 'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'", 'short': "'", 'none': "" }.get (self.type, None) if bar_symbol <> None: printer.dump ('\\bar "%s"' % bar_symbol) else: printer.dump ("|") if self.bar_number > 0 and (self.bar_number % 10) == 0: printer.dump ("\\barNumberCheck #%d " % self.bar_number) elif self.bar_number > 0: printer.print_verbatim (' %% %d' % self.bar_number) printer.newline () def ly_expression (self): return " | " class Event(Music): def __init__ (self): # strings to print before the note to which an event is attached. # Ignored for notes etc. self.before_note = None self.after_note = None # print something before the note to which an event is attached, e.g. overrides def print_before_note (self, printer): if self.before_note: printer.dump (self.before_note) # print something after the note to which an event is attached, e.g. resetting def print_after_note (self, printer): if self.after_note: printer.dump (self.after_note) pass class SpanEvent (Event): def __init__ (self): Event.__init__ (self) self.span_direction = 0 # start/stop self.line_type = 'solid' self.span_type = 0 # e.g. cres/decrescendo, ottava up/down self.size = 0 # size of e.g. octave shift def wait_for_note (self): return True def get_properties(self): return "'span-direction %d" % self.span_direction def set_span_type (self, type): self.span_type = type class SlurEvent (SpanEvent): def print_before_note (self, printer): command = {'dotted': '\\slurDotted', 'dashed' : '\\slurDashed'}.get (self.line_type, '') if command and self.span_direction == -1: printer.dump (command) def print_after_note (self, printer): # reset non-solid slur types! command = {'dotted': '\\slurSolid', 'dashed' : '\\slurSolid'}.get (self.line_type, '') if command and self.span_direction == -1: printer.dump (command) def ly_expression (self): return {-1: '(', 1:')'}.get (self.span_direction, '') class BeamEvent (SpanEvent): def ly_expression (self): return {-1: '[', 1:']'}.get (self.span_direction, '') class PedalEvent (SpanEvent): def ly_expression (self): return {-1: '\\sustainOn', 0:'\\sustainOff\\sustainOn', 1:'\\sustainOff'}.get (self.span_direction, '') class TextSpannerEvent (SpanEvent): def print_before_note (self, printer): if hasattr(self, 'style') and self.style=="wave": printer.dump("\once \override TextSpanner #'style = #'trill") try: x = {-1:'\\textSpannerDown', 0:'\\textSpannerNeutral', 1: '\\textSpannerUp'}.get(self.force_direction, '') printer.dump (x) except: pass def print_after_note (self, printer): pass def ly_expression (self): global whatOrnament if hasattr(self, 'style') and self.style=="ignore": return "" # if self.style=="wave": if whatOrnament == "wave": return {-1: '\\startTextSpan', 1:'\\stopTextSpan'}.get (self.span_direction, '') else: if hasattr(self, 'style') and self.style=="stop" and whatOrnament != "trill": return "" return {-1: '\\startTrillSpan', 1:'\\stopTrillSpan'}.get (self.span_direction, '') class BracketSpannerEvent (SpanEvent): # Ligature brackets use prefix-notation!!! def print_before_note (self, printer): if self.span_direction == -1: if self.force_direction == 1: printer.dump("\once \override LigatureBracket #' direction = #UP") elif self.force_direction == -1: printer.dump("\once \override LigatureBracket #' direction = #DOWN") printer.dump ('\[') # the bracket after the last note def print_after_note (self, printer): if self.span_direction == 1: printer.dump ('\]') # we're printing everything in print_(before|after)_note... def ly_expression (self): return ''; class OctaveShiftEvent (SpanEvent): def wait_for_note (self): return False def set_span_type (self, type): self.span_type = {'up': 1, 'down':-1}.get (type, 0) def ly_octave_shift_indicator (self): # convert 8/15 to lilypond indicators (+-1/+-2) try: value = {8: 1, 15: 2}[self.size] except KeyError: ly.warning (_ ("Invalid octave shift size found: %s. Using no shift.") % self.size) value = 0 # negative values go up! value *= -1 * self.span_type return value def ly_expression (self): dir = self.ly_octave_shift_indicator () value = '' if dir: value = '\ottava #%s' % dir return { - 1: value, 1: '\ottava #0'}.get (self.span_direction, '') class TrillSpanEvent (SpanEvent): def ly_expression (self): return {-1: '\\startTrillSpan', 0: '', # no need to write out anything for type='continue' 1:'\\stopTrillSpan'}.get (self.span_direction, '') class GlissandoEvent (SpanEvent): def print_before_note (self, printer): if self.span_direction == -1: style = { "dashed" : "dashed-line", "dotted" : "dotted-line", "wavy" : "zigzag" }. get (self.line_type, None) if style: printer.dump ("\\once \\override Glissando.style = #'%s" % style) def ly_expression (self): return {-1: '\\glissando', 1:''}.get (self.span_direction, '') class ArpeggioEvent(Event): def __init__ (self): Event.__init__ (self) self.direction = 0 self.non_arpeggiate = False def wait_for_note (self): return True def print_before_note (self, printer): if self.non_arpeggiate: printer.dump ("\\arpeggioBracket") else: dir = { -1: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.get (self.direction, '') if dir: printer.dump (dir) def print_after_note (self, printer): if self.non_arpeggiate or self.direction: printer.dump ("\\arpeggioNormal") def ly_expression (self): return ('\\arpeggio') class TieEvent(Event): def ly_expression (self): return '~' class HairpinEvent (SpanEvent): def set_span_type (self, type): self.span_type = {'crescendo' : 1, 'decrescendo' :-1, 'diminuendo' :-1 }.get (type, 0) def hairpin_to_ly (self): if self.span_direction == 1: return '\!' else: return {1: '\<', -1: '\>'}.get (self.span_type, '') def direction_mod (self): return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-') def ly_expression (self): return self.hairpin_to_ly () def print_ly (self, printer): val = self.hairpin_to_ly () if val: # printer.dump (val) printer.dump ('%s%s' % (self.direction_mod (), val)) class DynamicsEvent (Event): def __init__ (self): Event.__init__ (self) self.type = None self.force_direction = 0 def wait_for_note (self): return True def ly_expression (self): if self.type: return '\%s' % self.type else: return def direction_mod (self): return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-') def print_ly (self, printer): if self.type: printer.dump ('%s\\%s' % (self.direction_mod (), self.type)) class MarkEvent (Event): def __init__ (self, text="\\default"): Event.__init__ (self) self.mark = text def wait_for_note (self): return False def ly_contents (self): if self.mark: return '%s' % self.mark else: return "\"ERROR\"" def ly_expression (self): return '\\mark %s' % self.ly_contents () class MusicGlyphMarkEvent (MarkEvent): def ly_contents (self): if self.mark: return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark else: return '' class TextEvent (Event): def __init__ (self): Event.__init__ (self) self.Text = None self.force_direction = None self.markup = '' def wait_for_note (self): """ This is problematic: the lilypond-markup ^"text" requires wait_for_note to be true. Otherwise the compilation will fail. So we are forced to set return to True. But in some cases this might lead to a wrong placement of the text. In case of words like Allegro the text should be put in a '\tempo'-command. In this case we don't want to wait for the next note. In some other cases the text is supposed to be used in a '\mark\markup' construct. We would not want to wait for the next note either. There might be other problematic situations. In the long run we should differentiate between various contexts in MusicXML, e.g. the following markup should be interpreted as '\tempo "Allegretto"': Allegretto In the mean time arising problems have to be corrected manually after the conversion. """ return True def direction_mod (self): """ 1: placement="above"; -1: placement="below"; 0: no placement attribute. see musicxml_direction_to_indicator in musicxml2ly_conversion.py """ return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-') def ly_expression (self): base_string = '%s\"%s\"' if self.markup: base_string = '%s\markup{ ' + self.markup + ' {%s} }' return base_string % (self.direction_mod (), self.text) class ArticulationEvent (Event): def __init__ (self): Event.__init__ (self) self.type = None self.force_direction = None def wait_for_note (self): return True def direction_mod (self): return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '') def ly_expression (self): return '%s\\%s' % (self.direction_mod (), self.type) class ShortArticulationEvent (ArticulationEvent): def direction_mod (self): # default is - return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-') def ly_expression (self): if self.type: return '%s%s' % (self.direction_mod (), self.type) else: return '' class NoDirectionArticulationEvent (ArticulationEvent): def is_breathing_sign(self): return self.type == 'breathe' def print_after_note(self, printer): # The breathing sign should, according to current LilyPond # praxis, be treated as an independent musical # event. Consequently, it should be printed _after_ the note # to which it is attached. if self.is_breathing_sign(): printer.dump(r'\breathe') def ly_expression (self): if self.type and not self.is_breathing_sign(): return '\\%s' % self.type else: return '' class MarkupEvent (ShortArticulationEvent): def __init__ (self): ArticulationEvent.__init__ (self) self.contents = None def ly_expression (self): if self.contents: return "%s\\markup { %s }" % (self.direction_mod (), self.contents) else: return '' class FretEvent (MarkupEvent): def __init__ (self): MarkupEvent.__init__ (self) self.force_direction = 1 self.strings = 6 self.frets = 4 self.barre = None self.elements = [] def ly_expression (self): val = "" if self.strings <> 6: val += "w:%s;" % self.strings if self.frets <> 4: val += "h:%s;" % self.frets if self.barre and len (self.barre) >= 3: val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2]+get_transpose("integer")) have_fingering = False for i in self.elements: if len (i) > 1: val += "%s-%s" % (i[0], i[1]+(get_transpose("integer"),'')[isinstance(i[1],str)]) if len (i) > 2: have_fingering = True val += "-%s" % i[2] val += ";" if have_fingering: val = "f:1;" + val if val: return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val) else: return '' class FretBoardNote (Music): def __init__ (self): Music.__init__ (self) self.pitch = None self.string = None self.fingering = None def ly_expression (self): str = self.pitch.ly_expression() if self.fingering: str += "-%s" % self.fingering if self.string: str += "\%s" % self.string return str class FretBoardEvent (NestedMusic): def __init__ (self): NestedMusic.__init__ (self) self.duration = None def print_ly (self, printer): fretboard_notes = [n for n in self.elements if isinstance (n, FretBoardNote)] if fretboard_notes: notes = [] for n in fretboard_notes: notes.append (n.ly_expression ()) contents = string.join (notes) printer ('<%s>%s' % (contents,self.duration)) class FunctionWrapperEvent (Event): def __init__ (self, function_name=None): Event.__init__ (self) self.function_name = function_name def pre_note_ly (self, is_chord_element): if self.function_name: return "\\%s" % self.function_name else: return '' def pre_chord_ly (self): return '' def ly_expression (self): if self.function_name: return "\\%s" % self.function_name else: return '' class ParenthesizeEvent (FunctionWrapperEvent): def __init__ (self): FunctionWrapperEvent.__init__ (self, "parenthesize") class StemEvent (Event): """" A class to take care of stem values (up, down, double, none) """ def __init__ (self): Event.__init__ (self) self.value = None def pre_chord_ly (self): if self.value: return "\\%s" % self.value else: return '' def pre_note_ly (self, is_chord_element): return '' def ly_expression (self): return self.pre_chord_ly () class NotestyleEvent (Event): #class changed by DaLa: additional attribute color def __init__ (self): Event.__init__ (self) self.style = None self.filled = None self.color = None def pre_chord_ly (self): return_string = '' if self.style: return_string += " \\once \\override NoteHead #'style = #%s" % self.style if self.color: return_string += " \\once \\override NoteHead #'color = #(rgb-color %s %s %s)" % (self.color[0], self.color[1], self.color[2]) return return_string def pre_note_ly (self, is_chord_element): if self.style and is_chord_element: return "\\tweak style #%s" % self.style else: return '' def ly_expression (self): return self.pre_chord_ly () class StemstyleEvent (Event): #class added by DaLa def __init__ (self): Event.__init__ (self) self.color = None def pre_chord_ly (self): if self.color: return "\\once \\override Stem #'color = #(rgb-color %s %s %s)" % (self.color[0], self.color[1], self.color[2]) else: return '' def pre_note_ly (self, is_chord_element): return '' def ly_expression (self): return self.pre_chord_ly () class ChordPitch: def __init__ (self): self.alteration = 0 self.step = 0 def __repr__(self): return self.ly_expression() def ly_expression (self): return pitch_generating_function (self) class ChordModification: def __init__ (self): self.alteration = 0 self.step = 0 self.type = 0 def ly_expression (self): if self.type: val = {1: ".", -1: "^" }.get (self.type, "") val += "%s" % self.step val += {1: "+", -1: "-"}.get (self.alteration, "") return val else: return '' class ChordNameEvent (Event): def __init__ (self): Event.__init__ (self) self.root = None self.kind = None self.duration = None self.modifications = [] self.bass = None def add_modification (self, mod): self.modifications.append (mod) def ly_expression (self): if not self.root: return '' value = self.root.ly_expression () if self.duration: value += self.duration.ly_expression () if self.kind: value = self.kind.format(value) # First print all additions/changes, and only afterwards all subtractions for m in self.modifications: if m.type == 1: value += m.ly_expression () for m in self.modifications: if m.type == -1: value += m.ly_expression () if self.bass: value += "/+%s" % self.bass.ly_expression () return value class TremoloEvent(ArticulationEvent): def __init__(self): Event.__init__(self) self.strokes = 0 def ly_expression(self): ly_str = '' if self.strokes and int(self.strokes) > 0: # ly_dur is a global variable defined in class Duration # ly_dur stores the value of the reciprocal values of notes # ly_dur is used here to check the current note duration # if the duration is smaller than 8, e.g. # quarter, half and whole notes, # `:(2 ** (2 + number of tremolo strokes))' # should be appended to the pitch and duration, e.g. # 1 stroke: `c4:8' or `c2:8' or `c1:8' # 2 strokes: `c4:16' or `c2:16' or `c1:16' # ... # else (if ly_dur is equal to or greater than 8): # we need to make sure that the tremolo value that is to # be appended to the pitch and duration is twice the # duration (if there is only one tremolo stroke. # Each additional stroke doubles the tremolo value, e.g.: # 1 stroke: `c8:16', `c16:32', `c32:64', ... # 2 strokes: `c8:32', `c16:64', `c32:128', ... # ... if ly_dur < 8: ly_str += ':%s' % (2 ** (2 + int(self.strokes))) else: ly_str += ':%s' % (2 ** int((math.log(ly_dur, 2)) + int(self.strokes))) return ly_str class BendEvent (ArticulationEvent): def __init__ (self): Event.__init__ (self) self.alter = None def ly_expression (self): if self.alter != None: return "-\\bendAfter #%s" % self.alter else: return '' class RhythmicEvent(Event): def __init__ (self): Event.__init__ (self) self.duration = Duration() self.associated_events = [] def add_associated_event (self, ev): if ev: self.associated_events.append (ev) def pre_chord_ly (self): return [ev.pre_chord_ly () for ev in self.associated_events] def pre_note_ly (self, is_chord_element): return [ev.pre_note_ly (is_chord_element) for ev in self.associated_events] def ly_expression_pre_note (self, is_chord_element): res = string.join (self.pre_note_ly (is_chord_element), ' ') if res != '': res = res + ' ' return res def get_length (self): return self.duration.get_length() def get_properties (self): return ("'duration %s" % self.duration.lisp_expression ()) class RestEvent (RhythmicEvent): def __init__ (self): RhythmicEvent.__init__ (self) self.pitch = None def ly_expression (self): res = self.ly_expression_pre_note (False) if self.pitch: return res + "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ()) else: return 'r%s' % self.duration.ly_expression () def print_ly (self, printer): for ev in self.associated_events: ev.print_ly (printer) # if hasattr(self, 'color'): # printer.print_note_color("NoteHead", self.color) # printer.print_note_color("Stem", self.color) # printer.print_note_color("Beam", self.color) if self.pitch: self.pitch.print_ly (printer) self.duration.print_ly (printer) printer ('\\rest') else: printer('r') self.duration.print_ly (printer) class SkipEvent (RhythmicEvent): def ly_expression (self): return 's%s' % self.duration.ly_expression () class NoteEvent(RhythmicEvent): def __init__ (self): RhythmicEvent.__init__ (self) #self.pitch = None self.drum_type = None self.cautionary = False self.forced_accidental = False def get_properties (self): str = RhythmicEvent.get_properties (self) if self.pitch: str += self.pitch.lisp_expression () elif self.drum_type: str += "'drum-type '%s" % self.drum_type return str def pitch_mods (self): excl_question = '' if self.cautionary: excl_question += '?' if self.forced_accidental: excl_question += '!' return excl_question def ly_expression (self): # obtain all stuff that needs to be printed before the note: res = self.ly_expression_pre_note (True) if self.pitch: return res + '%s%s%s' % (self.pitch.ly_expression (), self.pitch_mods(), self.duration.ly_expression ()) elif self.drum_type: return res + '%s%s' (self.drum_type, self.duration.ly_expression ()) def chord_element_ly (self): # obtain all stuff that needs to be printed before the note: res = self.ly_expression_pre_note (True) if self.pitch: return res + '%s%s' % (self.pitch.ly_expression (), self.pitch_mods()) elif self.drum_type: return res + '%s%s' (self.drum_type) def print_ly (self, printer): for ev in self.associated_events: ev.print_ly (printer) if hasattr(self, 'color'): printer.print_note_color("NoteHead", self.color) printer.print_note_color("Stem", self.color) printer.print_note_color("Beam", self.color) if self.pitch: self.pitch.print_ly (printer) printer (self.pitch_mods ()) else: printer (self.drum_type) self.duration.print_ly (printer) # if hasattr(self, 'color'): # printer.print_note_color("NoteHead") # printer.print_note_color("Stem") # printer.print_note_color("Beam") class KeySignatureChange (Music): def __init__ (self): Music.__init__ (self) self.tonic = None self.mode = 'major' self.non_standard_alterations = None def format_non_standard_alteration (self, a): alter_dict = { -2: ",DOUBLE-FLAT", - 1.5: ",THREE-Q-FLAT", - 1: ",FLAT", - 0.5: ",SEMI-FLAT", 0: ",NATURAL", 0.5: ",SEMI-SHARP", 1: ",SHARP", 1.5: ",THREE-Q-SHARP", 2: ",DOUBLE-SHARP"} try: accidental = alter_dict[a[1]] except KeyError: ly.warning (_ ("Unable to convert alteration %s to a lilypond expression") % a[1]) return '' if len (a) == 2: return "( %s . %s )" % (a[0], accidental) elif len (a) == 3: return "(( %s . %s ) . %s )" % (a[2], a[0], accidental) else: return '' def ly_expression (self): if self.tonic: return '\\key %s \\%s' % (self.tonic.ly_step_expression (), self.mode) elif self.non_standard_alterations: alterations = [self.format_non_standard_alteration (a) for a in self.non_standard_alterations] return "\\set Staff.keyAlterations = #`(%s)" % string.join (alterations, " ") else: return '' class ShiftDurations (MusicWrapper): def __init__ (self): MusicWrapper.__init__ (self) self.params = [0,0] def set_shift_durations_parameters(self, timeSigChange): self.params = timeSigChange.get_shift_durations_parameters() def print_ly (self, func): func (' \\shiftDurations #%d #%d ' % tuple(self.params)) MusicWrapper.print_ly (self, func) class TimeSignatureChange (Music): def __init__ (self): Music.__init__ (self) self.fractions = [4, 4] self.style = None # Used for the --time-signature option of musicxml2ly self.originalFractions = [4, 4] def get_fractions_ratio (self): """ Calculate the ratio between the original time fraction and the new one. Used for the "--time-signature" option. @return: The ratio between the two time fractions. @rtype: float """ return (float(self.originalFractions[0])/self.originalFractions[1])*(float(self.fractions[1])/self.fractions[0]) def get_shift_durations_parameters (self): dur = math.ceil(math.log(self.get_fractions_ratio(),2)) dots = (1/self.get_fractions_ratio())/(math.pow(2,-dur)) dots = int(math.log(2-dots,0.5)) return [dur, dots] def format_fraction (self, frac): if isinstance (frac, list): l = [self.format_fraction (f) for f in frac] return "(" + string.join (l, " ") + ")" else: return "%s" % frac def ly_expression (self): st = '' # Print out the style if we have ome, but the '() should only be # forced for 2/2 or 4/4, since in all other cases we'll get numeric # signatures anyway despite the default 'C signature style! is_common_signature = self.fractions in ([2, 2], [4, 4], [4, 2]) if self.style: if self.style == "common": st = "\\defaultTimeSignature" elif (self.style != "'()"): st = "\\once \\override Staff.TimeSignature.style = #%s " % self.style elif (self.style != "'()") or is_common_signature: st = "\\numericTimeSignature" # Easy case: self.fractions = [n,d] => normal \time n/d call: if len (self.fractions) == 2 and isinstance (self.fractions[0], int): return st + '\\time %d/%d ' % tuple (self.fractions) elif self.fractions: return st + "\\compoundMeter #'%s" % self.format_fraction (self.fractions) else: return st + '' class ClefChange (Music): def __init__ (self): Music.__init__ (self) self.type = 'G' self.position = 2 self.octave = 0 def octave_modifier (self): return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '') def clef_name (self): return {('G', 2): "treble", ('G', 1): "french", ('C', 1): "soprano", ('C', 2): "mezzosoprano", ('C', 3): "alto", ('C', 4): "tenor", ('C', 5): "baritone", ('F', 3): "varbaritone", ('F', 4): "bass", ('F', 5): "subbass", ("percussion", 2): "percussion", # Workaround: MuseScore uses PERC instead of percussion ("PERC", 2): "percussion", ("TAB", 5): get_tab_clef ()}.get ((self.type, self.position), None) def ly_expression (self): return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ()) clef_dict = { "G": ("clefs.G", -2, -6), "C": ("clefs.C", 0, 0), "F": ("clefs.F", 2, 6), } def lisp_expression (self): try: (glyph, pos, c0) = self.clef_dict[self.type] except KeyError: return "" clefsetting = """ (make-music 'SequentialMusic 'elements (list (context-spec-music (make-property-set 'clefGlyph "%s") 'Staff) (context-spec-music (make-property-set 'clefPosition %d) 'Staff) (context-spec-music (make-property-set 'middleCPosition %d) 'Staff))) """ % (glyph, pos, c0) return clefsetting class Transposition (Music): def __init__ (self): Music.__init__ (self) self.pitch = None def ly_expression (self): self.pitch._force_absolute_pitch = True return '\\transposition %s' % self.pitch.ly_expression () class StaffChange (Music): def __init__ (self, staff): Music.__init__ (self) self.staff = staff def ly_expression (self): if self.staff: return "\\change Staff=\"%s\"" % self.staff else: return '' class SetEvent (Music): def __init__ (self, contextprop, value): Music.__init__ (self) self.context_prop = contextprop self.value = value def ly_expression (self): if self.value: return "\\set %s = %s" % (self.context_prop, self.value) else: return '' class StaffLinesEvent (Music): def __init__ (self, lines): Music.__init__ (self) self.lines = lines def ly_expression (self): if (self.lines > 0): return "\\stopStaff \\override Staff.StaffSymbol.line-count = #%s \\startStaff" % self.lines else: return "\\stopStaff \\revert Staff.StaffSymbol.line-count \\startStaff" class TempoMark (Music): def __init__ (self): Music.__init__ (self) self.baseduration = None self.newduration = None self.beats = None self.parentheses = False def set_base_duration (self, dur): self.baseduration = dur def set_new_duration (self, dur): self.newduration = dur def set_beats_per_minute (self, beats): self.beats = beats def set_parentheses (self, parentheses): self.parentheses = parentheses def wait_for_note (self): return False def duration_to_markup (self, dur): if dur: # Generate the markup to print the note, use scheme mode for # ly_expression to get longa and not \longa (which causes an error) return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True) else: return '' def tempo_markup_template (self): return "\\mark\\markup { \\fontsize #-2 \\line { %s } }" def ly_expression (self): res = '' if not self.baseduration: return res if self.beats: if self.parentheses: res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats) else: res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats) elif self.newduration: dm = self.duration_to_markup (self.baseduration) ndm = self.duration_to_markup (self.newduration) if self.parentheses: contents = "\"(\" %s = %s \")\"" % (dm, ndm) else: contents = " %s = %s " % (dm, ndm) res += self.tempo_markup_template() % contents else: return '' return res class FiguredBassNote (Music): def __init__ (self): Music.__init__ (self) self.number = '' self.prefix = '' self.suffix = '' def set_prefix (self, prefix): self.prefix = prefix def set_suffix (self, suffix): self.prefix = suffix def set_number (self, number): self.number = number def ly_expression (self): res = '' if self.number: res += self.number else: res += '_' if self.prefix: res += self.prefix if self.suffix: res += self.suffix return res class FiguredBassEvent (NestedMusic): def __init__ (self): NestedMusic.__init__ (self) self.duration = None self.real_duration = 0 self.parentheses = False return def set_duration (self, dur): self.duration = dur def set_parentheses (self, par): self.parentheses = par def set_real_duration (self, dur): self.real_duration = dur def print_ly (self, printer): figured_bass_events = [e for e in self.elements if isinstance (e, FiguredBassNote)] if figured_bass_events: notes = [] for x in figured_bass_events: notes.append (x.ly_expression ()) contents = string.join (notes) if self.parentheses: contents = '[%s]' % contents printer ('<%s>' % contents) self.duration.print_ly (printer) class MultiMeasureRest(Music): def lisp_expression (self): return """ (make-music 'MultiMeasureRestMusicGroup 'elements (list (make-music (quote BarCheck)) (make-music 'ChordEvent 'elements (list (make-music 'MultiMeasureRestEvent 'duration %s))) (make-music (quote BarCheck)))) """ % self.duration.lisp_expression () def ly_expression (self): return 'R%s' % self.duration.ly_expression () class Break (Music): def __init__ (self, tp="break"): Music.__init__ (self) self.type = tp def print_ly (self, printer): if self.type: printer.dump ("\\%s" % self.type) class StaffGroup: def __init__ (self, command="StaffGroup"): self.stafftype = command self.id = None self.instrument_name = None self.sound = None self.short_instrument_name = None self.symbol = None self.spanbar = None self.children = [] self.is_group = True self.context_modifications = [] # part_information is a list with entries of the form # [staffid, voicelist] # where voicelist is a list with entries of the form # [voiceid1, [lyricsid11, lyricsid12,...] ] self.part_information = None def append_staff (self, staff): self.children.append (staff) def set_part_information (self, part_name, staves_info): if part_name == self.id: self.part_information = staves_info else: for c in self.children: c.set_part_information (part_name, staves_info) def add_context_modification (self, modification): self.context_modifications.append (modification) def print_ly_contents (self, printer): for c in self.children: if c: c.print_ly (printer) #Intention: I want to put the content of new StaffGroup in angled brackets (<< >>) #printer.dump ("test")# test is printed twice at the end of a staffgroup with two staves. #printer ("test") # test is printed twice at the end of a staffgroup with two staves. def needs_with (self): needs_with = False needs_with |= self.spanbar == "no" needs_with |= self.instrument_name != None needs_with |= self.short_instrument_name != None needs_with |= (self.symbol != None) and (self.symbol != "bracket") return needs_with def print_ly_context_mods (self, printer): if self.instrument_name or self.short_instrument_name: printer.dump ("\\consists \"Instrument_name_engraver\"") if self.spanbar == "no": printer.dump ("\\hide SpanBar") brack = {"brace": "SystemStartBrace", "none": "SystemStartBar", "line": "SystemStartSquare"}.get (self.symbol, None) if brack: printer.dump ("systemStartDelimiter = #'%s" % brack) def print_ly_overrides (self, printer): needs_with = self.needs_with () | (len (self.context_modifications) > 0); if needs_with: printer.dump ("\\with {") self.print_ly_context_mods (printer) for m in self.context_modifications: printer.dump (m) printer.dump ("} <<") printer.newline () #print a single << after StaffGroup only when the with-block is not needed. #This doesn't work. << is printed before and after StaffGroup! #else: # printer.dump (" <<") #prints loads off << before and after StaffGroup and before \set Staff.instrumentName #elif not needs_with: # printer.dump (" <<") def print_chords(self, printer): try: for [staff_id, voices] in self.part_information: for [v, lyrics, figuredbass, chordnames, fretboards] in voices: if chordnames: printer ('\context ChordNames = "%s" {%s \\%s}' % (chordnames, get_transpose ("string"), chordnames)) printer.newline() except TypeError: return def print_fretboards(self, printer): try: for [staff_id, voices] in self.part_information: for [v, lyrics, figuredbass, chordnames, fretboards] in voices: if fretboards: printer ('\context FretBoards = "%s" {%s \\%s}' % (fretboards, get_transpose ("string"), fretboards)) printer.newline() except TypeError: return def print_ly (self, printer): self.print_chords(printer) self.print_fretboards(printer) if self.stafftype: printer.dump ("\\new %s" % self.stafftype) self.print_ly_overrides (printer) printer.newline () if self.stafftype: printer.dump ("<<") printer.newline () if self.stafftype and self.instrument_name: printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, escape_instrument_string (self.instrument_name))) printer.newline () if self.stafftype and self.short_instrument_name: printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype, escape_instrument_string (self.short_instrument_name))) printer.newline () if self.sound: printer.dump( r'\set {stafftype}.midiInstrument = #"{sound}"'.format( stafftype=self.stafftype, sound=self.sound)) printer.newline () self.print_ly_contents (printer) printer.newline () if self.stafftype: printer.dump (">>") printer.newline () class Staff (StaffGroup): def __init__ (self, command="Staff"): StaffGroup.__init__ (self, command) self.is_group = False self.part = None self.voice_command = "Voice" self.substafftype = None self.sound = None def needs_with (self): return False def print_ly_context_mods (self, printer): #printer.dump ("test") #does nothing. pass def print_ly_contents (self, printer): if not self.id or not self.part_information: return sub_staff_type = self.substafftype if not sub_staff_type: sub_staff_type = self.stafftype #printer.dump ("test") #prints test in each staff after the definitions of the instrument name and before the definition of the contexts. printer.newline() for [staff_id, voices] in self.part_information: # now comes the real staff definition: if staff_id: printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id)) else: printer ('\\context %s << ' % sub_staff_type) printer.newline () printer.dump("\mergeDifferentlyDottedOn\mergeDifferentlyHeadedOn") printer.newline() n = 0 nr_voices = len (voices) for [v, lyrics, figuredbass, chordnames, fretboards] in voices: n += 1 voice_count_text = '' if nr_voices > 1: """ The next line contains a bug: The voices might not appear in numerical order! Some voices might be missing e.g. if the xml file contains only voice one, three and four, this would result in: \voiceOne, \voiceTwo and \voiceThree. This causes wrong stem directions and collisions. """ voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo', 3: ' \\voiceThree'}.get (n, ' \\voiceFour') printer ('\\context %s = "%s" {%s %s \\%s }' % (self.voice_command, v, get_transpose ("string"), voice_count_text, v)) printer.newline () lyrics_id = 1 for l in lyrics: printer ('\\new Lyrics \\lyricsto "%s" { \\set stanza = "%s." \\%s }' % (v, lyrics_id, l)) lyrics_id += 1 printer.newline() if figuredbass: printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass)) printer ('>>') #printer.dump ("test") #prints test after each definition of a context. #printer.newline () #printer.dump ("test") #prints test after each definition of a context. def print_ly (self, printer): if self.part_information and len (self.part_information) > 1: self.stafftype = "PianoStaff" self.substafftype = "Staff" #printer.dump ('test') StaffGroup.print_ly (self, printer) class TabStaff (Staff): def __init__ (self, command="TabStaff"): Staff.__init__ (self, command) self.string_tunings = [] self.tablature_format = None self.voice_command = "TabVoice" def print_ly_overrides (self, printer): if self.string_tunings or self.tablature_format: printer.dump ("\\with {") if self.string_tunings: printer.dump ("stringTunings = #`(") for i in self.string_tunings: printer.dump (",%s" % i.lisp_expression ()) printer.dump (")") if self.tablature_format: printer.dump ("tablatureFormat = #%s" % self.tablature_format) printer.dump ("}") class DrumStaff (Staff): def __init__ (self, command="DrumStaff"): Staff.__init__ (self, command) self.drum_style_table = None self.voice_command = "DrumVoice" def print_ly_overrides (self, printer): if self.drum_style_table: printer.dump ("\with {") printer.dump ("drumStyleTable = #%s" % self.drum_style_table) printer.dump ("}") class RhythmicStaff (Staff): def __init__ (self, command="RhythmicStaff"): Staff.__init__ (self, command) #Test #def print_staffgroup_closing_brackets (self, printer): #test see class Score / class Staff # printer.dump ("test") class Score: def __init__ (self): """ Constructs a new Score object. """ self.contents = None self.create_midi = False def set_contents (self, contents): self.contents = contents def set_part_information (self, part_id, staves_info): if self.contents: self.contents.set_part_information (part_id, staves_info) def set_tempo (self, tempo): """ Set the tempo attribute of the Score. This attribute can be used in L{print_ly} for the midi output (see L{musicxml.Sound}). @param tempo: The value of the tempo, in beats per minute. @type tempo: String """ self.tempo = tempo #Test # def print_staffgroup_closing_brackets (self, printer): #test see class Score / class Staff # printer.dump ("test") def print_ly (self, printer): """ Print the content of the score to the printer, in lilypond format. @param printer: A printer given to display correctly the output. @type printer: L{Output_printer} """ self.create_midi = get_create_midi() printer.dump("\\score {") printer.newline () #prints opening <<: printer.dump ('<<') printer.newline () if self.contents: self.contents.print_ly(printer) #printer.dump ("test") prints test once before the >> of the score block, independent of the existence of a staffgroup. #if StaffGroup == False: # True or False: nothing happens. # printer.dump ('>>') printer.dump ('>>') printer.newline () #StaffGroup.print_staffgroup_closing_brackets(self, printer) #TypeError: unbound method print_staffgroup_closing_brackets() must be called with StaffGroup instance as first argument (got Score instance instead) #print_staffgroup_closing_brackets(self, printer) #NameError: global name 'print_staffgroup_closing_brackets' is not defined. prints test once before the >> of the score block, independent of the existence of a staffgroup. printer.dump ("\\layout {}") printer.newline () # If the --midi option was not passed to musicxml2ly, that comments the "midi" line if self.create_midi: printer.dump ("}") printer.newline() printer.dump("\\score {") printer.newline () printer.dump("\\unfoldRepeats \\articulate {") printer.newline () self.contents.print_ly(printer) printer.dump("}") printer.newline () else: printer.dump ("% To create MIDI output, uncomment the following line:") printer.newline () printer.dump ("% ") printer.dump ("\\midi {\\tempo 4 = "+self.tempo+" }") printer.newline () printer.dump ("}") printer.newline () def test_pitch (): bflat = Pitch() bflat.alteration = -1 bflat.step = 6 bflat.octave = -1 fifth = Pitch() fifth.step = 4 down = Pitch () down.step = -4 down.normalize () print bflat.semitones() print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth) print bflat.transposed (fifth).transposed (fifth).transposed (fifth) print bflat.semitones(), 'down' print bflat.transposed (down) print bflat.transposed (down).transposed (down) print bflat.transposed (down).transposed (down).transposed (down) def test_printer (): def make_note (): evc = ChordEvent() n = NoteEvent() evc.append (n) return n def make_tup (): m = SequentialMusic() m.append (make_note ()) m.append (make_note ()) m.append (make_note ()) t = TimeScaledMusic () t.numerator = 2 t.denominator = 3 t.element = m return t m = SequentialMusic () m.append (make_tup ()) m.append (make_tup ()) m.append (make_tup ()) printer = Output_printer() m.print_ly (printer) printer.newline () def test_expr (): m = SequentialMusic() l = 2 evc = ChordEvent() n = NoteEvent() n.duration.duration_log = l n.pitch.step = 1 evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) evc = ChordEvent() n = NoteEvent() n.duration.duration_log = l n.pitch.step = 3 evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) evc = ChordEvent() n = NoteEvent() n.duration.duration_log = l n.pitch.step = 2 evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) evc = ClefChange() evc.type = 'treble' m.insert_around (None, evc, 0) evc = ChordEvent() tonic = Pitch () tonic.step = 2 tonic.alteration = -2 n = KeySignatureChange() n.tonic = tonic.copy() n.scale = [0, 0, -2, 0, 0, -2, -2] evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) return m if __name__ == '__main__': test_printer () raise 'bla' test_pitch() expr = test_expr() expr.set_start (Rational (0)) print expr.ly_expression() start = Rational (0, 4) stop = Rational (4, 2) def sub(x, start=start, stop=stop): ok = x.start >= start and x.start + x.get_length() <= stop return ok print expr.lisp_sub_expression(sub)