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):
99 self._skipspace = False
100 self.unformatted_output (str)
102 words = string.split (str)
108 self.duration_log = 0
110 self.factor = Rational (1)
112 def lisp_expression (self):
113 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
115 self.factor.numerator (),
116 self.factor.denominator ())
119 def ly_expression (self, factor = None):
123 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
125 if factor <> Rational (1,1):
126 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
130 def print_ly (self, outputter):
131 str = self.ly_expression (self.factor / outputter.duration_factor ())
132 outputter.print_duration_string (str)
135 return self.ly_expression()
140 d.duration_log = self.duration_log
141 d.factor = self.factor
144 def get_length (self):
145 dot_fact = Rational( (1 << (1 + self.dots))-1,
148 log = abs (self.duration_log)
150 if self.duration_log < 0:
151 base = Rational (dur)
153 base = Rational (1, dur)
155 return base * dot_fact * self.factor
165 return self.ly_expression()
167 def transposed (self, interval):
169 c.alteration += interval.alteration
170 c.step += interval.step
171 c.octave += interval.octave
174 target_st = self.semitones() + interval.semitones()
175 c.alteration += target_st - c.semitones()
182 c.octave += c.step / 7
186 def lisp_expression (self):
187 return '(ly:make-pitch %d %d %d)' % (self.octave,
193 p.alteration = self.alteration
195 p.octave = self.octave
199 return self.step + self.octave *7
201 def semitones (self):
202 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
204 def ly_step_expression (self):
205 str = 'cdefgab'[self.step]
206 if self.alteration > 0:
207 str += 'is'* (self.alteration)
208 elif self.alteration < 0:
209 str += 'es'* (-self.alteration)
211 return str.replace ('aes', 'as').replace ('ees', 'es')
213 def ly_expression (self):
214 str = self.ly_step_expression ()
216 str += "'" * (self.octave + 1)
217 elif self.octave < -1:
218 str += "," * (-self.octave - 1)
221 def print_ly (self, outputter):
222 outputter (self.ly_expression())
227 self.start = Rational (0)
229 self.identifier = None
231 def get_length(self):
234 def get_properties (self):
237 def has_children (self):
240 def get_index (self):
242 return self.parent.elements.index (self)
246 return self.__class__.__name__
248 def lisp_expression (self):
251 props = self.get_properties ()
252 # props += 'start %f ' % self.start
254 return "(make-music '%s %s)" % (name, props)
256 def set_start (self, start):
259 def find_first (self, predicate):
264 def print_comment (self, printer, text = None):
275 lines = string.split (text, '\n')
278 printer.dump ('% ' + l)
282 def print_with_identifier (self, printer):
284 printer ("\\%s" % self.identifier)
286 self.print_ly (printer)
288 def print_ly (self, printer):
289 printer (self.ly_expression ())
291 class MusicWrapper (Music):
295 def print_ly (self, func):
296 self.element.print_ly (func)
298 class TimeScaledMusic (MusicWrapper):
299 def print_ly (self, func):
300 func ('\\times %d/%d ' %
301 (self.numerator, self.denominator))
302 func.add_factor (Rational (self.numerator, self.denominator))
303 MusicWrapper.print_ly (self, func)
306 class NestedMusic(Music):
308 Music.__init__ (self)
311 def append (self, what):
313 self.elements.append (what)
315 def has_children (self):
318 def insert_around (self, succ, elt, dir):
319 assert elt.parent == None
320 assert succ == None or succ in self.elements
325 idx = self.elements.index (succ)
332 idx = len (self.elements)
334 self.elements.insert (idx, elt)
337 def get_properties (self):
338 return ("'elements (list %s)"
339 % string.join (map (lambda x: x.lisp_expression(),
342 def get_subset_properties (self, predicate):
343 return ("'elements (list %s)"
344 % string.join (map (lambda x: x.lisp_expression(),
345 filter ( predicate, self.elements))))
346 def get_neighbor (self, music, dir):
347 assert music.parent == self
348 idx = self.elements.index (music)
350 idx = min (idx, len (self.elements) -1)
353 return self.elements[idx]
355 def delete_element (self, element):
356 assert element in self.elements
358 self.elements.remove (element)
359 element.parent = None
361 def set_start (self, start):
363 for e in self.elements:
366 def find_first (self, predicate):
367 r = Music.find_first (self, predicate)
371 for e in self.elements:
372 r = e.find_first (predicate)
377 class SequentialMusic (NestedMusic):
378 def print_ly (self, printer):
381 self.print_comment (printer)
384 for e in self.elements:
390 def lisp_sub_expression (self, pred):
394 props = self.get_subset_properties (pred)
396 return "(make-music '%s %s)" % (name, props)
398 def set_start (self, start):
399 for e in self.elements:
401 start += e.get_length()
403 class EventChord(NestedMusic):
404 def get_length (self):
406 for e in self.elements:
407 l = max(l, e.get_length())
410 def print_ly (self, printer):
411 note_events = [e for e in self.elements if
412 isinstance (e, NoteEvent)]
414 rest_events = [e for e in self.elements if
415 isinstance (e, RhythmicEvent)
416 and not isinstance (e, NoteEvent)]
418 other_events = [e for e in self.elements if
419 not isinstance (e, RhythmicEvent)]
422 rest_events[0].print_ly (printer)
423 elif len (note_events) == 1:
424 note_events[0].print_ly (printer)
426 pitches = [x.pitch.ly_expression () for x in note_events]
427 printer ('<%s>' % string.join (pitches))
428 note_events[0].duration.print_ly (printer)
432 # print 'huh', rest_events, note_events, other_events
433 for e in other_events:
436 self.print_comment (printer)
441 class SpanEvent (Event):
443 Event.__init__ (self)
444 self.span_direction = 0
445 def get_properties(self):
446 return "'span-direction %d" % self.span_direction
448 class SlurEvent (SpanEvent):
449 def ly_expression (self):
452 1:')'}[self.span_direction]
454 class BeamEvent (SpanEvent):
455 def ly_expression (self):
458 1:']'}[self.span_direction]
460 class ArpeggioEvent(Event):
461 def ly_expression (self):
462 return ('\\arpeggio')
465 class TieEvent(Event):
466 def ly_expression (self):
470 class RhythmicEvent(Event):
472 Event.__init__ (self)
473 self.duration = Duration()
475 def get_length (self):
476 return self.duration.get_length()
478 def get_properties (self):
479 return ("'duration %s"
480 % self.duration.lisp_expression ())
482 class RestEvent (RhythmicEvent):
483 def ly_expression (self):
484 return 'r%s' % self.duration.ly_expression ()
486 def print_ly (self, printer):
488 self.duration.print_ly (printer)
490 class SkipEvent (RhythmicEvent):
491 def ly_expression (self):
492 return 's%s' % self.duration.ly_expression ()
494 class NoteEvent(RhythmicEvent):
496 RhythmicEvent.__init__ (self)
498 self.cautionary = False
499 self.forced_accidental = False
501 def get_properties (self):
502 return ("'pitch %s\n 'duration %s"
503 % (self.pitch.lisp_expression (),
504 self.duration.lisp_expression ()))
506 def pitch_mods (self):
510 if self.forced_accidental:
515 def ly_expression (self):
516 return '%s%s%s' % (self.pitch.ly_expression (),
518 self.duration.ly_expression ())
520 def print_ly (self, printer):
521 self.pitch.print_ly (printer)
522 printer (self.pitch_mods ())
523 self.duration.print_ly (printer)
525 class KeySignatureChange (Music):
527 Music.__init__ (self)
532 def ly_expression (self):
533 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
536 def lisp_expression (self):
537 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
538 scale_str = ("'(%s)" % string.join (pairs))
540 return """ (make-music 'KeyChangeEvent
541 'pitch-alist %s) """ % scale_str
543 class TimeSignatureChange (Music):
545 Music.__init__ (self)
546 self.fraction = (4,4)
547 def ly_expression (self):
548 return '\\time %d/%d ' % self.fraction
550 class ClefChange (Music):
552 Music.__init__ (self)
556 def ly_expression (self):
557 return '\\clef "%s"' % self.type
559 "G": ("clefs.G", -2, -6),
560 "C": ("clefs.C", 0, 0),
561 "F": ("clefs.F", 2, 6),
564 def lisp_expression (self):
565 (glyph, pos, c0) = self.clef_dict [self.type]
567 (make-music 'SequentialMusic
570 (make-property-set 'clefGlyph "%s") 'Staff)
572 (make-property-set 'clefPosition %d) 'Staff)
574 (make-property-set 'middleCPosition %d) 'Staff)))
575 """ % (glyph, pos, c0)
581 bflat.alteration = -1
591 print bflat.semitones()
592 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
593 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
595 print bflat.semitones(), 'down'
596 print bflat.transposed (down)
597 print bflat.transposed (down).transposed (down)
598 print bflat.transposed (down).transposed (down).transposed (down)
610 m = SequentialMusic()
611 m.append (make_note ())
612 m.append (make_note ())
613 m.append (make_note ())
616 t = TimeScaledMusic ()
622 m = SequentialMusic ()
623 m.append (make_tup ())
624 m.append (make_tup ())
625 m.append (make_tup ())
627 printer = Output_printer()
632 m = SequentialMusic()
636 n.duration.duration_log = l
638 evc.insert_around (None, n, 0)
639 m.insert_around (None, evc, 0)
643 n.duration.duration_log = l
645 evc.insert_around (None, n, 0)
646 m.insert_around (None, evc, 0)
650 n.duration.duration_log = l
652 evc.insert_around (None, n, 0)
653 m.insert_around (None, evc, 0)
657 m.insert_around (None, evc, 0)
662 tonic.alteration = -2
663 n = KeySignatureChange()
665 n.scale = [0, 0, -2, 0, 0,-2,-2]
667 evc.insert_around (None, n, 0)
668 m.insert_around (None, evc, 0)
673 if __name__ == '__main__':
679 expr.set_start (Rational (0))
680 print expr.ly_expression()
681 start = Rational (0,4)
682 stop = Rational (4,2)
683 def sub(x, start=start, stop=stop):
684 ok = x.start >= start and x.start +x.get_length() <= stop
687 print expr.lisp_sub_expression(sub)