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 self._nesting += str.count ('<') + str.count ('{')
79 self._nesting -= str.count ('>') + str.count ('}')
80 self.print_verbatim (str)
82 def print_duration_string (self, str):
83 if self._last_duration == str:
86 self.unformatted_output (str)
88 def add_word (self, str):
89 if (len (str) + 1 + len (self._line) > self._line_len):
91 self._skipspace = True
93 if not self._skipspace:
95 self.unformatted_output (str)
96 self._skipspace = False
99 self._file.write (self._line + '\n')
100 self._line = ' ' * self._indent * self._nesting
101 self._skipspace = True
103 def skipspace (self):
104 self._skipspace = True
106 def __call__(self, arg):
109 def dump (self, str):
111 self._skipspace = False
112 self.unformatted_output (str)
114 words = string.split (str)
127 self.duration_log = 0
129 self.factor = Rational (1)
131 def lisp_expression (self):
132 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
134 self.factor.numerator (),
135 self.factor.denominator ())
138 def ly_expression (self, factor = None):
142 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
144 if factor <> Rational (1,1):
145 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
149 def print_ly (self, outputter):
150 str = self.ly_expression (self.factor / outputter.duration_factor ())
151 outputter.print_duration_string (str)
154 return self.ly_expression()
159 d.duration_log = self.duration_log
160 d.factor = self.factor
163 def get_length (self):
164 dot_fact = Rational( (1 << (1 + self.dots))-1,
167 log = abs (self.duration_log)
169 if self.duration_log < 0:
170 base = Rational (dur)
172 base = Rational (1, dur)
174 return base * dot_fact * self.factor
184 return self.ly_expression()
186 def transposed (self, interval):
188 c.alteration += interval.alteration
189 c.step += interval.step
190 c.octave += interval.octave
193 target_st = self.semitones() + interval.semitones()
194 c.alteration += target_st - c.semitones()
201 c.octave += c.step / 7
204 def lisp_expression (self):
205 return '(ly:make-pitch %d %d %d)' % (self.octave,
211 p.alteration = self.alteration
213 p.octave = self.octave
217 return self.step + self.octave *7
219 def semitones (self):
220 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
222 def ly_step_expression (self):
223 str = 'cdefgab'[self.step]
224 if self.alteration > 0:
225 str += 'is'* (self.alteration)
226 elif self.alteration < 0:
227 str += 'es'* (-self.alteration)
229 return str.replace ('aes', 'as').replace ('ees', 'es')
231 def ly_expression (self):
232 str = self.ly_step_expression ()
234 str += "'" * (self.octave + 1)
235 elif self.octave < -1:
236 str += "," * (-self.octave - 1)
240 def print_ly (self, outputter):
241 outputter (self.ly_expression())
246 self.start = Rational (0)
248 self.identifier = None
250 def get_length(self):
253 def get_properties (self):
256 def has_children (self):
259 def get_index (self):
261 return self.parent.elements.index (self)
265 return self.__class__.__name__
267 def lisp_expression (self):
270 props = self.get_properties ()
272 return "(make-music '%s %s)" % (name, props)
274 def set_start (self, start):
277 def find_first (self, predicate):
282 def print_comment (self, printer, text = None):
293 lines = string.split (text, '\n')
296 printer.unformatted_output ('% ' + l)
300 def print_with_identifier (self, printer):
302 printer ("\\%s" % self.identifier)
304 self.print_ly (printer)
306 def print_ly (self, printer):
307 printer (self.ly_expression ())
309 class MusicWrapper (Music):
313 def print_ly (self, func):
314 self.element.print_ly (func)
316 class ModeChangingMusicWrapper (MusicWrapper):
318 MusicWrapper.__init__ (self)
319 self.mode = 'notemode'
321 def print_ly (self, func):
322 func ('\\%s' % self.mode)
323 MusicWrapper.print_ly (self, func)
325 class TimeScaledMusic (MusicWrapper):
326 def print_ly (self, func):
327 func ('\\times %d/%d ' %
328 (self.numerator, self.denominator))
329 func.add_factor (Rational (self.numerator, self.denominator))
330 MusicWrapper.print_ly (self, func)
333 class NestedMusic(Music):
335 Music.__init__ (self)
338 def append (self, what):
340 self.elements.append (what)
342 def has_children (self):
345 def insert_around (self, succ, elt, dir):
346 assert elt.parent == None
347 assert succ == None or succ in self.elements
352 idx = self.elements.index (succ)
359 idx = len (self.elements)
361 self.elements.insert (idx, elt)
364 def get_properties (self):
365 return ("'elements (list %s)"
366 % string.join (map (lambda x: x.lisp_expression(),
369 def get_subset_properties (self, predicate):
370 return ("'elements (list %s)"
371 % string.join (map (lambda x: x.lisp_expression(),
372 filter ( predicate, self.elements))))
373 def get_neighbor (self, music, dir):
374 assert music.parent == self
375 idx = self.elements.index (music)
377 idx = min (idx, len (self.elements) -1)
380 return self.elements[idx]
382 def delete_element (self, element):
383 assert element in self.elements
385 self.elements.remove (element)
386 element.parent = None
388 def set_start (self, start):
390 for e in self.elements:
393 def find_first (self, predicate):
394 r = Music.find_first (self, predicate)
398 for e in self.elements:
399 r = e.find_first (predicate)
404 class SequentialMusic (NestedMusic):
405 def get_last_event_chord (self):
407 at = len( self.elements ) - 1
409 not isinstance (self.elements[at], EventChord) and
410 not isinstance (self.elements[at], BarCheck)):
413 if (at >= 0 and isinstance (self.elements[at], EventChord)):
414 value = self.elements[at]
417 def print_ly (self, printer, newline = True):
420 self.print_comment (printer)
424 for e in self.elements:
431 def lisp_sub_expression (self, pred):
435 props = self.get_subset_properties (pred)
437 return "(make-music '%s %s)" % (name, props)
439 def set_start (self, start):
440 for e in self.elements:
442 start += e.get_length()
446 self.lyrics_syllables = []
448 def print_ly (self, printer):
449 printer.dump ("\lyricmode {")
450 for l in self.lyrics_syllables:
451 printer.dump ( "%s " % l )
454 def ly_expression (self):
455 lstr = "\lyricmode {\n "
456 for l in self.lyrics_syllables:
464 self.header_fields = {}
465 def set_field (self, field, value):
466 self.header_fields[field] = value
468 def print_ly (self, printer):
469 printer.dump ("\header {")
471 for (k,v) in self.header_fields.items ():
473 printer.dump ('%s = %s' % (k,v))
482 class EventChord (NestedMusic):
484 NestedMusic.__init__ (self)
485 self.grace_elements = None
486 self.grace_type = None
487 def append_grace (self, element):
489 if not self.grace_elements:
490 self.grace_elements = SequentialMusic ()
491 self.grace_elements.append (element)
493 def get_length (self):
495 for e in self.elements:
496 l = max(l, e.get_length())
499 def print_ly (self, printer):
500 note_events = [e for e in self.elements if
501 isinstance (e, NoteEvent)]
503 rest_events = [e for e in self.elements if
504 isinstance (e, RhythmicEvent)
505 and not isinstance (e, NoteEvent)]
507 other_events = [e for e in self.elements if
508 not isinstance (e, RhythmicEvent)]
510 if self.grace_elements and self.elements:
512 printer ('\\%s' % self.grace_type)
515 # don't print newlines after the { and } braces
516 self.grace_elements.print_ly (printer, False)
519 rest_events[0].print_ly (printer)
520 elif len (note_events) == 1:
521 note_events[0].print_ly (printer)
523 pitches = [x.pitch.ly_expression () for x in note_events]
524 printer ('<%s>' % string.join (pitches))
525 note_events[0].duration.print_ly (printer)
529 for e in other_events:
532 self.print_comment (printer)
535 class BarCheck (Music):
537 Music.__init__ (self)
540 def print_ly (self, printer):
541 if self.bar_number > 0 and (self.bar_number % 10) == 0:
542 printer.dump ("| \\barNumberCheck #%d " % self.bar_number)
546 printer.print_verbatim (' %% %d' % self.bar_number)
550 def ly_expression (self):
556 class SpanEvent (Event):
558 Event.__init__ (self)
559 self.span_direction = 0 # start/stop
560 self.line_type = 'solid'
561 self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
562 self.size = 0 # size of e.g. ocrave shift
563 def wait_for_note (self):
565 def get_properties(self):
566 return "'span-direction %d" % self.span_direction
567 def set_span_type (self, type):
568 self.span_type = type
570 class SlurEvent (SpanEvent):
571 def ly_expression (self):
574 # TODO: setting dashed/dotted line style does not work, because that
575 # command needs to be written before the note, not when the
576 # event is observed after the note!
577 #before = {'dotted': '\\slurDotted',
578 # 'dashed' : '\\slurDashed'}.get (self.line_type, '')
580 #after = '\\slurSolid'
582 return {-1: before + '(' + after,
583 1:')'}.get (self.span_direction, '')
585 class BeamEvent (SpanEvent):
586 def ly_expression (self):
588 1:']'}.get (self.span_direction, '')
590 class PedalEvent (SpanEvent):
591 def ly_expression (self):
592 return {-1: '\\sustainDown',
593 1:'\\sustainUp'}.get (self.span_direction, '')
595 # type==-1 means octave up, type==-2 means octave down
596 class OctaveShiftEvent (SpanEvent):
597 def wait_for_note (self):
599 def set_span_type (self, type):
600 self.span_type = {'up': 1, 'down': -1}.get (type, 0)
601 def ly_octave_shift_indicator (self):
602 # convert 8/15 to lilypond indicators (+-1/+-2)
603 value = {8: 1, 15: 2}.get (self.size, 0)
604 # negative values go up!
605 value *= -1*self.span_type
607 def ly_expression (self):
608 dir = self.ly_octave_shift_indicator ()
611 value = '#(set-octavation %s)' % dir
614 1: '#(set-octavation 0)'}.get (self.span_direction, '')
616 class TrillSpanEvent (SpanEvent):
617 def ly_expression (self):
618 return {-1: '\\startTrillSpan',
619 0: '', # no need to write out anything for type='continue'
620 1:'\\stopTrillSpan'}.get (self.span_direction, '')
622 class GlissandoEvent (SpanEvent):
623 def ly_expression (self):
625 # TODO: wavy-line glissandos don't work, becasue the style has to be
626 # set before the note, at the \glissando it's already too late!
627 #if self.line_type == 'wavy':
628 #style = "\once\override Glissando #'style = #'zigzag"
629 # In lilypond, glissando is NOT a spanner, unlike MusicXML.
630 return {-1: style + '\\glissando',
631 1:''}.get (self.span_direction, '')
633 class ArpeggioEvent(Event):
634 def wait_for_note (self):
636 def ly_expression (self):
637 return ('\\arpeggio')
640 class TieEvent(Event):
641 def ly_expression (self):
645 class HairpinEvent (SpanEvent):
646 def set_span_type (self, type):
647 self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
648 def hairpin_to_ly (self):
649 if self.span_direction == 1:
652 return {1: '\<', -1: '\>'}.get (self.span_type, '')
654 def ly_expression (self):
655 return self.hairpin_to_ly ()
657 def print_ly (self, printer):
658 val = self.hairpin_to_ly ()
664 class DynamicsEvent (Event):
667 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
669 "f", "ff", "fff", "ffff",
670 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
671 def wait_for_note (self):
673 def ly_expression (self):
674 if self.type == None:
676 elif self.type in self.available_commands:
677 return '\%s' % self.type
679 return '-\markup{ \dynamic %s }' % self.type
681 def print_ly (self, printer):
682 if self.type == None:
684 elif self.type in self.available_commands:
685 printer.dump ("\\%s" % self.type)
687 printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
690 class ArticulationEvent (Event):
693 self.force_direction = None
695 def direction_mod (self):
696 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
698 def ly_expression (self):
699 return '%s\\%s' % (self.direction_mod (), self.type)
701 class ShortArticulationEvent (ArticulationEvent):
702 def direction_mod (self):
704 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
705 def ly_expression (self):
706 return '%s%s' % (self.direction_mod (), self.type)
708 class TremoloEvent (Event):
710 Event.__init__ (self)
713 def ly_expression (self):
716 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
719 class BendEvent (Event):
721 Event.__init__ (self)
723 def ly_expression (self):
725 return "-\\bendAfter #%s" % self.alter
729 class RhythmicEvent(Event):
731 Event.__init__ (self)
732 self.duration = Duration()
734 def get_length (self):
735 return self.duration.get_length()
737 def get_properties (self):
738 return ("'duration %s"
739 % self.duration.lisp_expression ())
741 class RestEvent (RhythmicEvent):
743 RhythmicEvent.__init__ (self)
745 def ly_expression (self):
747 return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
749 return 'r%s' % self.duration.ly_expression ()
751 def print_ly (self, printer):
753 self.pitch.print_ly (printer)
754 self.duration.print_ly (printer)
758 self.duration.print_ly (printer)
760 class SkipEvent (RhythmicEvent):
761 def ly_expression (self):
762 return 's%s' % self.duration.ly_expression ()
764 class NoteEvent(RhythmicEvent):
766 RhythmicEvent.__init__ (self)
768 self.drum_type = None
769 self.cautionary = False
770 self.forced_accidental = False
772 def get_properties (self):
773 str = RhythmicEvent.get_properties (self)
776 str += self.pitch.lisp_expression ()
778 str += "'drum-type '%s" % self.drum_type
782 def pitch_mods (self):
786 if self.forced_accidental:
791 def ly_expression (self):
793 return '%s%s%s' % (self.pitch.ly_expression (),
795 self.duration.ly_expression ())
797 return '%s%s' (self.drum_type,
798 self.duration.ly_expression ())
800 def print_ly (self, printer):
802 self.pitch.print_ly (printer)
803 printer (self.pitch_mods ())
805 printer (self.drum_type)
807 self.duration.print_ly (printer)
809 class KeySignatureChange (Music):
811 Music.__init__ (self)
816 def ly_expression (self):
817 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
820 def lisp_expression (self):
821 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
822 scale_str = ("'(%s)" % string.join (pairs))
824 return """ (make-music 'KeyChangeEvent
825 'pitch-alist %s) """ % scale_str
827 class TimeSignatureChange (Music):
829 Music.__init__ (self)
830 self.fraction = (4,4)
831 def ly_expression (self):
832 return '\\time %d/%d ' % self.fraction
834 class ClefChange (Music):
836 Music.__init__ (self)
841 def octave_modifier (self):
842 return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
843 def clef_name (self):
844 return {('G', 2): "treble",
847 ('C', 2): "mezzosoprano",
850 ('C', 5): "baritone",
851 ('F', 3): "varbaritone",
854 ("percussion", 2): "percussion",
855 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
856 def ly_expression (self):
857 return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
860 "G": ("clefs.G", -2, -6),
861 "C": ("clefs.C", 0, 0),
862 "F": ("clefs.F", 2, 6),
865 def lisp_expression (self):
867 (glyph, pos, c0) = self.clef_dict[self.type]
871 (make-music 'SequentialMusic
874 (make-property-set 'clefGlyph "%s") 'Staff)
876 (make-property-set 'clefPosition %d) 'Staff)
878 (make-property-set 'middleCPosition %d) 'Staff)))
879 """ % (glyph, pos, c0)
883 class MultiMeasureRest(Music):
885 def lisp_expression (self):
888 'MultiMeasureRestMusicGroup
890 (list (make-music (quote BarCheck))
895 'MultiMeasureRestEvent
898 (make-music (quote BarCheck))))
899 """ % self.duration.lisp_expression ()
901 def ly_expression (self):
902 return 'R%s' % self.duration.ly_expression ()
906 def __init__ (self, command = "StaffGroup"):
907 self.stafftype = command
909 self.instrument_name = None
910 self.short_instrument_name = None
914 # part_information is a list with entries of the form
915 # [staffid, voicelist]
916 # where voicelist is a list with entries of the form
917 # [voiceid1, [lyricsid11, lyricsid12,...] ]
918 self.part_information = None
920 def appendStaff (self, staff):
921 self.children.append (staff)
923 def setPartInformation (self, part_name, staves_info):
924 if part_name == self.id:
925 self.part_information = staves_info
927 for c in self.children:
928 c.setPartInformation (part_name, staves_info)
930 def print_ly_contents (self, printer):
931 for c in self.children:
934 def print_ly_overrides (self, printer):
936 needs_with |= self.spanbar == "no"
937 needs_with |= self.instrument_name != None
938 needs_with |= self.short_instrument_name != None
939 needs_with |= (self.symbol != None) and (self.symbol != "bracket")
941 printer.dump ("\\with {")
942 if self.instrument_name or self.short_instrument_name:
943 printer.dump ("\\consists \"Instrument_name_engraver\"")
944 if self.spanbar == "no":
945 printer.dump ("\\override SpanBar #'transparent = ##t")
946 brack = {"brace": "SystemStartBrace",
948 "line": "SystemStartBar"}.get (self.symbol, None)
950 printer.dump ("systemStartDelimiter = #'%s" % brack)
953 def print_ly (self, printer):
955 printer.dump ("\\new %s" % self.stafftype)
956 self.print_ly_overrides (printer)
959 if self.stafftype and self.instrument_name:
960 printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype,
961 escape_instrument_string (self.instrument_name)))
963 if self.stafftype and self.short_instrument_name:
964 printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype,
965 escape_instrument_string (self.short_instrument_name)))
967 self.print_ly_contents (printer)
973 class Staff (StaffGroup):
975 StaffGroup.__init__ (self, "Staff")
978 def print_ly_overrides (self, printer):
981 def print_ly_contents (self, printer):
982 if not self.id or not self.part_information:
985 for [staff_id, voices] in self.part_information:
987 printer ('\\context Staff = "%s" << ' % staff_id)
989 printer ('\\context Staff << ')
992 nr_voices = len (voices)
993 for [v, lyrics] in voices:
995 voice_count_text = ''
997 voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
998 3: ' \\voiceThree'}.get (n, ' \\voiceFour')
999 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1003 printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1007 def print_ly (self, printer):
1008 if self.part_information and len (self.part_information) > 1:
1009 self.stafftype = "PianoStaff"
1010 StaffGroup.print_ly (self, printer)
1015 bflat.alteration = -1
1025 print bflat.semitones()
1026 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
1027 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1029 print bflat.semitones(), 'down'
1030 print bflat.transposed (down)
1031 print bflat.transposed (down).transposed (down)
1032 print bflat.transposed (down).transposed (down).transposed (down)
1036 def test_printer ():
1044 m = SequentialMusic()
1045 m.append (make_note ())
1046 m.append (make_note ())
1047 m.append (make_note ())
1050 t = TimeScaledMusic ()
1056 m = SequentialMusic ()
1057 m.append (make_tup ())
1058 m.append (make_tup ())
1059 m.append (make_tup ())
1061 printer = Output_printer()
1062 m.print_ly (printer)
1066 m = SequentialMusic()
1070 n.duration.duration_log = l
1072 evc.insert_around (None, n, 0)
1073 m.insert_around (None, evc, 0)
1077 n.duration.duration_log = l
1079 evc.insert_around (None, n, 0)
1080 m.insert_around (None, evc, 0)
1084 n.duration.duration_log = l
1086 evc.insert_around (None, n, 0)
1087 m.insert_around (None, evc, 0)
1091 m.insert_around (None, evc, 0)
1096 tonic.alteration = -2
1097 n = KeySignatureChange()
1098 n.tonic=tonic.copy()
1099 n.scale = [0, 0, -2, 0, 0,-2,-2]
1101 evc.insert_around (None, n, 0)
1102 m.insert_around (None, evc, 0)
1107 if __name__ == '__main__':
1113 expr.set_start (Rational (0))
1114 print expr.ly_expression()
1115 start = Rational (0,4)
1116 stop = Rational (4,2)
1117 def sub(x, start=start, stop=stop):
1118 ok = x.start >= start and x.start +x.get_length() <= stop
1121 print expr.lisp_sub_expression(sub)