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()
417 class EventChord(NestedMusic):
418 def get_length (self):
420 for e in self.elements:
421 l = max(l, e.get_length())
424 def print_ly (self, printer):
425 note_events = [e for e in self.elements if
426 isinstance (e, NoteEvent)]
428 rest_events = [e for e in self.elements if
429 isinstance (e, RhythmicEvent)
430 and not isinstance (e, NoteEvent)]
432 other_events = [e for e in self.elements if
433 not isinstance (e, RhythmicEvent)]
436 rest_events[0].print_ly (printer)
437 elif len (note_events) == 1:
438 note_events[0].print_ly (printer)
440 pitches = [x.pitch.ly_expression () for x in note_events]
441 printer ('<%s>' % string.join (pitches))
442 note_events[0].duration.print_ly (printer)
446 for e in other_events:
449 self.print_comment (printer)
452 class BarCheck (Music):
454 Music.__init__ (self)
457 def print_ly (self, printer):
458 if self.bar_number > 0 and (self.bar_number % 10) == 0:
459 printer.dump ("| \\barNumberCheck #%d " % self.bar_number)
463 printer.print_verbatim (' %% %d' % self.bar_number)
467 def ly_expression (self):
473 class SpanEvent (Event):
475 Event.__init__ (self)
476 self.span_direction = 0
477 def get_properties(self):
478 return "'span-direction %d" % self.span_direction
480 class SlurEvent (SpanEvent):
481 def ly_expression (self):
484 1:')'}.get (self.span_direction, '')
486 class BeamEvent (SpanEvent):
487 def ly_expression (self):
490 1:']'}.get (self.span_direction, '')
492 class ArpeggioEvent(Event):
493 def ly_expression (self):
494 return ('\\arpeggio')
497 class TieEvent(Event):
498 def ly_expression (self):
502 class HairpinEvent (Event):
503 def __init__ (self, type):
505 def hairpin_to_ly (self):
506 return { 0: '\!', 1: '\<', -1: '\>' }.get (self.type, '')
508 def ly_expression (self):
509 return self.hairpin_to_ly ()
511 def print_ly (self, printer):
512 val = self.hairpin_to_ly ()
518 class DynamicsEvent (Event):
521 self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
523 "f", "ff", "fff", "ffff",
524 "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
525 def ly_expression (self):
526 if self.type == None:
528 elif self.type in self.available_commands:
529 return '\%s' % self.type
531 return '\markup{ \dynamic %s }' % self.type
533 def print_ly (self, printer):
534 if self.type == None:
536 elif self.type in self.available_commands:
537 printer.dump ("\\%s" % self.type)
539 printer.dump ("\\markup{ \\dynamic %s }" % self.type)
542 class ArticulationEvent (Event):
545 self.force_direction = None
547 def direction_mod (self):
548 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
550 def ly_expression (self):
551 return '%s\\%s' % (self.direction_mod (), self.type)
554 class TremoloEvent (Event):
558 def ly_expression (self):
561 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
565 class RhythmicEvent(Event):
567 Event.__init__ (self)
568 self.duration = Duration()
570 def get_length (self):
571 return self.duration.get_length()
573 def get_properties (self):
574 return ("'duration %s"
575 % self.duration.lisp_expression ())
577 class RestEvent (RhythmicEvent):
578 def ly_expression (self):
579 return 'r%s' % self.duration.ly_expression ()
581 def print_ly (self, printer):
583 self.duration.print_ly (printer)
585 class SkipEvent (RhythmicEvent):
586 def ly_expression (self):
587 return 's%s' % self.duration.ly_expression ()
589 class NoteEvent(RhythmicEvent):
591 RhythmicEvent.__init__ (self)
593 self.drum_type = None
594 self.cautionary = False
595 self.forced_accidental = False
597 def get_properties (self):
598 str = RhythmicEvent.get_properties ()
601 str += self.pitch.lisp_expression ()
603 str += "'drum-type '%s" % self.drum_type
607 def pitch_mods (self):
611 if self.forced_accidental:
616 def ly_expression (self):
618 return '%s%s%s' % (self.pitch.ly_expression (),
620 self.duration.ly_expression ())
622 return '%s%s' (self.drum_type,
623 self.duration.ly_expression ())
625 def print_ly (self, printer):
627 self.pitch.print_ly (printer)
628 printer (self.pitch_mods ())
630 printer (self.drum_type)
632 self.duration.print_ly (printer)
634 class KeySignatureChange (Music):
636 Music.__init__ (self)
641 def ly_expression (self):
642 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
645 def lisp_expression (self):
646 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
647 scale_str = ("'(%s)" % string.join (pairs))
649 return """ (make-music 'KeyChangeEvent
650 'pitch-alist %s) """ % scale_str
652 class TimeSignatureChange (Music):
654 Music.__init__ (self)
655 self.fraction = (4,4)
656 def ly_expression (self):
657 return '\\time %d/%d ' % self.fraction
659 class ClefChange (Music):
661 Music.__init__ (self)
665 def ly_expression (self):
666 return '\\clef "%s"' % self.type
668 "G": ("clefs.G", -2, -6),
669 "C": ("clefs.C", 0, 0),
670 "F": ("clefs.F", 2, 6),
673 def lisp_expression (self):
674 (glyph, pos, c0) = self.clef_dict.get (self.type)
676 (make-music 'SequentialMusic
679 (make-property-set 'clefGlyph "%s") 'Staff)
681 (make-property-set 'clefPosition %d) 'Staff)
683 (make-property-set 'middleCPosition %d) 'Staff)))
684 """ % (glyph, pos, c0)
688 class MultiMeasureRest(Music):
690 def lisp_expression (self):
693 'MultiMeasureRestMusicGroup
695 (list (make-music (quote BarCheck))
700 'MultiMeasureRestEvent
703 (make-music (quote BarCheck))))
704 """ % self.duration.lisp_expression ()
706 def ly_expression (self):
707 return 'R%s' % self.duration.ly_expression ()
712 bflat.alteration = -1
722 print bflat.semitones()
723 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
724 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
726 print bflat.semitones(), 'down'
727 print bflat.transposed (down)
728 print bflat.transposed (down).transposed (down)
729 print bflat.transposed (down).transposed (down).transposed (down)
741 m = SequentialMusic()
742 m.append (make_note ())
743 m.append (make_note ())
744 m.append (make_note ())
747 t = TimeScaledMusic ()
753 m = SequentialMusic ()
754 m.append (make_tup ())
755 m.append (make_tup ())
756 m.append (make_tup ())
758 printer = Output_printer()
763 m = SequentialMusic()
767 n.duration.duration_log = l
769 evc.insert_around (None, n, 0)
770 m.insert_around (None, evc, 0)
774 n.duration.duration_log = l
776 evc.insert_around (None, n, 0)
777 m.insert_around (None, evc, 0)
781 n.duration.duration_log = l
783 evc.insert_around (None, n, 0)
784 m.insert_around (None, evc, 0)
788 m.insert_around (None, evc, 0)
793 tonic.alteration = -2
794 n = KeySignatureChange()
796 n.scale = [0, 0, -2, 0, 0,-2,-2]
798 evc.insert_around (None, n, 0)
799 m.insert_around (None, evc, 0)
804 if __name__ == '__main__':
810 expr.set_start (Rational (0))
811 print expr.ly_expression()
812 start = Rational (0,4)
813 stop = Rational (4,2)
814 def sub(x, start=start, stop=stop):
815 ok = x.start >= start and x.start +x.get_length() <= stop
818 print expr.lisp_sub_expression(sub)