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:')'}[self
.span_direction
]
486 class BeamEvent (SpanEvent
):
487 def ly_expression (self
):
490 1:']'}[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 RhythmicEvent(Event
):
504 Event
.__init
__ (self
)
505 self
.duration
= Duration()
507 def get_length (self
):
508 return self
.duration
.get_length()
510 def get_properties (self
):
511 return ("'duration %s"
512 % self
.duration
.lisp_expression ())
514 class RestEvent (RhythmicEvent
):
515 def ly_expression (self
):
516 return 'r%s' % self
.duration
.ly_expression ()
518 def print_ly (self
, printer
):
520 self
.duration
.print_ly (printer
)
522 class SkipEvent (RhythmicEvent
):
523 def ly_expression (self
):
524 return 's%s' % self
.duration
.ly_expression ()
526 class NoteEvent(RhythmicEvent
):
528 RhythmicEvent
.__init
__ (self
)
530 self
.drum_type
= None
531 self
.cautionary
= False
532 self
.forced_accidental
= False
534 def get_properties (self
):
535 str = RhythmicEvent
.get_properties ()
538 str += self
.pitch
.lisp_expression ()
540 str += "'drum-type '%s" % self
.drum_type
544 def pitch_mods (self
):
548 if self
.forced_accidental
:
553 def ly_expression (self
):
555 return '%s%s%s' % (self
.pitch
.ly_expression (),
557 self
.duration
.ly_expression ())
559 return '%s%s' (self
.drum_type
,
560 self
.duration
.ly_expression ())
562 def print_ly (self
, printer
):
564 self
.pitch
.print_ly (printer
)
565 printer (self
.pitch_mods ())
567 printer (self
.drum_type
)
569 self
.duration
.print_ly (printer
)
571 class KeySignatureChange (Music
):
573 Music
.__init
__ (self
)
578 def ly_expression (self
):
579 return '\\key %s \\%s' % (self
.tonic
.ly_step_expression (),
582 def lisp_expression (self
):
583 pairs
= ['(%d . %d)' % (i
, self
.scale
[i
]) for i
in range (0,7)]
584 scale_str
= ("'(%s)" % string
.join (pairs
))
586 return """ (make-music 'KeyChangeEvent
587 'pitch-alist %s) """ % scale_str
589 class TimeSignatureChange (Music
):
591 Music
.__init
__ (self
)
592 self
.fraction
= (4,4)
593 def ly_expression (self
):
594 return '\\time %d/%d ' % self
.fraction
596 class ClefChange (Music
):
598 Music
.__init
__ (self
)
602 def ly_expression (self
):
603 return '\\clef "%s"' % self
.type
605 "G": ("clefs.G", -2, -6),
606 "C": ("clefs.C", 0, 0),
607 "F": ("clefs.F", 2, 6),
610 def lisp_expression (self
):
611 (glyph
, pos
, c0
) = self
.clef_dict
[self
.type]
613 (make-music 'SequentialMusic
616 (make-property-set 'clefGlyph "%s") 'Staff)
618 (make-property-set 'clefPosition %d) 'Staff)
620 (make-property-set 'middleCPosition %d) 'Staff)))
621 """ % (glyph
, pos
, c0
)
625 class MultiMeasureRest(Music
):
627 def lisp_expression (self
):
630 'MultiMeasureRestMusicGroup
632 (list (make-music (quote BarCheck))
637 'MultiMeasureRestEvent
640 (make-music (quote BarCheck))))
641 """ % self
.duration
.lisp_expression ()
643 def ly_expression (self
):
644 return 'R%s' % self
.duration
.ly_expression ()
649 bflat
.alteration
= -1
659 print bflat
.semitones()
660 print bflat
.transposed (fifth
), bflat
.transposed (fifth
).transposed (fifth
)
661 print bflat
.transposed (fifth
).transposed (fifth
).transposed (fifth
)
663 print bflat
.semitones(), 'down'
664 print bflat
.transposed (down
)
665 print bflat
.transposed (down
).transposed (down
)
666 print bflat
.transposed (down
).transposed (down
).transposed (down
)
678 m
= SequentialMusic()
679 m
.append (make_note ())
680 m
.append (make_note ())
681 m
.append (make_note ())
684 t
= TimeScaledMusic ()
690 m
= SequentialMusic ()
691 m
.append (make_tup ())
692 m
.append (make_tup ())
693 m
.append (make_tup ())
695 printer
= Output_printer()
700 m
= SequentialMusic()
704 n
.duration
.duration_log
= l
706 evc
.insert_around (None, n
, 0)
707 m
.insert_around (None, evc
, 0)
711 n
.duration
.duration_log
= l
713 evc
.insert_around (None, n
, 0)
714 m
.insert_around (None, evc
, 0)
718 n
.duration
.duration_log
= l
720 evc
.insert_around (None, n
, 0)
721 m
.insert_around (None, evc
, 0)
725 m
.insert_around (None, evc
, 0)
730 tonic
.alteration
= -2
731 n
= KeySignatureChange()
733 n
.scale
= [0, 0, -2, 0, 0,-2,-2]
735 evc
.insert_around (None, n
, 0)
736 m
.insert_around (None, evc
, 0)
741 if __name__
== '__main__':
747 expr
.set_start (Rational (0))
748 print expr
.ly_expression()
749 start
= Rational (0,4)
750 stop
= Rational (4,2)
751 def sub(x
, start
=start
, stop
=stop
):
752 ok
= x
.start
>= start
and x
.start
+x
.get_length() <= stop
755 print expr
.lisp_sub_expression(sub
)