6 from rational import Rational
8 class Output_stack_element:
10 self.factor = Rational (1)
12 o = Output_stack_element()
13 o.factor = self.factor
18 """A class that takes care of formatting (eg.: indenting) a
19 Music expression as a .ly file.
22 ## TODO: support for \relative.
28 self._file = sys.stdout
30 self._output_state_stack = [Output_stack_element()]
31 self._skipspace = False
32 self._last_duration = None
34 def set_file (self, file):
37 def dump_version (self):
39 self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
42 def get_indent (self):
43 return self._nesting * self._indent
46 last = self._output_state_stack[-1]
47 self._output_state_stack.append (last.copy())
49 def add_factor (self, factor):
51 self._output_state_stack[-1].factor *= factor
54 del self._output_state_stack[-1]
55 if not self._output_state_stack:
58 def duration_factor (self):
59 return self._output_state_stack[-1].factor
61 def print_verbatim (self, str):
64 def unformatted_output (self, str):
65 self._nesting += str.count ('<') + str.count ('{')
66 self._nesting -= str.count ('>') + str.count ('}')
67 self.print_verbatim (str)
69 def print_duration_string (self, str):
70 if self._last_duration == str:
73 self.unformatted_output (str)
75 def add_word (self, str):
76 if (len (str) + 1 + len (self._line) > self._line_len):
78 self._skipspace = True
80 if not self._skipspace:
82 self.unformatted_output (str)
83 self._skipspace = False
86 self._file.write (self._line + '\n')
87 self._line = ' ' * self._indent * self._nesting
88 self._skipspace = True
91 self._skipspace = True
93 def __call__(self, arg):
98 self._skipspace = False
99 self.unformatted_output (str)
101 words = string.split (str)
114 self.duration_log = 0
116 self.factor = Rational (1)
118 def lisp_expression (self):
119 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
121 self.factor.numerator (),
122 self.factor.denominator ())
125 def ly_expression (self, factor = None):
129 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
131 if factor <> Rational (1,1):
132 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
136 def print_ly (self, outputter):
137 str = self.ly_expression (self.factor / outputter.duration_factor ())
138 outputter.print_duration_string (str)
141 return self.ly_expression()
146 d.duration_log = self.duration_log
147 d.factor = self.factor
150 def get_length (self):
151 dot_fact = Rational( (1 << (1 + self.dots))-1,
154 log = abs (self.duration_log)
156 if self.duration_log < 0:
157 base = Rational (dur)
159 base = Rational (1, dur)
161 return base * dot_fact * self.factor
171 return self.ly_expression()
173 def transposed (self, interval):
175 c.alteration += interval.alteration
176 c.step += interval.step
177 c.octave += interval.octave
180 target_st = self.semitones() + interval.semitones()
181 c.alteration += target_st - c.semitones()
188 c.octave += c.step / 7
191 def lisp_expression (self):
192 return '(ly:make-pitch %d %d %d)' % (self.octave,
198 p.alteration = self.alteration
200 p.octave = self.octave
204 return self.step + self.octave *7
206 def semitones (self):
207 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
209 def ly_step_expression (self):
210 str = 'cdefgab'[self.step]
211 if self.alteration > 0:
212 str += 'is'* (self.alteration)
213 elif self.alteration < 0:
214 str += 'es'* (-self.alteration)
216 return str.replace ('aes', 'as').replace ('ees', 'es')
218 def ly_expression (self):
219 str = self.ly_step_expression ()
221 str += "'" * (self.octave + 1)
222 elif self.octave < -1:
223 str += "," * (-self.octave - 1)
227 def print_ly (self, outputter):
228 outputter (self.ly_expression())
233 self.start = Rational (0)
235 self.identifier = None
237 def get_length(self):
240 def get_properties (self):
243 def has_children (self):
246 def get_index (self):
248 return self.parent.elements.index (self)
252 return self.__class__.__name__
254 def lisp_expression (self):
257 props = self.get_properties ()
259 return "(make-music '%s %s)" % (name, props)
261 def set_start (self, start):
264 def find_first (self, predicate):
269 def print_comment (self, printer, text = None):
280 lines = string.split (text, '\n')
283 printer.unformatted_output ('% ' + l)
287 def print_with_identifier (self, printer):
289 printer ("\\%s" % self.identifier)
291 self.print_ly (printer)
293 def print_ly (self, printer):
294 printer (self.ly_expression ())
296 class MusicWrapper (Music):
300 def print_ly (self, func):
301 self.element.print_ly (func)
303 class ModeChangingMusicWrapper (MusicWrapper):
305 MusicWrapper.__init__ (self)
306 self.mode = 'notemode'
308 def print_ly (self, func):
309 func ('\\%s' % self.mode)
310 MusicWrapper.print_ly (self, func)
312 class TimeScaledMusic (MusicWrapper):
313 def print_ly (self, func):
314 func ('\\times %d/%d ' %
315 (self.numerator, self.denominator))
316 func.add_factor (Rational (self.numerator, self.denominator))
317 MusicWrapper.print_ly (self, func)
320 class NestedMusic(Music):
322 Music.__init__ (self)
325 def append (self, what):
327 self.elements.append (what)
329 def has_children (self):
332 def insert_around (self, succ, elt, dir):
333 assert elt.parent == None
334 assert succ == None or succ in self.elements
339 idx = self.elements.index (succ)
346 idx = len (self.elements)
348 self.elements.insert (idx, elt)
351 def get_properties (self):
352 return ("'elements (list %s)"
353 % string.join (map (lambda x: x.lisp_expression(),
356 def get_subset_properties (self, predicate):
357 return ("'elements (list %s)"
358 % string.join (map (lambda x: x.lisp_expression(),
359 filter ( predicate, self.elements))))
360 def get_neighbor (self, music, dir):
361 assert music.parent == self
362 idx = self.elements.index (music)
364 idx = min (idx, len (self.elements) -1)
367 return self.elements[idx]
369 def delete_element (self, element):
370 assert element in self.elements
372 self.elements.remove (element)
373 element.parent = None
375 def set_start (self, start):
377 for e in self.elements:
380 def find_first (self, predicate):
381 r = Music.find_first (self, predicate)
385 for e in self.elements:
386 r = e.find_first (predicate)
391 class SequentialMusic (NestedMusic):
392 def print_ly (self, printer):
395 self.print_comment (printer)
398 for e in self.elements:
404 def lisp_sub_expression (self, pred):
408 props = self.get_subset_properties (pred)
410 return "(make-music '%s %s)" % (name, props)
412 def set_start (self, start):
413 for e in self.elements:
415 start += e.get_length()
419 self.lyrics_syllables = []
421 def print_ly (self, printer):
422 printer.dump ("\lyricmode {")
423 for l in self.lyrics_syllables:
424 printer.dump ( "%s " % l )
427 def ly_expression (self):
428 lstr = "\lyricmode {\n "
429 for l in self.lyrics_syllables:
435 class EventChord(NestedMusic):
436 def get_length (self):
438 for e in self.elements:
439 l = max(l, e.get_length())
442 def print_ly (self, printer):
443 note_events = [e for e in self.elements if
444 isinstance (e, NoteEvent)]
446 rest_events = [e for e in self.elements if
447 isinstance (e, RhythmicEvent)
448 and not isinstance (e, NoteEvent)]
450 other_events = [e for e in self.elements if
451 not isinstance (e, RhythmicEvent)]
454 rest_events[0].print_ly (printer)
455 elif len (note_events) == 1:
456 note_events[0].print_ly (printer)
458 pitches = [x.pitch.ly_expression () for x in note_events]
459 printer ('<%s>' % string.join (pitches))
460 note_events[0].duration.print_ly (printer)
464 for e in other_events:
467 self.print_comment (printer)
470 class BarCheck (Music):
472 Music.__init__ (self)
475 def print_ly (self, printer):
476 if self.bar_number > 0 and (self.bar_number % 10) == 0:
477 printer.dump ("| \\barNumberCheck #%d " % self.bar_number)
481 printer.print_verbatim (' %% %d' % self.bar_number)
485 def ly_expression (self):
491 class SpanEvent (Event):
493 Event.__init__ (self)
494 self.span_direction = 0
497 def wait_for_note (self):
499 def get_properties(self):
500 return "'span-direction %d" % self.span_direction
502 class SlurEvent (SpanEvent):
503 def ly_expression (self):
506 # TODO: setting dashed/dotted line style does not work, because that
507 # command needs to be written before the note, not when the
508 # event is observed after the note!
509 #if self.line_type == 1:
510 #before = '\\slurDotted'
511 #elif self.line_type == 2:
512 #before = '\\slurDashed'
514 #after = '\\slurSolid'
516 return {-1: before + '(' + after,
518 1:')'}.get (self.span_direction, '')
520 class BeamEvent (SpanEvent):
521 def ly_expression (self):
524 1:']'}.get (self.span_direction, '')
526 class PedalEvent (SpanEvent):
527 def ly_expression (self):
528 return {-1: '\\sustainDown',
530 1:'\\sustainUp'}.get (self.span_direction, '')
532 # type==-1 means octave up, type==-2 means octave down
533 class OctaveShiftEvent (SpanEvent):
534 def wait_for_note (self):
536 def ly_octave_shift_indicator (self):
539 elif self.size == 15:
544 if self.span_direction == -2:
547 def ly_expression (self):
548 dir = self.ly_octave_shift_indicator ()
551 value = '#(set-octavation %s)' % dir
555 1: '#(set-octavation 0)'}.get (self.span_direction, '')
557 class TrillSpanEvent (SpanEvent):
558 def ly_expression (self):
559 return {-1: '\\startTrillSpan',
561 1:'\\stopTrillSpan'}.get (self.span_direction, '')
563 class GlissandoEvent (SpanEvent):
564 def ly_expression (self):
566 # TODO: wavy-line glissandos don't work, becasue the style has to be
567 # set before the note, at the \glissando it's already too late!
568 #if self.line_type == 3: # wavy-line:
569 #style = "\once\override Glissando #'style = #'zigzag"
570 # In lilypond, glissando is NOT a spanner, unlike MusicXML.
571 return {-1: style + '\\glissando',
573 1:''}.get (self.span_direction, '')
575 class ArpeggioEvent(Event):
576 def wait_for_note (self):
578 def ly_expression (self):
579 return ('\\arpeggio')
582 class TieEvent(Event):
583 def ly_expression (self):
587 class HairpinEvent (SpanEvent):
588 def __init__ (self, type):
590 def hairpin_to_ly (self):
591 return { 0: '\!', 1: '\<', -1: '\>' }.get (self.type, '')
593 def ly_expression (self):
594 return self.hairpin_to_ly ()
596 def print_ly (self, printer):
597 val = self.hairpin_to_ly ()
603 class DynamicsEvent (Event):
606 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
608 "f", "ff", "fff", "ffff",
609 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
610 def wait_for_note (self):
612 def ly_expression (self):
613 if self.type == None:
615 elif self.type in self.available_commands:
616 return '\%s' % self.type
618 return '\markup{ \dynamic %s }' % self.type
620 def print_ly (self, printer):
621 if self.type == None:
623 elif self.type in self.available_commands:
624 printer.dump ("\\%s" % self.type)
626 printer.dump ("\\markup{ \\dynamic %s }" % self.type)
629 class ArticulationEvent (Event):
632 self.force_direction = None
634 def direction_mod (self):
635 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
637 def ly_expression (self):
638 return '%s\\%s' % (self.direction_mod (), self.type)
640 class ShortArticulationEvent (ArticulationEvent):
641 def direction_mod (self):
643 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
644 def ly_expression (self):
645 return '%s%s' % (self.direction_mod (), self.type)
647 class TremoloEvent (Event):
649 Event.__init__ (self)
652 def ly_expression (self):
655 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
658 class BendEvent (Event):
660 Event.__init__ (self)
662 def ly_expression (self):
664 return "-\\bendAfter #%s" % self.alter
668 class RhythmicEvent(Event):
670 Event.__init__ (self)
671 self.duration = Duration()
673 def get_length (self):
674 return self.duration.get_length()
676 def get_properties (self):
677 return ("'duration %s"
678 % self.duration.lisp_expression ())
680 class RestEvent (RhythmicEvent):
681 def ly_expression (self):
682 return 'r%s' % self.duration.ly_expression ()
684 def print_ly (self, printer):
686 self.duration.print_ly (printer)
688 class SkipEvent (RhythmicEvent):
689 def ly_expression (self):
690 return 's%s' % self.duration.ly_expression ()
692 class NoteEvent(RhythmicEvent):
694 RhythmicEvent.__init__ (self)
696 self.drum_type = None
697 self.cautionary = False
698 self.forced_accidental = False
700 def get_properties (self):
701 str = RhythmicEvent.get_properties ()
704 str += self.pitch.lisp_expression ()
706 str += "'drum-type '%s" % self.drum_type
710 def pitch_mods (self):
714 if self.forced_accidental:
719 def ly_expression (self):
721 return '%s%s%s' % (self.pitch.ly_expression (),
723 self.duration.ly_expression ())
725 return '%s%s' (self.drum_type,
726 self.duration.ly_expression ())
728 def print_ly (self, printer):
730 self.pitch.print_ly (printer)
731 printer (self.pitch_mods ())
733 printer (self.drum_type)
735 self.duration.print_ly (printer)
737 class KeySignatureChange (Music):
739 Music.__init__ (self)
744 def ly_expression (self):
745 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
748 def lisp_expression (self):
749 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
750 scale_str = ("'(%s)" % string.join (pairs))
752 return """ (make-music 'KeyChangeEvent
753 'pitch-alist %s) """ % scale_str
755 class TimeSignatureChange (Music):
757 Music.__init__ (self)
758 self.fraction = (4,4)
759 def ly_expression (self):
760 return '\\time %d/%d ' % self.fraction
762 class ClefChange (Music):
764 Music.__init__ (self)
768 def ly_expression (self):
769 return '\\clef "%s"' % self.type
771 "G": ("clefs.G", -2, -6),
772 "C": ("clefs.C", 0, 0),
773 "F": ("clefs.F", 2, 6),
776 def lisp_expression (self):
777 (glyph, pos, c0) = self.clef_dict.get (self.type)
779 (make-music 'SequentialMusic
782 (make-property-set 'clefGlyph "%s") 'Staff)
784 (make-property-set 'clefPosition %d) 'Staff)
786 (make-property-set 'middleCPosition %d) 'Staff)))
787 """ % (glyph, pos, c0)
791 class MultiMeasureRest(Music):
793 def lisp_expression (self):
796 'MultiMeasureRestMusicGroup
798 (list (make-music (quote BarCheck))
803 'MultiMeasureRestEvent
806 (make-music (quote BarCheck))))
807 """ % self.duration.lisp_expression ()
809 def ly_expression (self):
810 return 'R%s' % self.duration.ly_expression ()
815 bflat.alteration = -1
825 print bflat.semitones()
826 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
827 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
829 print bflat.semitones(), 'down'
830 print bflat.transposed (down)
831 print bflat.transposed (down).transposed (down)
832 print bflat.transposed (down).transposed (down).transposed (down)
844 m = SequentialMusic()
845 m.append (make_note ())
846 m.append (make_note ())
847 m.append (make_note ())
850 t = TimeScaledMusic ()
856 m = SequentialMusic ()
857 m.append (make_tup ())
858 m.append (make_tup ())
859 m.append (make_tup ())
861 printer = Output_printer()
866 m = SequentialMusic()
870 n.duration.duration_log = l
872 evc.insert_around (None, n, 0)
873 m.insert_around (None, evc, 0)
877 n.duration.duration_log = l
879 evc.insert_around (None, n, 0)
880 m.insert_around (None, evc, 0)
884 n.duration.duration_log = l
886 evc.insert_around (None, n, 0)
887 m.insert_around (None, evc, 0)
891 m.insert_around (None, evc, 0)
896 tonic.alteration = -2
897 n = KeySignatureChange()
899 n.scale = [0, 0, -2, 0, 0,-2,-2]
901 evc.insert_around (None, n, 0)
902 m.insert_around (None, evc, 0)
907 if __name__ == '__main__':
913 expr.set_start (Rational (0))
914 print expr.ly_expression()
915 start = Rational (0,4)
916 stop = Rational (4,2)
917 def sub(x, start=start, stop=stop):
918 ok = x.start >= start and x.start +x.get_length() <= stop
921 print expr.lisp_sub_expression(sub)