6 from rational import Rational
9 class Output_stack_element:
11 self.factor = Rational (1)
13 o = Output_stack_element()
14 o.factor = self.factor
19 """A class that takes care of formatting (eg.: indenting) a
20 Music expression as a .ly file.
23 ## TODO: support for \relative.
29 self._file = sys.stdout
31 self._output_state_stack = [Output_stack_element()]
32 self._skipspace = False
33 self._last_duration = None
35 def set_file (self, file):
38 def dump_version (self):
40 self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
43 def get_indent (self):
44 return self._nesting * self._indent
47 last = self._output_state_stack[-1]
48 self._output_state_stack.append (last.copy())
50 def add_factor (self, factor):
52 self._output_state_stack[-1].factor *= factor
55 del self._output_state_stack[-1]
56 if not self._output_state_stack:
59 def duration_factor (self):
60 return self._output_state_stack[-1].factor
62 def print_verbatim (self, str):
65 def unformatted_output (self, str):
66 self._nesting += str.count ('<') + str.count ('{')
67 self._nesting -= str.count ('>') + str.count ('}')
68 self.print_verbatim (str)
70 def print_duration_string (self, str):
71 if self._last_duration == str:
74 self.unformatted_output (str)
76 def add_word (self, str):
77 if (len (str) + 1 + len (self._line) > self._line_len):
79 self._skipspace = True
81 if not self._skipspace:
83 self.unformatted_output (str)
84 self._skipspace = False
87 self._file.write (self._line + '\n')
88 self._line = ' ' * self._indent * self._nesting
89 self._skipspace = True
92 self._skipspace = True
94 def __call__(self, arg):
99 self._skipspace = False
100 self.unformatted_output (str)
102 words = string.split (str)
115 self.duration_log = 0
117 self.factor = Rational (1)
119 def lisp_expression (self):
120 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
122 self.factor.numerator (),
123 self.factor.denominator ())
126 def ly_expression (self, factor = None):
130 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
132 if factor <> Rational (1,1):
133 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
137 def print_ly (self, outputter):
138 str = self.ly_expression (self.factor / outputter.duration_factor ())
139 outputter.print_duration_string (str)
142 return self.ly_expression()
147 d.duration_log = self.duration_log
148 d.factor = self.factor
151 def get_length (self):
152 dot_fact = Rational( (1 << (1 + self.dots))-1,
155 log = abs (self.duration_log)
157 if self.duration_log < 0:
158 base = Rational (dur)
160 base = Rational (1, dur)
162 return base * dot_fact * self.factor
172 return self.ly_expression()
174 def transposed (self, interval):
176 c.alteration += interval.alteration
177 c.step += interval.step
178 c.octave += interval.octave
181 target_st = self.semitones() + interval.semitones()
182 c.alteration += target_st - c.semitones()
189 c.octave += c.step / 7
192 def lisp_expression (self):
193 return '(ly:make-pitch %d %d %d)' % (self.octave,
199 p.alteration = self.alteration
201 p.octave = self.octave
205 return self.step + self.octave *7
207 def semitones (self):
208 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
210 def ly_step_expression (self):
211 str = 'cdefgab'[self.step]
212 if self.alteration > 0:
213 str += 'is'* (self.alteration)
214 elif self.alteration < 0:
215 str += 'es'* (-self.alteration)
217 return str.replace ('aes', 'as').replace ('ees', 'es')
219 def ly_expression (self):
220 str = self.ly_step_expression ()
222 str += "'" * (self.octave + 1)
223 elif self.octave < -1:
224 str += "," * (-self.octave - 1)
228 def print_ly (self, outputter):
229 outputter (self.ly_expression())
234 self.start = Rational (0)
236 self.identifier = None
238 def get_length(self):
241 def get_properties (self):
244 def has_children (self):
247 def get_index (self):
249 return self.parent.elements.index (self)
253 return self.__class__.__name__
255 def lisp_expression (self):
258 props = self.get_properties ()
260 return "(make-music '%s %s)" % (name, props)
262 def set_start (self, start):
265 def find_first (self, predicate):
270 def print_comment (self, printer, text = None):
281 lines = string.split (text, '\n')
284 printer.unformatted_output ('% ' + l)
288 def print_with_identifier (self, printer):
290 printer ("\\%s" % self.identifier)
292 self.print_ly (printer)
294 def print_ly (self, printer):
295 printer (self.ly_expression ())
297 class MusicWrapper (Music):
301 def print_ly (self, func):
302 self.element.print_ly (func)
304 class ModeChangingMusicWrapper (MusicWrapper):
306 MusicWrapper.__init__ (self)
307 self.mode = 'notemode'
309 def print_ly (self, func):
310 func ('\\%s' % self.mode)
311 MusicWrapper.print_ly (self, func)
313 class TimeScaledMusic (MusicWrapper):
314 def print_ly (self, func):
315 func ('\\times %d/%d ' %
316 (self.numerator, self.denominator))
317 func.add_factor (Rational (self.numerator, self.denominator))
318 MusicWrapper.print_ly (self, func)
321 class NestedMusic(Music):
323 Music.__init__ (self)
326 def append (self, what):
328 self.elements.append (what)
330 def has_children (self):
333 def insert_around (self, succ, elt, dir):
334 assert elt.parent == None
335 assert succ == None or succ in self.elements
340 idx = self.elements.index (succ)
347 idx = len (self.elements)
349 self.elements.insert (idx, elt)
352 def get_properties (self):
353 return ("'elements (list %s)"
354 % string.join (map (lambda x: x.lisp_expression(),
357 def get_subset_properties (self, predicate):
358 return ("'elements (list %s)"
359 % string.join (map (lambda x: x.lisp_expression(),
360 filter ( predicate, self.elements))))
361 def get_neighbor (self, music, dir):
362 assert music.parent == self
363 idx = self.elements.index (music)
365 idx = min (idx, len (self.elements) -1)
368 return self.elements[idx]
370 def delete_element (self, element):
371 assert element in self.elements
373 self.elements.remove (element)
374 element.parent = None
376 def set_start (self, start):
378 for e in self.elements:
381 def find_first (self, predicate):
382 r = Music.find_first (self, predicate)
386 for e in self.elements:
387 r = e.find_first (predicate)
392 class SequentialMusic (NestedMusic):
393 def print_ly (self, printer):
396 self.print_comment (printer)
399 for e in self.elements:
405 def lisp_sub_expression (self, pred):
409 props = self.get_subset_properties (pred)
411 return "(make-music '%s %s)" % (name, props)
413 def set_start (self, start):
414 for e in self.elements:
416 start += e.get_length()
420 self.lyrics_syllables = []
422 def print_ly (self, printer):
423 printer.dump ("\lyricmode {")
424 for l in self.lyrics_syllables:
425 printer.dump ( "%s " % l )
428 def ly_expression (self):
429 lstr = "\lyricmode {\n "
430 for l in self.lyrics_syllables:
436 class EventChord (NestedMusic):
437 def get_length (self):
439 for e in self.elements:
440 l = max(l, e.get_length())
443 def print_ly (self, printer):
444 note_events = [e for e in self.elements if
445 isinstance (e, NoteEvent)]
447 rest_events = [e for e in self.elements if
448 isinstance (e, RhythmicEvent)
449 and not isinstance (e, NoteEvent)]
451 other_events = [e for e in self.elements if
452 not isinstance (e, RhythmicEvent)]
455 rest_events[0].print_ly (printer)
456 elif len (note_events) == 1:
457 note_events[0].print_ly (printer)
459 pitches = [x.pitch.ly_expression () for x in note_events]
460 printer ('<%s>' % string.join (pitches))
461 note_events[0].duration.print_ly (printer)
465 for e in other_events:
468 self.print_comment (printer)
471 class BarCheck (Music):
473 Music.__init__ (self)
476 def print_ly (self, printer):
477 if self.bar_number > 0 and (self.bar_number % 10) == 0:
478 printer.dump ("| \\barNumberCheck #%d " % self.bar_number)
482 printer.print_verbatim (' %% %d' % self.bar_number)
486 def ly_expression (self):
492 class SpanEvent (Event):
494 Event.__init__ (self)
495 self.span_direction = 0
498 def wait_for_note (self):
500 def get_properties(self):
501 return "'span-direction %d" % self.span_direction
503 class SlurEvent (SpanEvent):
504 def ly_expression (self):
507 # TODO: setting dashed/dotted line style does not work, because that
508 # command needs to be written before the note, not when the
509 # event is observed after the note!
510 #if self.line_type == 1:
511 #before = '\\slurDotted'
512 #elif self.line_type == 2:
513 #before = '\\slurDashed'
515 #after = '\\slurSolid'
517 return {-1: before + '(' + after,
519 1:')'}.get (self.span_direction, '')
521 class BeamEvent (SpanEvent):
522 def ly_expression (self):
525 1:']'}.get (self.span_direction, '')
527 class PedalEvent (SpanEvent):
528 def ly_expression (self):
529 return {-1: '\\sustainDown',
531 1:'\\sustainUp'}.get (self.span_direction, '')
533 # type==-1 means octave up, type==-2 means octave down
534 class OctaveShiftEvent (SpanEvent):
535 def wait_for_note (self):
537 def ly_octave_shift_indicator (self):
540 elif self.size == 15:
545 if self.span_direction == -2:
548 def ly_expression (self):
549 dir = self.ly_octave_shift_indicator ()
552 value = '#(set-octavation %s)' % dir
556 1: '#(set-octavation 0)'}.get (self.span_direction, '')
558 class TrillSpanEvent (SpanEvent):
559 def ly_expression (self):
560 return {-1: '\\startTrillSpan',
562 1:'\\stopTrillSpan'}.get (self.span_direction, '')
564 class GlissandoEvent (SpanEvent):
565 def ly_expression (self):
567 # TODO: wavy-line glissandos don't work, becasue the style has to be
568 # set before the note, at the \glissando it's already too late!
569 #if self.line_type == 3: # wavy-line:
570 #style = "\once\override Glissando #'style = #'zigzag"
571 # In lilypond, glissando is NOT a spanner, unlike MusicXML.
572 return {-1: style + '\\glissando',
574 1:''}.get (self.span_direction, '')
576 class ArpeggioEvent(Event):
577 def wait_for_note (self):
579 def ly_expression (self):
580 return ('\\arpeggio')
583 class TieEvent(Event):
584 def ly_expression (self):
588 class HairpinEvent (SpanEvent):
589 def __init__ (self, type):
591 def hairpin_to_ly (self):
592 return { 0: '\!', 1: '\<', -1: '\>' }.get (self.type, '')
594 def ly_expression (self):
595 return self.hairpin_to_ly ()
597 def print_ly (self, printer):
598 val = self.hairpin_to_ly ()
604 class DynamicsEvent (Event):
607 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
609 "f", "ff", "fff", "ffff",
610 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
611 def wait_for_note (self):
613 def ly_expression (self):
614 if self.type == None:
616 elif self.type in self.available_commands:
617 return '\%s' % self.type
619 return '-\markup{ \dynamic %s }' % self.type
621 def print_ly (self, printer):
622 if self.type == None:
624 elif self.type in self.available_commands:
625 printer.dump ("\\%s" % self.type)
627 printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
630 class ArticulationEvent (Event):
633 self.force_direction = None
635 def direction_mod (self):
636 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
638 def ly_expression (self):
639 return '%s\\%s' % (self.direction_mod (), self.type)
641 class ShortArticulationEvent (ArticulationEvent):
642 def direction_mod (self):
644 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
645 def ly_expression (self):
646 return '%s%s' % (self.direction_mod (), self.type)
648 class TremoloEvent (Event):
650 Event.__init__ (self)
653 def ly_expression (self):
656 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
659 class BendEvent (Event):
661 Event.__init__ (self)
663 def ly_expression (self):
665 return "-\\bendAfter #%s" % self.alter
669 class RhythmicEvent(Event):
671 Event.__init__ (self)
672 self.duration = Duration()
674 def get_length (self):
675 return self.duration.get_length()
677 def get_properties (self):
678 return ("'duration %s"
679 % self.duration.lisp_expression ())
681 class RestEvent (RhythmicEvent):
682 def ly_expression (self):
683 return 'r%s' % self.duration.ly_expression ()
685 def print_ly (self, printer):
687 self.duration.print_ly (printer)
689 class SkipEvent (RhythmicEvent):
690 def ly_expression (self):
691 return 's%s' % self.duration.ly_expression ()
693 class NoteEvent(RhythmicEvent):
695 RhythmicEvent.__init__ (self)
697 self.drum_type = None
698 self.cautionary = False
699 self.forced_accidental = False
701 def get_properties (self):
702 str = RhythmicEvent.get_properties (self)
705 str += self.pitch.lisp_expression ()
707 str += "'drum-type '%s" % self.drum_type
711 def pitch_mods (self):
715 if self.forced_accidental:
720 def ly_expression (self):
722 return '%s%s%s' % (self.pitch.ly_expression (),
724 self.duration.ly_expression ())
726 return '%s%s' (self.drum_type,
727 self.duration.ly_expression ())
729 def print_ly (self, printer):
731 self.pitch.print_ly (printer)
732 printer (self.pitch_mods ())
734 printer (self.drum_type)
736 self.duration.print_ly (printer)
738 class KeySignatureChange (Music):
740 Music.__init__ (self)
745 def ly_expression (self):
746 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
749 def lisp_expression (self):
750 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
751 scale_str = ("'(%s)" % string.join (pairs))
753 return """ (make-music 'KeyChangeEvent
754 'pitch-alist %s) """ % scale_str
756 class TimeSignatureChange (Music):
758 Music.__init__ (self)
759 self.fraction = (4,4)
760 def ly_expression (self):
761 return '\\time %d/%d ' % self.fraction
763 class ClefChange (Music):
765 Music.__init__ (self)
770 def octave_modifier (self):
771 return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
772 def clef_name (self):
773 return {('G', 2): "treble",
776 ('C', 2): "mezzosoprano",
779 ('C', 5): "baritone",
780 ('F', 3): "varbaritone",
783 ("percussion", 2): "percussion",
784 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
785 def ly_expression (self):
786 return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
789 "G": ("clefs.G", -2, -6),
790 "C": ("clefs.C", 0, 0),
791 "F": ("clefs.F", 2, 6),
794 def lisp_expression (self):
796 (glyph, pos, c0) = self.clef_dict[self.type]
800 (make-music 'SequentialMusic
803 (make-property-set 'clefGlyph "%s") 'Staff)
805 (make-property-set 'clefPosition %d) 'Staff)
807 (make-property-set 'middleCPosition %d) 'Staff)))
808 """ % (glyph, pos, c0)
812 class MultiMeasureRest(Music):
814 def lisp_expression (self):
817 'MultiMeasureRestMusicGroup
819 (list (make-music (quote BarCheck))
824 'MultiMeasureRestEvent
827 (make-music (quote BarCheck))))
828 """ % self.duration.lisp_expression ()
830 def ly_expression (self):
831 return 'R%s' % self.duration.ly_expression ()
836 bflat.alteration = -1
846 print bflat.semitones()
847 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
848 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
850 print bflat.semitones(), 'down'
851 print bflat.transposed (down)
852 print bflat.transposed (down).transposed (down)
853 print bflat.transposed (down).transposed (down).transposed (down)
865 m = SequentialMusic()
866 m.append (make_note ())
867 m.append (make_note ())
868 m.append (make_note ())
871 t = TimeScaledMusic ()
877 m = SequentialMusic ()
878 m.append (make_tup ())
879 m.append (make_tup ())
880 m.append (make_tup ())
882 printer = Output_printer()
887 m = SequentialMusic()
891 n.duration.duration_log = l
893 evc.insert_around (None, n, 0)
894 m.insert_around (None, evc, 0)
898 n.duration.duration_log = l
900 evc.insert_around (None, n, 0)
901 m.insert_around (None, evc, 0)
905 n.duration.duration_log = l
907 evc.insert_around (None, n, 0)
908 m.insert_around (None, evc, 0)
912 m.insert_around (None, evc, 0)
917 tonic.alteration = -2
918 n = KeySignatureChange()
920 n.scale = [0, 0, -2, 0, 0,-2,-2]
922 evc.insert_around (None, n, 0)
923 m.insert_around (None, evc, 0)
928 if __name__ == '__main__':
934 expr.set_start (Rational (0))
935 print expr.ly_expression()
936 start = Rational (0,4)
937 stop = Rational (4,2)
938 def sub(x, start=start, stop=stop):
939 ok = x.start >= start and x.start +x.get_length() <= stop
942 print expr.lisp_sub_expression(sub)