6 from rational import Rational
9 def escape_output_string (input_string):
10 return "\"" + string.replace (input_string, "\"", "\\\"") + "\""
12 class Output_stack_element:
14 self.factor = Rational (1)
16 o = Output_stack_element()
17 o.factor = self.factor
22 """A class that takes care of formatting (eg.: indenting) a
23 Music expression as a .ly file.
26 ## TODO: support for \relative.
32 self._file = sys.stdout
34 self._output_state_stack = [Output_stack_element()]
35 self._skipspace = False
36 self._last_duration = None
38 def set_file (self, file):
41 def dump_version (self):
43 self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
46 def get_indent (self):
47 return self._nesting * self._indent
50 last = self._output_state_stack[-1]
51 self._output_state_stack.append (last.copy())
53 def add_factor (self, factor):
55 self._output_state_stack[-1].factor *= factor
58 del self._output_state_stack[-1]
59 if not self._output_state_stack:
62 def duration_factor (self):
63 return self._output_state_stack[-1].factor
65 def print_verbatim (self, str):
68 def unformatted_output (self, str):
69 self._nesting += str.count ('<') + str.count ('{')
70 self._nesting -= str.count ('>') + str.count ('}')
71 self.print_verbatim (str)
73 def print_duration_string (self, str):
74 if self._last_duration == str:
77 self.unformatted_output (str)
79 def add_word (self, str):
80 if (len (str) + 1 + len (self._line) > self._line_len):
82 self._skipspace = True
84 if not self._skipspace:
86 self.unformatted_output (str)
87 self._skipspace = False
90 self._file.write (self._line + '\n')
91 self._line = ' ' * self._indent * self._nesting
92 self._skipspace = True
95 self._skipspace = True
97 def __call__(self, arg):
100 def dump (self, str):
102 self._skipspace = False
103 self.unformatted_output (str)
105 words = string.split (str)
118 self.duration_log = 0
120 self.factor = Rational (1)
122 def lisp_expression (self):
123 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
125 self.factor.numerator (),
126 self.factor.denominator ())
129 def ly_expression (self, factor = None):
133 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
135 if factor <> Rational (1,1):
136 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
140 def print_ly (self, outputter):
141 str = self.ly_expression (self.factor / outputter.duration_factor ())
142 outputter.print_duration_string (str)
145 return self.ly_expression()
150 d.duration_log = self.duration_log
151 d.factor = self.factor
154 def get_length (self):
155 dot_fact = Rational( (1 << (1 + self.dots))-1,
158 log = abs (self.duration_log)
160 if self.duration_log < 0:
161 base = Rational (dur)
163 base = Rational (1, dur)
165 return base * dot_fact * self.factor
175 return self.ly_expression()
177 def transposed (self, interval):
179 c.alteration += interval.alteration
180 c.step += interval.step
181 c.octave += interval.octave
184 target_st = self.semitones() + interval.semitones()
185 c.alteration += target_st - c.semitones()
192 c.octave += c.step / 7
195 def lisp_expression (self):
196 return '(ly:make-pitch %d %d %d)' % (self.octave,
202 p.alteration = self.alteration
204 p.octave = self.octave
208 return self.step + self.octave *7
210 def semitones (self):
211 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
213 def ly_step_expression (self):
214 str = 'cdefgab'[self.step]
215 if self.alteration > 0:
216 str += 'is'* (self.alteration)
217 elif self.alteration < 0:
218 str += 'es'* (-self.alteration)
220 return str.replace ('aes', 'as').replace ('ees', 'es')
222 def ly_expression (self):
223 str = self.ly_step_expression ()
225 str += "'" * (self.octave + 1)
226 elif self.octave < -1:
227 str += "," * (-self.octave - 1)
231 def print_ly (self, outputter):
232 outputter (self.ly_expression())
237 self.start = Rational (0)
239 self.identifier = None
241 def get_length(self):
244 def get_properties (self):
247 def has_children (self):
250 def get_index (self):
252 return self.parent.elements.index (self)
256 return self.__class__.__name__
258 def lisp_expression (self):
261 props = self.get_properties ()
263 return "(make-music '%s %s)" % (name, props)
265 def set_start (self, start):
268 def find_first (self, predicate):
273 def print_comment (self, printer, text = None):
284 lines = string.split (text, '\n')
287 printer.unformatted_output ('% ' + l)
291 def print_with_identifier (self, printer):
293 printer ("\\%s" % self.identifier)
295 self.print_ly (printer)
297 def print_ly (self, printer):
298 printer (self.ly_expression ())
300 class MusicWrapper (Music):
304 def print_ly (self, func):
305 self.element.print_ly (func)
307 class ModeChangingMusicWrapper (MusicWrapper):
309 MusicWrapper.__init__ (self)
310 self.mode = 'notemode'
312 def print_ly (self, func):
313 func ('\\%s' % self.mode)
314 MusicWrapper.print_ly (self, func)
316 class TimeScaledMusic (MusicWrapper):
317 def print_ly (self, func):
318 func ('\\times %d/%d ' %
319 (self.numerator, self.denominator))
320 func.add_factor (Rational (self.numerator, self.denominator))
321 MusicWrapper.print_ly (self, func)
324 class NestedMusic(Music):
326 Music.__init__ (self)
329 def append (self, what):
331 self.elements.append (what)
333 def has_children (self):
336 def insert_around (self, succ, elt, dir):
337 assert elt.parent == None
338 assert succ == None or succ in self.elements
343 idx = self.elements.index (succ)
350 idx = len (self.elements)
352 self.elements.insert (idx, elt)
355 def get_properties (self):
356 return ("'elements (list %s)"
357 % string.join (map (lambda x: x.lisp_expression(),
360 def get_subset_properties (self, predicate):
361 return ("'elements (list %s)"
362 % string.join (map (lambda x: x.lisp_expression(),
363 filter ( predicate, self.elements))))
364 def get_neighbor (self, music, dir):
365 assert music.parent == self
366 idx = self.elements.index (music)
368 idx = min (idx, len (self.elements) -1)
371 return self.elements[idx]
373 def delete_element (self, element):
374 assert element in self.elements
376 self.elements.remove (element)
377 element.parent = None
379 def set_start (self, start):
381 for e in self.elements:
384 def find_first (self, predicate):
385 r = Music.find_first (self, predicate)
389 for e in self.elements:
390 r = e.find_first (predicate)
395 class SequentialMusic (NestedMusic):
396 def print_ly (self, printer):
399 self.print_comment (printer)
402 for e in self.elements:
408 def lisp_sub_expression (self, pred):
412 props = self.get_subset_properties (pred)
414 return "(make-music '%s %s)" % (name, props)
416 def set_start (self, start):
417 for e in self.elements:
419 start += e.get_length()
423 self.lyrics_syllables = []
425 def print_ly (self, printer):
426 printer.dump ("\lyricmode {")
427 for l in self.lyrics_syllables:
428 printer.dump ( "%s " % l )
431 def ly_expression (self):
432 lstr = "\lyricmode {\n "
433 for l in self.lyrics_syllables:
441 self.header_fields = {}
442 def set_field (self, field, value):
443 self.header_fields[field] = value
445 def print_ly (self, printer):
446 printer.dump ("\header {")
448 for (k,v) in self.header_fields.items ():
450 printer.dump ('%s = %s' % (k,v))
459 class EventChord (NestedMusic):
461 NestedMusic.__init__ (self)
462 self.grace_elements = []
463 def append_grace (self, element):
465 self.grace_elements.append (element)
467 def get_length (self):
469 for e in self.elements:
470 l = max(l, e.get_length())
473 def print_ly (self, printer):
474 note_events = [e for e in self.elements if
475 isinstance (e, NoteEvent)]
477 rest_events = [e for e in self.elements if
478 isinstance (e, RhythmicEvent)
479 and not isinstance (e, NoteEvent)]
481 other_events = [e for e in self.elements if
482 not isinstance (e, RhythmicEvent)]
484 if self.grace_elements and self.elements:
486 for g in self.grace_elements:
491 rest_events[0].print_ly (printer)
492 elif len (note_events) == 1:
493 note_events[0].print_ly (printer)
495 pitches = [x.pitch.ly_expression () for x in note_events]
496 printer ('<%s>' % string.join (pitches))
497 note_events[0].duration.print_ly (printer)
501 for e in other_events:
504 self.print_comment (printer)
507 class BarCheck (Music):
509 Music.__init__ (self)
512 def print_ly (self, printer):
513 if self.bar_number > 0 and (self.bar_number % 10) == 0:
514 printer.dump ("| \\barNumberCheck #%d " % self.bar_number)
518 printer.print_verbatim (' %% %d' % self.bar_number)
522 def ly_expression (self):
528 class SpanEvent (Event):
530 Event.__init__ (self)
531 self.span_direction = 0 # start/stop
532 self.line_type = 'solid'
533 self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
534 self.size = 0 # size of e.g. ocrave shift
535 def wait_for_note (self):
537 def get_properties(self):
538 return "'span-direction %d" % self.span_direction
539 def set_span_type (self, type):
540 self.span_type = type
542 class SlurEvent (SpanEvent):
543 def ly_expression (self):
546 # TODO: setting dashed/dotted line style does not work, because that
547 # command needs to be written before the note, not when the
548 # event is observed after the note!
549 #before = {'dotted': '\\slurDotted',
550 # 'dashed' : '\\slurDashed'}.get (self.line_type, '')
552 #after = '\\slurSolid'
554 return {-1: before + '(' + after,
555 1:')'}.get (self.span_direction, '')
557 class BeamEvent (SpanEvent):
558 def ly_expression (self):
560 1:']'}.get (self.span_direction, '')
562 class PedalEvent (SpanEvent):
563 def ly_expression (self):
564 return {-1: '\\sustainDown',
565 1:'\\sustainUp'}.get (self.span_direction, '')
567 # type==-1 means octave up, type==-2 means octave down
568 class OctaveShiftEvent (SpanEvent):
569 def wait_for_note (self):
571 def set_span_type (self, type):
572 self.span_type = {'up': 1, 'down': -1}.get (type, 0)
573 def ly_octave_shift_indicator (self):
574 # convert 8/15 to lilypond indicators (+-1/+-2)
575 value = {8: 1, 15: 2}.get (self.size, 0)
576 # negative values go up!
577 value *= -1*self.span_type
579 def ly_expression (self):
580 dir = self.ly_octave_shift_indicator ()
583 value = '#(set-octavation %s)' % dir
586 1: '#(set-octavation 0)'}.get (self.span_direction, '')
588 class TrillSpanEvent (SpanEvent):
589 def ly_expression (self):
590 return {-1: '\\startTrillSpan',
591 0: '', # no need to write out anything for type='continue'
592 1:'\\stopTrillSpan'}.get (self.span_direction, '')
594 class GlissandoEvent (SpanEvent):
595 def ly_expression (self):
597 # TODO: wavy-line glissandos don't work, becasue the style has to be
598 # set before the note, at the \glissando it's already too late!
599 #if self.line_type == 'wavy':
600 #style = "\once\override Glissando #'style = #'zigzag"
601 # In lilypond, glissando is NOT a spanner, unlike MusicXML.
602 return {-1: style + '\\glissando',
603 1:''}.get (self.span_direction, '')
605 class ArpeggioEvent(Event):
606 def wait_for_note (self):
608 def ly_expression (self):
609 return ('\\arpeggio')
612 class TieEvent(Event):
613 def ly_expression (self):
617 class HairpinEvent (SpanEvent):
618 def set_span_type (self, type):
619 self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
620 def hairpin_to_ly (self):
621 if self.span_direction == 1:
624 return {1: '\<', -1: '\>'}.get (self.span_type, '')
626 def ly_expression (self):
627 return self.hairpin_to_ly ()
629 def print_ly (self, printer):
630 val = self.hairpin_to_ly ()
636 class DynamicsEvent (Event):
639 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
641 "f", "ff", "fff", "ffff",
642 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
643 def wait_for_note (self):
645 def ly_expression (self):
646 if self.type == None:
648 elif self.type in self.available_commands:
649 return '\%s' % self.type
651 return '-\markup{ \dynamic %s }' % self.type
653 def print_ly (self, printer):
654 if self.type == None:
656 elif self.type in self.available_commands:
657 printer.dump ("\\%s" % self.type)
659 printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
662 class ArticulationEvent (Event):
665 self.force_direction = None
667 def direction_mod (self):
668 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
670 def ly_expression (self):
671 return '%s\\%s' % (self.direction_mod (), self.type)
673 class ShortArticulationEvent (ArticulationEvent):
674 def direction_mod (self):
676 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
677 def ly_expression (self):
678 return '%s%s' % (self.direction_mod (), self.type)
680 class TremoloEvent (Event):
682 Event.__init__ (self)
685 def ly_expression (self):
688 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
691 class BendEvent (Event):
693 Event.__init__ (self)
695 def ly_expression (self):
697 return "-\\bendAfter #%s" % self.alter
701 class RhythmicEvent(Event):
703 Event.__init__ (self)
704 self.duration = Duration()
706 def get_length (self):
707 return self.duration.get_length()
709 def get_properties (self):
710 return ("'duration %s"
711 % self.duration.lisp_expression ())
713 class RestEvent (RhythmicEvent):
715 RhythmicEvent.__init__ (self)
717 def ly_expression (self):
719 return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
721 return 'r%s' % self.duration.ly_expression ()
723 def print_ly (self, printer):
725 self.pitch.print_ly (printer)
726 self.duration.print_ly (printer)
730 self.duration.print_ly (printer)
732 class SkipEvent (RhythmicEvent):
733 def ly_expression (self):
734 return 's%s' % self.duration.ly_expression ()
736 class NoteEvent(RhythmicEvent):
738 RhythmicEvent.__init__ (self)
740 self.drum_type = None
741 self.cautionary = False
742 self.forced_accidental = False
744 def get_properties (self):
745 str = RhythmicEvent.get_properties (self)
748 str += self.pitch.lisp_expression ()
750 str += "'drum-type '%s" % self.drum_type
754 def pitch_mods (self):
758 if self.forced_accidental:
763 def ly_expression (self):
765 return '%s%s%s' % (self.pitch.ly_expression (),
767 self.duration.ly_expression ())
769 return '%s%s' (self.drum_type,
770 self.duration.ly_expression ())
772 def print_ly (self, printer):
774 self.pitch.print_ly (printer)
775 printer (self.pitch_mods ())
777 printer (self.drum_type)
779 self.duration.print_ly (printer)
781 class KeySignatureChange (Music):
783 Music.__init__ (self)
788 def ly_expression (self):
789 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
792 def lisp_expression (self):
793 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
794 scale_str = ("'(%s)" % string.join (pairs))
796 return """ (make-music 'KeyChangeEvent
797 'pitch-alist %s) """ % scale_str
799 class TimeSignatureChange (Music):
801 Music.__init__ (self)
802 self.fraction = (4,4)
803 def ly_expression (self):
804 return '\\time %d/%d ' % self.fraction
806 class ClefChange (Music):
808 Music.__init__ (self)
813 def octave_modifier (self):
814 return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
815 def clef_name (self):
816 return {('G', 2): "treble",
819 ('C', 2): "mezzosoprano",
822 ('C', 5): "baritone",
823 ('F', 3): "varbaritone",
826 ("percussion", 2): "percussion",
827 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
828 def ly_expression (self):
829 return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
832 "G": ("clefs.G", -2, -6),
833 "C": ("clefs.C", 0, 0),
834 "F": ("clefs.F", 2, 6),
837 def lisp_expression (self):
839 (glyph, pos, c0) = self.clef_dict[self.type]
843 (make-music 'SequentialMusic
846 (make-property-set 'clefGlyph "%s") 'Staff)
848 (make-property-set 'clefPosition %d) 'Staff)
850 (make-property-set 'middleCPosition %d) 'Staff)))
851 """ % (glyph, pos, c0)
855 class MultiMeasureRest(Music):
857 def lisp_expression (self):
860 'MultiMeasureRestMusicGroup
862 (list (make-music (quote BarCheck))
867 'MultiMeasureRestEvent
870 (make-music (quote BarCheck))))
871 """ % self.duration.lisp_expression ()
873 def ly_expression (self):
874 return 'R%s' % self.duration.ly_expression ()
878 def __init__ (self, command = "StaffGroup"):
879 self.stafftype = command
881 self.instrument_name = None
882 self.short_instrument_name = None
886 # part_information is a list with entries of the form
887 # [staffid, voicelist]
888 # where voicelist is a list with entries of the form
889 # [voiceid1, [lyricsid11, lyricsid12,...] ]
890 self.part_information = None
892 def appendStaff (self, staff):
893 self.children.append (staff)
895 def setPartInformation (self, part_name, staves_info):
896 if part_name == self.id:
897 self.part_information = staves_info
899 for c in self.children:
900 c.setPartInformation (part_name, staves_info)
902 def print_ly_contents (self, printer):
903 for c in self.children:
906 def print_ly_overrides (self, printer):
908 needs_with |= self.spanbar == "no"
909 needs_with |= self.instrument_name != None
910 needs_with |= self.short_instrument_name != None
911 needs_with |= (self.symbol != None) and (self.symbol != "bracket")
913 printer.dump ("\\with {")
914 if self.instrument_name or self.short_instrument_name:
915 printer.dump ("\\consists \"Instrument_name_engraver\"")
916 if self.spanbar == "no":
917 printer.dump ("\\override SpanBar #'transparent = ##t")
918 brack = {"brace": "SystemStartBrace",
920 "line": "SystemStartBar"}.get (self.symbol, None)
922 printer.dump ("systemStartDelimiter = #'%s" % brack)
925 def print_ly (self, printer):
927 printer.dump ("\\new %s" % self.stafftype)
928 self.print_ly_overrides (printer)
931 if self.stafftype and self.instrument_name:
932 printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype,
933 escape_output_string (self.instrument_name)))
935 if self.stafftype and self.short_instrument_name:
936 printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype,
937 escape_output_string (self.short_instrument_name)))
939 self.print_ly_contents (printer)
945 class Staff (StaffGroup):
947 StaffGroup.__init__ (self, "Staff")
950 def print_ly_overrides (self, printer):
953 def print_ly_contents (self, printer):
954 if not self.id or not self.part_information:
957 for [staff_id, voices] in self.part_information:
959 printer ('\\context Staff = "%s" << ' % staff_id)
961 printer ('\\context Staff << ')
964 nr_voices = len (voices)
965 for [v, lyrics] in voices:
967 voice_count_text = ''
969 voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
970 3: ' \\voiceThree'}.get (n, ' \\voiceFour')
971 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
975 printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
979 def print_ly (self, printer):
980 if self.part_information and len (self.part_information) > 1:
981 self.stafftype = "PianoStaff"
982 StaffGroup.print_ly (self, printer)
987 bflat.alteration = -1
997 print bflat.semitones()
998 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
999 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1001 print bflat.semitones(), 'down'
1002 print bflat.transposed (down)
1003 print bflat.transposed (down).transposed (down)
1004 print bflat.transposed (down).transposed (down).transposed (down)
1008 def test_printer ():
1016 m = SequentialMusic()
1017 m.append (make_note ())
1018 m.append (make_note ())
1019 m.append (make_note ())
1022 t = TimeScaledMusic ()
1028 m = SequentialMusic ()
1029 m.append (make_tup ())
1030 m.append (make_tup ())
1031 m.append (make_tup ())
1033 printer = Output_printer()
1034 m.print_ly (printer)
1038 m = SequentialMusic()
1042 n.duration.duration_log = l
1044 evc.insert_around (None, n, 0)
1045 m.insert_around (None, evc, 0)
1049 n.duration.duration_log = l
1051 evc.insert_around (None, n, 0)
1052 m.insert_around (None, evc, 0)
1056 n.duration.duration_log = l
1058 evc.insert_around (None, n, 0)
1059 m.insert_around (None, evc, 0)
1063 m.insert_around (None, evc, 0)
1068 tonic.alteration = -2
1069 n = KeySignatureChange()
1070 n.tonic=tonic.copy()
1071 n.scale = [0, 0, -2, 0, 0,-2,-2]
1073 evc.insert_around (None, n, 0)
1074 m.insert_around (None, evc, 0)
1079 if __name__ == '__main__':
1085 expr.set_start (Rational (0))
1086 print expr.ly_expression()
1087 start = Rational (0,4)
1088 stop = Rational (4,2)
1089 def sub(x, start=start, stop=stop):
1090 ok = x.start >= start and x.start +x.get_length() <= stop
1093 print expr.lisp_sub_expression(sub)