6 from rational import Rational
9 def escape_instrument_string (input_string):
10 retstring = string.replace (input_string, "\"", "\\\"")
11 if re.match ('.*\n.*', retstring):
12 strings = retstring.split ('\r\n')
13 retstring = "\\markup { \\column { "
15 retstring += "\\line {\"" + s + "\"} "
18 retstring = "\"" + retstring + "\""
21 class Output_stack_element:
23 self.factor = Rational (1)
25 o = Output_stack_element()
26 o.factor = self.factor
31 """A class that takes care of formatting (eg.: indenting) a
32 Music expression as a .ly file.
35 ## TODO: support for \relative.
41 self._file = sys.stdout
43 self._output_state_stack = [Output_stack_element()]
44 self._skipspace = False
45 self._last_duration = None
47 def set_file (self, file):
50 def dump_version (self):
52 self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
55 def get_indent (self):
56 return self._nesting * self._indent
59 last = self._output_state_stack[-1]
60 self._output_state_stack.append (last.copy())
62 def add_factor (self, factor):
64 self._output_state_stack[-1].factor *= factor
67 del self._output_state_stack[-1]
68 if not self._output_state_stack:
71 def duration_factor (self):
72 return self._output_state_stack[-1].factor
74 def print_verbatim (self, str):
77 def unformatted_output (self, str):
78 # don't indent on \< and indent only once on <<
79 self._nesting += ( str.count ('<')
80 - str.count ('\<') - str.count ('<<')
82 self._nesting -= ( str.count ('>') - str.count ('\>') - str.count ('>>')
83 - str.count ('->') - str.count ('_>')
86 self.print_verbatim (str)
88 def print_duration_string (self, str):
89 if self._last_duration == str:
92 self.unformatted_output (str)
94 def add_word (self, str):
95 if (len (str) + 1 + len (self._line) > self._line_len):
97 self._skipspace = True
99 if not self._skipspace:
101 self.unformatted_output (str)
102 self._skipspace = False
105 self._file.write (self._line + '\n')
106 self._line = ' ' * self._indent * self._nesting
107 self._skipspace = True
109 def skipspace (self):
110 self._skipspace = True
112 def __call__(self, arg):
115 def dump (self, str):
117 self._skipspace = False
118 self.unformatted_output (str)
120 words = string.split (str)
133 self.duration_log = 0
135 self.factor = Rational (1)
137 def lisp_expression (self):
138 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
140 self.factor.numerator (),
141 self.factor.denominator ())
144 def ly_expression (self, factor = None):
148 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
150 if factor <> Rational (1,1):
151 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
155 def print_ly (self, outputter):
156 str = self.ly_expression (self.factor / outputter.duration_factor ())
157 outputter.print_duration_string (str)
160 return self.ly_expression()
165 d.duration_log = self.duration_log
166 d.factor = self.factor
169 def get_length (self):
170 dot_fact = Rational( (1 << (1 + self.dots))-1,
173 log = abs (self.duration_log)
175 if self.duration_log < 0:
176 base = Rational (dur)
178 base = Rational (1, dur)
180 return base * dot_fact * self.factor
190 return self.ly_expression()
192 def transposed (self, interval):
194 c.alteration += interval.alteration
195 c.step += interval.step
196 c.octave += interval.octave
199 target_st = self.semitones() + interval.semitones()
200 c.alteration += target_st - c.semitones()
207 c.octave += c.step / 7
210 def lisp_expression (self):
211 return '(ly:make-pitch %d %d %d)' % (self.octave,
217 p.alteration = self.alteration
219 p.octave = self.octave
223 return self.step + self.octave *7
225 def semitones (self):
226 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
228 def ly_step_expression (self):
229 str = 'cdefgab'[self.step]
230 if self.alteration > 0:
231 str += 'is'* (self.alteration)
232 elif self.alteration < 0:
233 str += 'es'* (-self.alteration)
235 return str.replace ('aes', 'as').replace ('ees', 'es')
237 def ly_expression (self):
238 str = self.ly_step_expression ()
240 str += "'" * (self.octave + 1)
241 elif self.octave < -1:
242 str += "," * (-self.octave - 1)
246 def print_ly (self, outputter):
247 outputter (self.ly_expression())
252 self.start = Rational (0)
254 self.identifier = None
256 def get_length(self):
259 def get_properties (self):
262 def has_children (self):
265 def get_index (self):
267 return self.parent.elements.index (self)
271 return self.__class__.__name__
273 def lisp_expression (self):
276 props = self.get_properties ()
278 return "(make-music '%s %s)" % (name, props)
280 def set_start (self, start):
283 def find_first (self, predicate):
288 def print_comment (self, printer, text = None):
299 lines = string.split (text, '\n')
302 printer.unformatted_output ('% ' + l)
306 def print_with_identifier (self, printer):
308 printer ("\\%s" % self.identifier)
310 self.print_ly (printer)
312 def print_ly (self, printer):
313 printer (self.ly_expression ())
315 class MusicWrapper (Music):
319 def print_ly (self, func):
320 self.element.print_ly (func)
322 class ModeChangingMusicWrapper (MusicWrapper):
324 MusicWrapper.__init__ (self)
325 self.mode = 'notemode'
327 def print_ly (self, func):
328 func ('\\%s' % self.mode)
329 MusicWrapper.print_ly (self, func)
331 class TimeScaledMusic (MusicWrapper):
332 def print_ly (self, func):
333 func ('\\times %d/%d ' %
334 (self.numerator, self.denominator))
335 func.add_factor (Rational (self.numerator, self.denominator))
336 MusicWrapper.print_ly (self, func)
339 class NestedMusic(Music):
341 Music.__init__ (self)
344 def append (self, what):
346 self.elements.append (what)
348 def has_children (self):
351 def insert_around (self, succ, elt, dir):
352 assert elt.parent == None
353 assert succ == None or succ in self.elements
358 idx = self.elements.index (succ)
365 idx = len (self.elements)
367 self.elements.insert (idx, elt)
370 def get_properties (self):
371 return ("'elements (list %s)"
372 % string.join (map (lambda x: x.lisp_expression(),
375 def get_subset_properties (self, predicate):
376 return ("'elements (list %s)"
377 % string.join (map (lambda x: x.lisp_expression(),
378 filter ( predicate, self.elements))))
379 def get_neighbor (self, music, dir):
380 assert music.parent == self
381 idx = self.elements.index (music)
383 idx = min (idx, len (self.elements) -1)
386 return self.elements[idx]
388 def delete_element (self, element):
389 assert element in self.elements
391 self.elements.remove (element)
392 element.parent = None
394 def set_start (self, start):
396 for e in self.elements:
399 def find_first (self, predicate):
400 r = Music.find_first (self, predicate)
404 for e in self.elements:
405 r = e.find_first (predicate)
410 class SequentialMusic (NestedMusic):
411 def get_last_event_chord (self):
413 at = len( self.elements ) - 1
415 not isinstance (self.elements[at], EventChord) and
416 not isinstance (self.elements[at], BarCheck)):
419 if (at >= 0 and isinstance (self.elements[at], EventChord)):
420 value = self.elements[at]
423 def print_ly (self, printer, newline = True):
426 self.print_comment (printer)
430 for e in self.elements:
437 def lisp_sub_expression (self, pred):
441 props = self.get_subset_properties (pred)
443 return "(make-music '%s %s)" % (name, props)
445 def set_start (self, start):
446 for e in self.elements:
448 start += e.get_length()
452 self.lyrics_syllables = []
454 def print_ly (self, printer):
455 printer.dump ("\lyricmode {")
456 for l in self.lyrics_syllables:
457 printer.dump ( "%s " % l )
460 def ly_expression (self):
461 lstr = "\lyricmode {\n "
462 for l in self.lyrics_syllables:
470 self.header_fields = {}
471 def set_field (self, field, value):
472 self.header_fields[field] = value
474 def print_ly (self, printer):
475 printer.dump ("\header {")
477 for (k,v) in self.header_fields.items ():
479 printer.dump ('%s = %s' % (k,v))
488 class EventChord (NestedMusic):
490 NestedMusic.__init__ (self)
491 self.grace_elements = None
492 self.grace_type = None
493 def append_grace (self, element):
495 if not self.grace_elements:
496 self.grace_elements = SequentialMusic ()
497 self.grace_elements.append (element)
499 def get_length (self):
501 for e in self.elements:
502 l = max(l, e.get_length())
505 def print_ly (self, printer):
506 note_events = [e for e in self.elements if
507 isinstance (e, NoteEvent)]
509 rest_events = [e for e in self.elements if
510 isinstance (e, RhythmicEvent)
511 and not isinstance (e, NoteEvent)]
513 other_events = [e for e in self.elements if
514 not isinstance (e, RhythmicEvent)]
516 if self.grace_elements and self.elements:
518 printer ('\\%s' % self.grace_type)
521 # don't print newlines after the { and } braces
522 self.grace_elements.print_ly (printer, False)
525 rest_events[0].print_ly (printer)
526 elif len (note_events) == 1:
527 note_events[0].print_ly (printer)
529 pitches = [x.pitch.ly_expression () for x in note_events]
530 printer ('<%s>' % string.join (pitches))
531 note_events[0].duration.print_ly (printer)
535 for e in other_events:
538 self.print_comment (printer)
541 class BarCheck (Music):
543 Music.__init__ (self)
546 def print_ly (self, printer):
547 if self.bar_number > 0 and (self.bar_number % 10) == 0:
548 printer.dump ("| \\barNumberCheck #%d " % self.bar_number)
552 printer.print_verbatim (' %% %d' % self.bar_number)
556 def ly_expression (self):
562 class SpanEvent (Event):
564 Event.__init__ (self)
565 self.span_direction = 0 # start/stop
566 self.line_type = 'solid'
567 self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
568 self.size = 0 # size of e.g. ocrave shift
569 def wait_for_note (self):
571 def get_properties(self):
572 return "'span-direction %d" % self.span_direction
573 def set_span_type (self, type):
574 self.span_type = type
576 class SlurEvent (SpanEvent):
577 def ly_expression (self):
580 # TODO: setting dashed/dotted line style does not work, because that
581 # command needs to be written before the note, not when the
582 # event is observed after the note!
583 #before = {'dotted': '\\slurDotted',
584 # 'dashed' : '\\slurDashed'}.get (self.line_type, '')
586 #after = '\\slurSolid'
588 return {-1: before + '(' + after,
589 1:')'}.get (self.span_direction, '')
591 class BeamEvent (SpanEvent):
592 def ly_expression (self):
594 1:']'}.get (self.span_direction, '')
596 class PedalEvent (SpanEvent):
597 def ly_expression (self):
598 return {-1: '\\sustainDown',
599 1:'\\sustainUp'}.get (self.span_direction, '')
601 # type==-1 means octave up, type==-2 means octave down
602 class OctaveShiftEvent (SpanEvent):
603 def wait_for_note (self):
605 def set_span_type (self, type):
606 self.span_type = {'up': 1, 'down': -1}.get (type, 0)
607 def ly_octave_shift_indicator (self):
608 # convert 8/15 to lilypond indicators (+-1/+-2)
609 value = {8: 1, 15: 2}.get (self.size, 0)
610 # negative values go up!
611 value *= -1*self.span_type
613 def ly_expression (self):
614 dir = self.ly_octave_shift_indicator ()
617 value = '#(set-octavation %s)' % dir
620 1: '#(set-octavation 0)'}.get (self.span_direction, '')
622 class TrillSpanEvent (SpanEvent):
623 def ly_expression (self):
624 return {-1: '\\startTrillSpan',
625 0: '', # no need to write out anything for type='continue'
626 1:'\\stopTrillSpan'}.get (self.span_direction, '')
628 class GlissandoEvent (SpanEvent):
629 def ly_expression (self):
631 # TODO: wavy-line glissandos don't work, becasue the style has to be
632 # set before the note, at the \glissando it's already too late!
633 #if self.line_type == 'wavy':
634 #style = "\once\override Glissando #'style = #'zigzag"
635 # In lilypond, glissando is NOT a spanner, unlike MusicXML.
636 return {-1: style + '\\glissando',
637 1:''}.get (self.span_direction, '')
639 class ArpeggioEvent(Event):
640 def wait_for_note (self):
642 def ly_expression (self):
643 return ('\\arpeggio')
646 class TieEvent(Event):
647 def ly_expression (self):
651 class HairpinEvent (SpanEvent):
652 def set_span_type (self, type):
653 self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
654 def hairpin_to_ly (self):
655 if self.span_direction == 1:
658 return {1: '\<', -1: '\>'}.get (self.span_type, '')
660 def ly_expression (self):
661 return self.hairpin_to_ly ()
663 def print_ly (self, printer):
664 val = self.hairpin_to_ly ()
670 class DynamicsEvent (Event):
673 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
675 "f", "ff", "fff", "ffff",
676 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
677 def wait_for_note (self):
679 def ly_expression (self):
680 if self.type == None:
682 elif self.type in self.available_commands:
683 return '\%s' % self.type
685 return '-\markup{ \dynamic %s }' % self.type
687 def print_ly (self, printer):
688 if self.type == None:
690 elif self.type in self.available_commands:
691 printer.dump ("\\%s" % self.type)
693 printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
696 class ArticulationEvent (Event):
699 self.force_direction = None
701 def direction_mod (self):
702 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
704 def ly_expression (self):
705 return '%s\\%s' % (self.direction_mod (), self.type)
707 class ShortArticulationEvent (ArticulationEvent):
708 def direction_mod (self):
710 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
711 def ly_expression (self):
712 return '%s%s' % (self.direction_mod (), self.type)
714 class TremoloEvent (Event):
716 Event.__init__ (self)
719 def ly_expression (self):
722 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
725 class BendEvent (Event):
727 Event.__init__ (self)
729 def ly_expression (self):
731 return "-\\bendAfter #%s" % self.alter
735 class RhythmicEvent(Event):
737 Event.__init__ (self)
738 self.duration = Duration()
740 def get_length (self):
741 return self.duration.get_length()
743 def get_properties (self):
744 return ("'duration %s"
745 % self.duration.lisp_expression ())
747 class RestEvent (RhythmicEvent):
749 RhythmicEvent.__init__ (self)
751 def ly_expression (self):
753 return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
755 return 'r%s' % self.duration.ly_expression ()
757 def print_ly (self, printer):
759 self.pitch.print_ly (printer)
760 self.duration.print_ly (printer)
764 self.duration.print_ly (printer)
766 class SkipEvent (RhythmicEvent):
767 def ly_expression (self):
768 return 's%s' % self.duration.ly_expression ()
770 class NoteEvent(RhythmicEvent):
772 RhythmicEvent.__init__ (self)
774 self.drum_type = None
775 self.cautionary = False
776 self.forced_accidental = False
778 def get_properties (self):
779 str = RhythmicEvent.get_properties (self)
782 str += self.pitch.lisp_expression ()
784 str += "'drum-type '%s" % self.drum_type
788 def pitch_mods (self):
792 if self.forced_accidental:
797 def ly_expression (self):
799 return '%s%s%s' % (self.pitch.ly_expression (),
801 self.duration.ly_expression ())
803 return '%s%s' (self.drum_type,
804 self.duration.ly_expression ())
806 def print_ly (self, printer):
808 self.pitch.print_ly (printer)
809 printer (self.pitch_mods ())
811 printer (self.drum_type)
813 self.duration.print_ly (printer)
815 class KeySignatureChange (Music):
817 Music.__init__ (self)
822 def ly_expression (self):
823 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
826 def lisp_expression (self):
827 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
828 scale_str = ("'(%s)" % string.join (pairs))
830 return """ (make-music 'KeyChangeEvent
831 'pitch-alist %s) """ % scale_str
833 class TimeSignatureChange (Music):
835 Music.__init__ (self)
836 self.fraction = (4,4)
837 def ly_expression (self):
838 return '\\time %d/%d ' % self.fraction
840 class ClefChange (Music):
842 Music.__init__ (self)
847 def octave_modifier (self):
848 return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
849 def clef_name (self):
850 return {('G', 2): "treble",
853 ('C', 2): "mezzosoprano",
856 ('C', 5): "baritone",
857 ('F', 3): "varbaritone",
860 ("percussion", 2): "percussion",
861 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
862 def ly_expression (self):
863 return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
866 "G": ("clefs.G", -2, -6),
867 "C": ("clefs.C", 0, 0),
868 "F": ("clefs.F", 2, 6),
871 def lisp_expression (self):
873 (glyph, pos, c0) = self.clef_dict[self.type]
877 (make-music 'SequentialMusic
880 (make-property-set 'clefGlyph "%s") 'Staff)
882 (make-property-set 'clefPosition %d) 'Staff)
884 (make-property-set 'middleCPosition %d) 'Staff)))
885 """ % (glyph, pos, c0)
889 class MultiMeasureRest(Music):
891 def lisp_expression (self):
894 'MultiMeasureRestMusicGroup
896 (list (make-music (quote BarCheck))
901 'MultiMeasureRestEvent
904 (make-music (quote BarCheck))))
905 """ % self.duration.lisp_expression ()
907 def ly_expression (self):
908 return 'R%s' % self.duration.ly_expression ()
912 def __init__ (self, command = "StaffGroup"):
913 self.stafftype = command
915 self.instrument_name = None
916 self.short_instrument_name = None
920 # part_information is a list with entries of the form
921 # [staffid, voicelist]
922 # where voicelist is a list with entries of the form
923 # [voiceid1, [lyricsid11, lyricsid12,...] ]
924 self.part_information = None
926 def appendStaff (self, staff):
927 self.children.append (staff)
929 def setPartInformation (self, part_name, staves_info):
930 if part_name == self.id:
931 self.part_information = staves_info
933 for c in self.children:
934 c.setPartInformation (part_name, staves_info)
936 def print_ly_contents (self, printer):
937 for c in self.children:
940 def print_ly_overrides (self, printer):
942 needs_with |= self.spanbar == "no"
943 needs_with |= self.instrument_name != None
944 needs_with |= self.short_instrument_name != None
945 needs_with |= (self.symbol != None) and (self.symbol != "bracket")
947 printer.dump ("\\with {")
948 if self.instrument_name or self.short_instrument_name:
949 printer.dump ("\\consists \"Instrument_name_engraver\"")
950 if self.spanbar == "no":
951 printer.dump ("\\override SpanBar #'transparent = ##t")
952 brack = {"brace": "SystemStartBrace",
954 "line": "SystemStartBar"}.get (self.symbol, None)
956 printer.dump ("systemStartDelimiter = #'%s" % brack)
959 def print_ly (self, printer):
961 printer.dump ("\\new %s" % self.stafftype)
962 self.print_ly_overrides (printer)
965 if self.stafftype and self.instrument_name:
966 printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype,
967 escape_instrument_string (self.instrument_name)))
969 if self.stafftype and self.short_instrument_name:
970 printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype,
971 escape_instrument_string (self.short_instrument_name)))
973 self.print_ly_contents (printer)
979 class Staff (StaffGroup):
981 StaffGroup.__init__ (self, "Staff")
984 def print_ly_overrides (self, printer):
987 def print_ly_contents (self, printer):
988 if not self.id or not self.part_information:
991 for [staff_id, voices] in self.part_information:
993 printer ('\\context Staff = "%s" << ' % staff_id)
995 printer ('\\context Staff << ')
998 nr_voices = len (voices)
999 for [v, lyrics] in voices:
1001 voice_count_text = ''
1003 voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1004 3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1005 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1009 printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1013 def print_ly (self, printer):
1014 if self.part_information and len (self.part_information) > 1:
1015 self.stafftype = "PianoStaff"
1016 StaffGroup.print_ly (self, printer)
1021 bflat.alteration = -1
1031 print bflat.semitones()
1032 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
1033 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1035 print bflat.semitones(), 'down'
1036 print bflat.transposed (down)
1037 print bflat.transposed (down).transposed (down)
1038 print bflat.transposed (down).transposed (down).transposed (down)
1042 def test_printer ():
1050 m = SequentialMusic()
1051 m.append (make_note ())
1052 m.append (make_note ())
1053 m.append (make_note ())
1056 t = TimeScaledMusic ()
1062 m = SequentialMusic ()
1063 m.append (make_tup ())
1064 m.append (make_tup ())
1065 m.append (make_tup ())
1067 printer = Output_printer()
1068 m.print_ly (printer)
1072 m = SequentialMusic()
1076 n.duration.duration_log = l
1078 evc.insert_around (None, n, 0)
1079 m.insert_around (None, evc, 0)
1083 n.duration.duration_log = l
1085 evc.insert_around (None, n, 0)
1086 m.insert_around (None, evc, 0)
1090 n.duration.duration_log = l
1092 evc.insert_around (None, n, 0)
1093 m.insert_around (None, evc, 0)
1097 m.insert_around (None, evc, 0)
1102 tonic.alteration = -2
1103 n = KeySignatureChange()
1104 n.tonic=tonic.copy()
1105 n.scale = [0, 0, -2, 0, 0,-2,-2]
1107 evc.insert_around (None, n, 0)
1108 m.insert_around (None, evc, 0)
1113 if __name__ == '__main__':
1119 expr.set_start (Rational (0))
1120 print expr.ly_expression()
1121 start = Rational (0,4)
1122 stop = Rational (4,2)
1123 def sub(x, start=start, stop=stop):
1124 ok = x.start >= start and x.start +x.get_length() <= stop
1127 print expr.lisp_sub_expression(sub)