6 from rational import Rational
8 # Store previously converted pitch for \relative conversion as a global state variable
10 relative_pitches = False
12 def escape_instrument_string (input_string):
13 retstring = string.replace (input_string, "\"", "\\\"")
14 if re.match ('.*[\r\n]+.*', retstring):
15 rx = re.compile (r'[\n\r]+')
16 strings = rx.split (retstring)
17 retstring = "\\markup { \\column { "
19 retstring += "\\line {\"" + s + "\"} "
22 retstring = "\"" + retstring + "\""
25 class Output_stack_element:
27 self.factor = Rational (1)
29 o = Output_stack_element()
30 o.factor = self.factor
35 """A class that takes care of formatting (eg.: indenting) a
36 Music expression as a .ly file.
39 ## TODO: support for \relative.
45 self._file = sys.stdout
47 self._output_state_stack = [Output_stack_element()]
48 self._skipspace = False
49 self._last_duration = None
51 def set_file (self, file):
54 def dump_version (self):
56 self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
59 def get_indent (self):
60 return self._nesting * self._indent
63 last = self._output_state_stack[-1]
64 self._output_state_stack.append (last.copy())
66 def add_factor (self, factor):
68 self._output_state_stack[-1].factor *= factor
71 del self._output_state_stack[-1]
72 if not self._output_state_stack:
75 def duration_factor (self):
76 return self._output_state_stack[-1].factor
78 def print_verbatim (self, str):
81 def unformatted_output (self, str):
82 # don't indent on \< and indent only once on <<
83 self._nesting += ( str.count ('<')
84 - str.count ('\<') - str.count ('<<')
86 self._nesting -= ( str.count ('>') - str.count ('\>') - str.count ('>>')
87 - str.count ('->') - str.count ('_>')
90 self.print_verbatim (str)
92 def print_duration_string (self, str):
93 if self._last_duration == str:
96 self.unformatted_output (str)
98 def add_word (self, str):
99 if (len (str) + 1 + len (self._line) > self._line_len):
101 self._skipspace = True
103 if not self._skipspace:
105 self.unformatted_output (str)
106 self._skipspace = False
109 self._file.write (self._line + '\n')
110 self._line = ' ' * self._indent * self._nesting
111 self._skipspace = True
113 def skipspace (self):
114 self._skipspace = True
116 def __call__(self, arg):
119 def dump (self, str):
121 self._skipspace = False
122 self.unformatted_output (str)
124 words = string.split (str)
137 self.duration_log = 0
139 self.factor = Rational (1)
141 def lisp_expression (self):
142 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
144 self.factor.numerator (),
145 self.factor.denominator ())
148 def ly_expression (self, factor = None):
152 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
154 if factor <> Rational (1,1):
155 if factor.denominator () <> 1:
156 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
158 str += '*%d' % factor.numerator ()
162 def print_ly (self, outputter):
163 str = self.ly_expression (self.factor / outputter.duration_factor ())
164 outputter.print_duration_string (str)
167 return self.ly_expression()
172 d.duration_log = self.duration_log
173 d.factor = self.factor
176 def get_length (self):
177 dot_fact = Rational( (1 << (1 + self.dots))-1,
180 log = abs (self.duration_log)
182 if self.duration_log < 0:
183 base = Rational (dur)
185 base = Rational (1, dur)
187 return base * dot_fact * self.factor
190 # Implement the different note names for the various languages
191 def pitch_generic (pitch, notenames, accidentals):
192 str = notenames[pitch.step]
193 if pitch.alteration < 0:
194 str += accidentals[0] * (-pitch.alteration)
195 elif pitch.alteration > 0:
196 str += accidentals[3] * (pitch.alteration)
199 def pitch_general (pitch):
200 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['es', 'eh', 'ih', 'is'])
201 return str.replace ('aes', 'as').replace ('ees', 'es')
203 def pitch_nederlands (pitch):
204 return pitch_general (pitch)
206 def pitch_english (pitch):
207 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['f', 'qf', 'qs', 's'])
208 return str.replace ('aes', 'as').replace ('ees', 'es')
210 def pitch_deutsch (pitch):
211 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['es', 'eh', 'ih', 'is'])
212 return str.replace ('hes', 'b').replace ('aes', 'as').replace ('ees', 'es')
214 def pitch_norsk (pitch):
215 return pitch_deutsch (pitch)
217 def pitch_svenska (pitch):
218 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', '', '', 'iss'])
219 return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es')
221 def pitch_italiano (pitch):
222 str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd'])
225 def pitch_catalan (pitch):
226 return pitch_italiano (pitch)
228 def pitch_espanol (pitch):
229 str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 's'])
232 def pitch_vlaams (pitch):
233 str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 'k'])
236 def set_pitch_language (language):
237 global pitch_generating_function
239 "nederlands": pitch_nederlands,
240 "english": pitch_english,
241 "deutsch": pitch_deutsch,
242 "norsk": pitch_norsk,
243 "svenska": pitch_svenska,
244 "italiano": pitch_italiano,
245 "catalan": pitch_catalan,
246 "espanol": pitch_espanol,
247 "vlaams": pitch_vlaams}
248 pitch_generating_function = function_dict.get (language, pitch_general)
250 # global variable to hold the formatting function.
251 pitch_generating_function = pitch_general
261 return self.ly_expression()
263 def transposed (self, interval):
265 c.alteration += interval.alteration
266 c.step += interval.step
267 c.octave += interval.octave
270 target_st = self.semitones() + interval.semitones()
271 c.alteration += target_st - c.semitones()
278 c.octave += c.step / 7
281 def lisp_expression (self):
282 return '(ly:make-pitch %d %d %d)' % (self.octave,
288 p.alteration = self.alteration
290 p.octave = self.octave
294 return self.step + self.octave *7
296 def semitones (self):
297 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
299 def ly_step_expression (self):
300 return pitch_generating_function (self)
302 def absolute_pitch (self):
304 return "'" * (self.octave + 1)
305 elif self.octave < -1:
306 return "," * (-self.octave - 1)
310 def relative_pitch (self):
311 global previous_pitch
312 if not previous_pitch:
313 previous_pitch = self
314 return self.absolute_pitch ()
315 previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step
316 this_pitch_steps = self.octave * 7 + self.step
317 pitch_diff = (this_pitch_steps - previous_pitch_steps)
318 previous_pitch = self
320 return "'" * ((pitch_diff + 3) / 7)
321 elif pitch_diff < -3:
322 return "," * ((-pitch_diff + 3) / 7)
326 def ly_expression (self):
327 str = self.ly_step_expression ()
329 str += self.relative_pitch ()
331 str += self.absolute_pitch ()
335 def print_ly (self, outputter):
336 outputter (self.ly_expression())
341 self.start = Rational (0)
343 self.identifier = None
345 def get_length(self):
348 def get_properties (self):
351 def has_children (self):
354 def get_index (self):
356 return self.parent.elements.index (self)
360 return self.__class__.__name__
362 def lisp_expression (self):
365 props = self.get_properties ()
367 return "(make-music '%s %s)" % (name, props)
369 def set_start (self, start):
372 def find_first (self, predicate):
377 def print_comment (self, printer, text = None):
388 lines = string.split (text, '\n')
391 printer.unformatted_output ('% ' + l)
395 def print_with_identifier (self, printer):
397 printer ("\\%s" % self.identifier)
399 self.print_ly (printer)
401 def print_ly (self, printer):
402 printer (self.ly_expression ())
404 class MusicWrapper (Music):
408 def print_ly (self, func):
409 self.element.print_ly (func)
411 class ModeChangingMusicWrapper (MusicWrapper):
413 MusicWrapper.__init__ (self)
414 self.mode = 'notemode'
416 def print_ly (self, func):
417 func ('\\%s' % self.mode)
418 MusicWrapper.print_ly (self, func)
420 class RelativeMusic (MusicWrapper):
422 MusicWrapper.__init__ (self)
423 self.basepitch = None
425 def print_ly (self, func):
426 global previous_pitch
427 global relative_pitches
428 prev_relative_pitches = relative_pitches
429 relative_pitches = True
430 previous_pitch = self.basepitch
431 if not previous_pitch:
432 previous_pitch = Pitch ()
433 func ('\\relative %s%s' % (pitch_generating_function (previous_pitch),
434 previous_pitch.absolute_pitch ()))
435 MusicWrapper.print_ly (self, func)
436 relative_pitches = prev_relative_pitches
438 class TimeScaledMusic (MusicWrapper):
439 def print_ly (self, func):
440 func ('\\times %d/%d ' %
441 (self.numerator, self.denominator))
442 func.add_factor (Rational (self.numerator, self.denominator))
443 MusicWrapper.print_ly (self, func)
446 class NestedMusic(Music):
448 Music.__init__ (self)
451 def append (self, what):
453 self.elements.append (what)
455 def has_children (self):
458 def insert_around (self, succ, elt, dir):
459 assert elt.parent == None
460 assert succ == None or succ in self.elements
465 idx = self.elements.index (succ)
472 idx = len (self.elements)
474 self.elements.insert (idx, elt)
477 def get_properties (self):
478 return ("'elements (list %s)"
479 % string.join (map (lambda x: x.lisp_expression(),
482 def get_subset_properties (self, predicate):
483 return ("'elements (list %s)"
484 % string.join (map (lambda x: x.lisp_expression(),
485 filter ( predicate, self.elements))))
486 def get_neighbor (self, music, dir):
487 assert music.parent == self
488 idx = self.elements.index (music)
490 idx = min (idx, len (self.elements) -1)
493 return self.elements[idx]
495 def delete_element (self, element):
496 assert element in self.elements
498 self.elements.remove (element)
499 element.parent = None
501 def set_start (self, start):
503 for e in self.elements:
506 def find_first (self, predicate):
507 r = Music.find_first (self, predicate)
511 for e in self.elements:
512 r = e.find_first (predicate)
517 class SequentialMusic (NestedMusic):
518 def get_last_event_chord (self):
520 at = len( self.elements ) - 1
522 not isinstance (self.elements[at], EventChord) and
523 not isinstance (self.elements[at], BarLine)):
526 if (at >= 0 and isinstance (self.elements[at], EventChord)):
527 value = self.elements[at]
530 def print_ly (self, printer, newline = True):
533 self.print_comment (printer)
537 for e in self.elements:
544 def lisp_sub_expression (self, pred):
548 props = self.get_subset_properties (pred)
550 return "(make-music '%s %s)" % (name, props)
552 def set_start (self, start):
553 for e in self.elements:
555 start += e.get_length()
559 self.repeat_type = "volta"
560 self.repeat_count = 2
563 def set_music (self, music):
564 if isinstance (music, Music):
566 elif isinstance (music, list):
567 self.music = SequentialMusic ()
568 self.music.elements = music
570 sys.stderr.write ("WARNING: Unable to set the music %s for the repeat %s" % (music, self))
571 def add_ending (self, music):
572 self.endings.append (music)
573 def print_ly (self, printer):
574 printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count))
576 self.music.print_ly (printer)
578 sys.stderr.write ("WARNING: Encountered repeat without body\n")
581 printer.dump ('\\alternative {')
582 for e in self.endings:
589 self.lyrics_syllables = []
591 def print_ly (self, printer):
592 printer.dump ("\lyricmode {")
593 for l in self.lyrics_syllables:
594 printer.dump ( "%s " % l )
597 def ly_expression (self):
598 lstr = "\lyricmode {\n "
599 for l in self.lyrics_syllables:
607 self.header_fields = {}
608 def set_field (self, field, value):
609 self.header_fields[field] = value
611 def print_ly (self, printer):
612 printer.dump ("\header {")
614 for (k,v) in self.header_fields.items ():
616 printer.dump ('%s = %s' % (k,v))
625 self.global_staff_size = -1
628 self.page_height = -1
631 self.bottom_margin = -1
632 self.left_margin = -1
633 self.right_margin = -1
634 self.system_left_margin = -1
635 self.system_right_margin = -1
636 self.system_distance = -1
637 self.top_system_distance = -1
639 def print_length_field (self, printer, field, value):
641 printer.dump ("%s = %s\\cm" % (field, value))
643 def print_ly (self, printer):
644 if self.global_staff_size > 0:
645 printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size)
647 printer.dump ('\\paper {')
649 self.print_length_field (printer, "paper-width", self.page_width)
650 self.print_length_field (printer, "paper-height", self.page_height)
651 self.print_length_field (printer, "top-margin", self.top_margin)
652 self.print_length_field (printer, "botton-margin", self.bottom_margin)
653 self.print_length_field (printer, "left-margin", self.left_margin)
654 # TODO: maybe set line-width instead of right-margin?
655 self.print_length_field (printer, "right-margin", self.right_margin)
656 # TODO: What's the corresponding setting for system_left_margin and
657 # system_right_margin in Lilypond?
658 self.print_length_field (printer, "between-system-space", self.system_distance)
659 self.print_length_field (printer, "page-top-space", self.top_system_distance)
665 class EventChord (NestedMusic):
667 NestedMusic.__init__ (self)
668 self.grace_elements = None
669 self.grace_type = None
670 def append_grace (self, element):
672 if not self.grace_elements:
673 self.grace_elements = SequentialMusic ()
674 self.grace_elements.append (element)
676 def get_length (self):
678 for e in self.elements:
679 l = max(l, e.get_length())
682 def print_ly (self, printer):
683 note_events = [e for e in self.elements if
684 isinstance (e, NoteEvent)]
686 rest_events = [e for e in self.elements if
687 isinstance (e, RhythmicEvent)
688 and not isinstance (e, NoteEvent)]
690 other_events = [e for e in self.elements if
691 not isinstance (e, RhythmicEvent)]
693 if self.grace_elements and self.elements:
695 printer ('\\%s' % self.grace_type)
698 # don't print newlines after the { and } braces
699 self.grace_elements.print_ly (printer, False)
702 rest_events[0].print_ly (printer)
703 elif len (note_events) == 1:
704 note_events[0].print_ly (printer)
706 global previous_pitch
709 for x in note_events:
710 pitches.append (x.pitch.ly_expression ())
712 basepitch = previous_pitch
713 printer ('<%s>' % string.join (pitches))
714 previous_pitch = basepitch
715 note_events[0].duration.print_ly (printer)
719 for e in other_events:
722 self.print_comment (printer)
724 class Partial (Music):
726 Music.__init__ (self)
728 def print_ly (self, printer):
730 printer.dump ("\\partial %s" % self.partial.ly_expression ())
732 class BarLine (Music):
734 Music.__init__ (self)
738 def print_ly (self, printer):
739 bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': ":",
740 'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
741 'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
742 'short': "'", 'none': "" }.get (self.type, None)
743 if bar_symbol <> None:
744 printer.dump ('\\bar "%s"' % bar_symbol)
748 if self.bar_number > 0 and (self.bar_number % 10) == 0:
749 printer.dump ("\\barNumberCheck #%d " % self.bar_number)
751 printer.print_verbatim (' %% %d' % self.bar_number)
754 def ly_expression (self):
760 class SpanEvent (Event):
762 Event.__init__ (self)
763 self.span_direction = 0 # start/stop
764 self.line_type = 'solid'
765 self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
766 self.size = 0 # size of e.g. ocrave shift
767 def wait_for_note (self):
769 def get_properties(self):
770 return "'span-direction %d" % self.span_direction
771 def set_span_type (self, type):
772 self.span_type = type
774 class SlurEvent (SpanEvent):
775 def ly_expression (self):
778 # TODO: setting dashed/dotted line style does not work, because that
779 # command needs to be written before the note, not when the
780 # event is observed after the note!
781 #before = {'dotted': '\\slurDotted',
782 # 'dashed' : '\\slurDashed'}.get (self.line_type, '')
784 #after = '\\slurSolid'
786 return {-1: before + '(' + after,
787 1:')'}.get (self.span_direction, '')
789 class BeamEvent (SpanEvent):
790 def ly_expression (self):
792 1:']'}.get (self.span_direction, '')
794 class PedalEvent (SpanEvent):
795 def ly_expression (self):
796 return {-1: '\\sustainDown',
797 1:'\\sustainUp'}.get (self.span_direction, '')
799 # type==-1 means octave up, type==-2 means octave down
800 class OctaveShiftEvent (SpanEvent):
801 def wait_for_note (self):
803 def set_span_type (self, type):
804 self.span_type = {'up': 1, 'down': -1}.get (type, 0)
805 def ly_octave_shift_indicator (self):
806 # convert 8/15 to lilypond indicators (+-1/+-2)
807 value = {8: 1, 15: 2}.get (self.size, 0)
808 # negative values go up!
809 value *= -1*self.span_type
811 def ly_expression (self):
812 dir = self.ly_octave_shift_indicator ()
815 value = '#(set-octavation %s)' % dir
818 1: '#(set-octavation 0)'}.get (self.span_direction, '')
820 class TrillSpanEvent (SpanEvent):
821 def ly_expression (self):
822 return {-1: '\\startTrillSpan',
823 0: '', # no need to write out anything for type='continue'
824 1:'\\stopTrillSpan'}.get (self.span_direction, '')
826 class GlissandoEvent (SpanEvent):
827 def ly_expression (self):
829 # TODO: wavy-line glissandos don't work, becasue the style has to be
830 # set before the note, at the \glissando it's already too late!
831 #if self.line_type == 'wavy':
832 #style = "\once\override Glissando #'style = #'zigzag"
833 # In lilypond, glissando is NOT a spanner, unlike MusicXML.
834 return {-1: style + '\\glissando',
835 1:''}.get (self.span_direction, '')
837 class ArpeggioEvent(Event):
839 Event.__init__ (self)
841 def wait_for_note (self):
843 def ly_expression (self):
844 # TODO: Use self.direction for up/down arpeggios
845 return ('\\arpeggio')
848 class TieEvent(Event):
849 def ly_expression (self):
853 class HairpinEvent (SpanEvent):
854 def set_span_type (self, type):
855 self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
856 def hairpin_to_ly (self):
857 if self.span_direction == 1:
860 return {1: '\<', -1: '\>'}.get (self.span_type, '')
862 def ly_expression (self):
863 return self.hairpin_to_ly ()
865 def print_ly (self, printer):
866 val = self.hairpin_to_ly ()
872 class DynamicsEvent (Event):
875 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
877 "f", "ff", "fff", "ffff",
878 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
879 def wait_for_note (self):
881 def ly_expression (self):
882 if self.type == None:
884 elif self.type in self.available_commands:
885 return '\%s' % self.type
887 return '-\markup{ \dynamic %s }' % self.type
889 def print_ly (self, printer):
890 if self.type == None:
892 elif self.type in self.available_commands:
893 printer.dump ("\\%s" % self.type)
895 printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
898 class TextEvent (Event):
901 self.force_direction = None
903 def wait_for_note (self):
906 def direction_mod (self):
907 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
909 def ly_expression (self):
910 base_string = '%s\"%s\"'
912 base_string = '%s\markup{ ' + self.markup + ' {%s} }'
913 return base_string % (self.direction_mod (), self.text)
915 class ArticulationEvent (Event):
918 self.force_direction = None
919 def wait_for_note (self):
922 def direction_mod (self):
923 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
925 def ly_expression (self):
926 return '%s\\%s' % (self.direction_mod (), self.type)
928 class ShortArticulationEvent (ArticulationEvent):
929 def direction_mod (self):
931 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
932 def ly_expression (self):
934 return '%s%s' % (self.direction_mod (), self.type)
938 class NoDirectionArticulationEvent (ArticulationEvent):
939 def ly_expression (self):
941 return '\\%s' % self.type
945 class MarkupEvent (ShortArticulationEvent):
947 ArticulationEvent.__init__ (self)
949 def ly_expression (self):
951 return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
955 class FretEvent (MarkupEvent):
957 MarkupEvent.__init__ (self)
958 self.force_direction = 1
963 def ly_expression (self):
965 if self.strings <> 6:
966 val += "w:%s;" % self.strings
968 val += "h:%s;" % self.frets
969 if self.barre and len (self.barre) >= 3:
970 val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
971 have_fingering = False
972 for i in self.elements:
974 val += "%s-%s" % (i[0], i[1])
976 have_fingering = True
982 return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
986 class TremoloEvent (ArticulationEvent):
988 Event.__init__ (self)
991 def ly_expression (self):
993 if self.bars and self.bars > 0:
994 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
997 class BendEvent (ArticulationEvent):
999 Event.__init__ (self)
1001 def ly_expression (self):
1003 return "-\\bendAfter #%s" % self.alter
1007 class RhythmicEvent(Event):
1008 def __init__ (self):
1009 Event.__init__ (self)
1010 self.duration = Duration()
1012 def get_length (self):
1013 return self.duration.get_length()
1015 def get_properties (self):
1016 return ("'duration %s"
1017 % self.duration.lisp_expression ())
1019 class RestEvent (RhythmicEvent):
1020 def __init__ (self):
1021 RhythmicEvent.__init__ (self)
1023 def ly_expression (self):
1025 return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1027 return 'r%s' % self.duration.ly_expression ()
1029 def print_ly (self, printer):
1031 self.pitch.print_ly (printer)
1032 self.duration.print_ly (printer)
1036 self.duration.print_ly (printer)
1038 class SkipEvent (RhythmicEvent):
1039 def ly_expression (self):
1040 return 's%s' % self.duration.ly_expression ()
1042 class NoteEvent(RhythmicEvent):
1043 def __init__ (self):
1044 RhythmicEvent.__init__ (self)
1046 self.drum_type = None
1047 self.cautionary = False
1048 self.forced_accidental = False
1050 def get_properties (self):
1051 str = RhythmicEvent.get_properties (self)
1054 str += self.pitch.lisp_expression ()
1055 elif self.drum_type:
1056 str += "'drum-type '%s" % self.drum_type
1060 def pitch_mods (self):
1063 excl_question += '?'
1064 if self.forced_accidental:
1065 excl_question += '!'
1067 return excl_question
1069 def ly_expression (self):
1071 return '%s%s%s' % (self.pitch.ly_expression (),
1073 self.duration.ly_expression ())
1074 elif self.drum_type:
1075 return '%s%s' (self.drum_type,
1076 self.duration.ly_expression ())
1078 def print_ly (self, printer):
1080 self.pitch.print_ly (printer)
1081 printer (self.pitch_mods ())
1083 printer (self.drum_type)
1085 self.duration.print_ly (printer)
1087 class KeySignatureChange (Music):
1088 def __init__ (self):
1089 Music.__init__ (self)
1091 self.tonic = Pitch()
1094 def ly_expression (self):
1095 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1098 def lisp_expression (self):
1099 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
1100 scale_str = ("'(%s)" % string.join (pairs))
1102 return """ (make-music 'KeyChangeEvent
1103 'pitch-alist %s) """ % scale_str
1105 class TimeSignatureChange (Music):
1106 def __init__ (self):
1107 Music.__init__ (self)
1108 self.fraction = (4,4)
1109 def ly_expression (self):
1110 return '\\time %d/%d ' % self.fraction
1112 class ClefChange (Music):
1113 def __init__ (self):
1114 Music.__init__ (self)
1119 def octave_modifier (self):
1120 return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1121 def clef_name (self):
1122 return {('G', 2): "treble",
1124 ('C', 1): "soprano",
1125 ('C', 2): "mezzosoprano",
1128 ('C', 5): "baritone",
1129 ('F', 3): "varbaritone",
1131 ('F', 5): "subbass",
1132 ("percussion", 2): "percussion",
1133 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1134 def ly_expression (self):
1135 return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1138 "G": ("clefs.G", -2, -6),
1139 "C": ("clefs.C", 0, 0),
1140 "F": ("clefs.F", 2, 6),
1143 def lisp_expression (self):
1145 (glyph, pos, c0) = self.clef_dict[self.type]
1149 (make-music 'SequentialMusic
1152 (make-property-set 'clefGlyph "%s") 'Staff)
1154 (make-property-set 'clefPosition %d) 'Staff)
1156 (make-property-set 'middleCPosition %d) 'Staff)))
1157 """ % (glyph, pos, c0)
1161 class StaffChange (Music):
1162 def __init__ (self, staff):
1163 Music.__init__ (self)
1165 def ly_expression (self):
1167 return "\\change Staff=\"%s\"" % self.staff
1172 class MultiMeasureRest(Music):
1174 def lisp_expression (self):
1177 'MultiMeasureRestMusicGroup
1179 (list (make-music (quote BarCheck))
1184 'MultiMeasureRestEvent
1187 (make-music (quote BarCheck))))
1188 """ % self.duration.lisp_expression ()
1190 def ly_expression (self):
1191 return 'R%s' % self.duration.ly_expression ()
1195 def __init__ (self, command = "StaffGroup"):
1196 self.stafftype = command
1198 self.instrument_name = None
1199 self.short_instrument_name = None
1203 self.is_group = True
1204 # part_information is a list with entries of the form
1205 # [staffid, voicelist]
1206 # where voicelist is a list with entries of the form
1207 # [voiceid1, [lyricsid11, lyricsid12,...] ]
1208 self.part_information = None
1210 def append_staff (self, staff):
1211 self.children.append (staff)
1213 def set_part_information (self, part_name, staves_info):
1214 if part_name == self.id:
1215 self.part_information = staves_info
1217 for c in self.children:
1218 c.set_part_information (part_name, staves_info)
1220 def print_ly_contents (self, printer):
1221 for c in self.children:
1223 c.print_ly (printer)
1224 def print_ly_overrides (self, printer):
1226 needs_with |= self.spanbar == "no"
1227 needs_with |= self.instrument_name != None
1228 needs_with |= self.short_instrument_name != None
1229 needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1231 printer.dump ("\\with {")
1232 if self.instrument_name or self.short_instrument_name:
1233 printer.dump ("\\consists \"Instrument_name_engraver\"")
1234 if self.spanbar == "no":
1235 printer.dump ("\\override SpanBar #'transparent = ##t")
1236 brack = {"brace": "SystemStartBrace",
1238 "line": "SystemStartSquare"}.get (self.symbol, None)
1240 printer.dump ("systemStartDelimiter = #'%s" % brack)
1243 def print_ly (self, printer):
1245 printer.dump ("\\new %s" % self.stafftype)
1246 self.print_ly_overrides (printer)
1249 if self.stafftype and self.instrument_name:
1250 printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype,
1251 escape_instrument_string (self.instrument_name)))
1253 if self.stafftype and self.short_instrument_name:
1254 printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1255 escape_instrument_string (self.short_instrument_name)))
1257 self.print_ly_contents (printer)
1263 class Staff (StaffGroup):
1264 def __init__ (self, command = "Staff"):
1265 StaffGroup.__init__ (self, command)
1266 self.is_group = False
1268 self.voice_command = "Voice"
1269 self.substafftype = None
1271 def print_ly_overrides (self, printer):
1274 def print_ly_contents (self, printer):
1275 if not self.id or not self.part_information:
1277 sub_staff_type = self.substafftype
1278 if not sub_staff_type:
1279 sub_staff_type = self.stafftype
1281 for [staff_id, voices] in self.part_information:
1283 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1285 printer ('\\context %s << ' % sub_staff_type)
1288 nr_voices = len (voices)
1289 for [v, lyrics] in voices:
1291 voice_count_text = ''
1293 voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1294 3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1295 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1299 printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1303 def print_ly (self, printer):
1304 if self.part_information and len (self.part_information) > 1:
1305 self.stafftype = "PianoStaff"
1306 self.substafftype = "Staff"
1307 StaffGroup.print_ly (self, printer)
1309 class TabStaff (Staff):
1310 def __init__ (self, command = "TabStaff"):
1311 Staff.__init__ (self, command)
1312 self.string_tunings = []
1313 self.tablature_format = None
1314 self.voice_command = "TabVoice"
1315 def print_ly_overrides (self, printer):
1316 if self.string_tunings or self.tablature_format:
1317 printer.dump ("\\with {")
1318 if self.string_tunings:
1319 printer.dump ("stringTunings = #'(")
1320 for i in self.string_tunings:
1321 printer.dump ("%s" % i.semitones ())
1323 if self.tablature_format:
1324 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1328 class DrumStaff (Staff):
1329 def __init__ (self, command = "DrumStaff"):
1330 Staff.__init__ (self, command)
1331 self.drum_style_table = None
1332 self.voice_command = "DrumVoice"
1333 def print_ly_overrides (self, printer):
1334 if self.drum_style_table:
1335 printer.dump ("\with {")
1336 printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1339 class RhythmicStaff (Staff):
1340 def __init__ (self, command = "RhythmicStaff"):
1341 Staff.__init__ (self, command)
1346 bflat.alteration = -1
1356 print bflat.semitones()
1357 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
1358 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1360 print bflat.semitones(), 'down'
1361 print bflat.transposed (down)
1362 print bflat.transposed (down).transposed (down)
1363 print bflat.transposed (down).transposed (down).transposed (down)
1367 def test_printer ():
1375 m = SequentialMusic()
1376 m.append (make_note ())
1377 m.append (make_note ())
1378 m.append (make_note ())
1381 t = TimeScaledMusic ()
1387 m = SequentialMusic ()
1388 m.append (make_tup ())
1389 m.append (make_tup ())
1390 m.append (make_tup ())
1392 printer = Output_printer()
1393 m.print_ly (printer)
1397 m = SequentialMusic()
1401 n.duration.duration_log = l
1403 evc.insert_around (None, n, 0)
1404 m.insert_around (None, evc, 0)
1408 n.duration.duration_log = l
1410 evc.insert_around (None, n, 0)
1411 m.insert_around (None, evc, 0)
1415 n.duration.duration_log = l
1417 evc.insert_around (None, n, 0)
1418 m.insert_around (None, evc, 0)
1422 m.insert_around (None, evc, 0)
1427 tonic.alteration = -2
1428 n = KeySignatureChange()
1429 n.tonic=tonic.copy()
1430 n.scale = [0, 0, -2, 0, 0,-2,-2]
1432 evc.insert_around (None, n, 0)
1433 m.insert_around (None, evc, 0)
1438 if __name__ == '__main__':
1444 expr.set_start (Rational (0))
1445 print expr.ly_expression()
1446 start = Rational (0,4)
1447 stop = Rational (4,2)
1448 def sub(x, start=start, stop=stop):
1449 ok = x.start >= start and x.start +x.get_length() <= stop
1452 print expr.lisp_sub_expression(sub)