]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
Merge branch 'master' of ssh://jomand@git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / python / musicexp.py
1 import inspect
2 import sys
3 import string
4 import re
5
6 from rational import Rational
7
8
9 def escape_instrument_string (input_string):
10     retstring = string.replace (input_string, "\"", "\\\"")
11     if re.match ('.*\n.*', retstring):
12         strings = retstring.split ('\r\n')
13         retstring = "\\markup { \\column { "
14         for s in strings:
15             retstring += "\\line {\"" + s + "\"} "
16         retstring += "} }"
17     else:
18         retstring = "\"" + retstring + "\""
19     return retstring
20
21 class Output_stack_element:
22     def __init__ (self):
23         self.factor = Rational (1)
24     def copy (self):
25         o = Output_stack_element()
26         o.factor = self.factor
27         return o
28
29 class Output_printer:
30
31     """A class that takes care of formatting (eg.: indenting) a
32     Music expression as a .ly file.
33     
34     """
35     ## TODO: support for \relative.
36     
37     def __init__ (self):
38         self._line = ''
39         self._indent = 4
40         self._nesting = 0
41         self._file = sys.stdout
42         self._line_len = 72
43         self._output_state_stack = [Output_stack_element()]
44         self._skipspace = False
45         self._last_duration = None
46
47     def set_file (self, file):
48         self._file = file
49         
50     def dump_version (self):
51         self.newline ()
52         self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
53         self.newline ()
54         
55     def get_indent (self):
56         return self._nesting * self._indent
57     
58     def override (self):
59         last = self._output_state_stack[-1]
60         self._output_state_stack.append (last.copy())
61         
62     def add_factor (self, factor):
63         self.override()
64         self._output_state_stack[-1].factor *=  factor
65
66     def revert (self):
67         del self._output_state_stack[-1]
68         if not self._output_state_stack:
69             raise 'empty'
70
71     def duration_factor (self):
72         return self._output_state_stack[-1].factor
73
74     def print_verbatim (self, str):
75         self._line += str
76
77     def unformatted_output (self, str):
78         # don't indent on \< and indent only once on <<
79         self._nesting += ( str.count ('<') 
80                          - str.count ('\<') - str.count ('<<') 
81                          + str.count ('{') )
82         self._nesting -= ( str.count ('>') - str.count ('\>') - str.count ('>>')
83                                            - str.count ('->') - str.count ('_>')
84                                            - str.count ('^>')
85                          + str.count ('}') )
86         self.print_verbatim (str)
87         
88     def print_duration_string (self, str):
89         if self._last_duration == str:
90             return
91         
92         self.unformatted_output (str)
93                   
94     def add_word (self, str):
95         if (len (str) + 1 + len (self._line) > self._line_len):
96             self.newline()
97             self._skipspace = True
98
99         if not self._skipspace:
100             self._line += ' '
101         self.unformatted_output (str)
102         self._skipspace = False
103         
104     def newline (self):
105         self._file.write (self._line + '\n')
106         self._line = ' ' * self._indent * self._nesting
107         self._skipspace = True
108
109     def skipspace (self):
110         self._skipspace = True
111         
112     def __call__(self, arg):
113         self.dump (arg)
114     
115     def dump (self, str):
116         if self._skipspace:
117             self._skipspace = False
118             self.unformatted_output (str)
119         else:
120             words = string.split (str)
121             for w in words:
122                 self.add_word (w)
123
124
125     def close (self):
126         self.newline ()
127         self._file.close ()
128         self._file = None
129         
130         
131 class Duration:
132     def __init__ (self):
133         self.duration_log = 0
134         self.dots = 0
135         self.factor = Rational (1)
136         
137     def lisp_expression (self):
138         return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
139                              self.dots,
140                              self.factor.numerator (),
141                              self.factor.denominator ())
142
143
144     def ly_expression (self, factor = None):
145         if not factor:
146             factor = self.factor
147             
148         str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
149
150         if factor <> Rational (1,1):
151             if factor.denominator () <> 1:
152                 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
153             else:
154                 str += '*%d' % factor.numerator ()
155
156         return str
157     
158     def print_ly (self, outputter):
159         str = self.ly_expression (self.factor / outputter.duration_factor ())
160         outputter.print_duration_string (str)
161         
162     def __repr__(self):
163         return self.ly_expression()
164         
165     def copy (self):
166         d = Duration ()
167         d.dots = self.dots
168         d.duration_log = self.duration_log
169         d.factor = self.factor
170         return d
171
172     def get_length (self):
173         dot_fact = Rational( (1 << (1 + self.dots))-1,
174                              1 << self.dots)
175
176         log = abs (self.duration_log)
177         dur = 1 << log
178         if self.duration_log < 0:
179             base = Rational (dur)
180         else:
181             base = Rational (1, dur)
182
183         return base * dot_fact * self.factor
184
185     
186 class Pitch:
187     def __init__ (self):
188         self.alteration = 0
189         self.step = 0
190         self.octave = 0
191         
192     def __repr__(self):
193         return self.ly_expression()
194
195     def transposed (self, interval):
196         c = self.copy ()
197         c.alteration  += interval.alteration
198         c.step += interval.step
199         c.octave += interval.octave
200         c.normalize ()
201         
202         target_st = self.semitones()  + interval.semitones()
203         c.alteration += target_st - c.semitones()
204         return c
205
206     def normalize (c):
207         while c.step < 0:
208             c.step += 7
209             c.octave -= 1
210         c.octave += c.step / 7
211         c.step = c.step  % 7
212
213     def lisp_expression (self):
214         return '(ly:make-pitch %d %d %d)' % (self.octave,
215                                              self.step,
216                                              self.alteration)
217
218     def copy (self):
219         p = Pitch ()
220         p.alteration = self.alteration
221         p.step = self.step
222         p.octave = self.octave 
223         return p
224
225     def steps (self):
226         return self.step + self.octave *7
227
228     def semitones (self):
229         return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
230     
231     def ly_step_expression (self): 
232         str = 'cdefgab'[self.step]
233         if self.alteration > 0:
234             str += 'is'* (self.alteration)
235         elif self.alteration < 0:
236             str += 'es'* (-self.alteration)
237
238         return str.replace ('aes', 'as').replace ('ees', 'es')
239     
240     def ly_expression (self):
241         str = self.ly_step_expression ()
242         if self.octave >= 0:
243             str += "'" * (self.octave + 1) 
244         elif self.octave < -1:
245             str += "," * (-self.octave - 1) 
246             
247         return str
248     
249     def print_ly (self, outputter):
250         outputter (self.ly_expression())
251     
252 class Music:
253     def __init__ (self):
254         self.parent = None
255         self.start = Rational (0)
256         self.comment = ''
257         self.identifier = None
258         
259     def get_length(self):
260         return Rational (0)
261     
262     def get_properties (self):
263         return ''
264     
265     def has_children (self):
266         return False
267     
268     def get_index (self):
269         if self.parent:
270             return self.parent.elements.index (self)
271         else:
272             return None
273     def name (self):
274         return self.__class__.__name__
275     
276     def lisp_expression (self):
277         name = self.name()
278
279         props = self.get_properties ()
280         
281         return "(make-music '%s %s)" % (name,  props)
282
283     def set_start (self, start):
284         self.start = start
285
286     def find_first (self, predicate):
287         if predicate (self):
288             return self
289         return None
290
291     def print_comment (self, printer, text = None):
292         if not text:
293             text = self.comment
294
295         if not text:
296             return
297             
298         if text == '\n':
299             printer.newline ()
300             return
301         
302         lines = string.split (text, '\n')
303         for l in lines:
304             if l:
305                 printer.unformatted_output ('% ' + l)
306             printer.newline ()
307             
308
309     def print_with_identifier (self, printer):
310         if self.identifier: 
311             printer ("\\%s" % self.identifier)
312         else:
313             self.print_ly (printer)
314
315     def print_ly (self, printer):
316         printer (self.ly_expression ())
317
318 class MusicWrapper (Music):
319     def __init__ (self):
320         Music.__init__(self)
321         self.element = None
322     def print_ly (self, func):
323         self.element.print_ly (func)
324
325 class ModeChangingMusicWrapper (MusicWrapper):
326     def __init__ (self):
327         MusicWrapper.__init__ (self)
328         self.mode = 'notemode'
329
330     def print_ly (self, func):
331         func ('\\%s' % self.mode)
332         MusicWrapper.print_ly (self, func)
333
334 class TimeScaledMusic (MusicWrapper):
335     def print_ly (self, func):
336         func ('\\times %d/%d ' %
337            (self.numerator, self.denominator))
338         func.add_factor (Rational (self.numerator, self.denominator))
339         MusicWrapper.print_ly (self, func)
340         func.revert ()
341
342 class NestedMusic(Music):
343     def __init__ (self):
344         Music.__init__ (self)
345         self.elements = []
346
347     def append (self, what):
348         if what:
349             self.elements.append (what)
350             
351     def has_children (self):
352         return self.elements
353
354     def insert_around (self, succ, elt, dir):
355         assert elt.parent == None
356         assert succ == None or succ in self.elements
357
358         
359         idx = 0
360         if succ:
361             idx = self.elements.index (succ)
362             if dir > 0:
363                 idx += 1
364         else:
365             if dir < 0:
366                 idx = 0
367             elif dir > 0:
368                 idx = len (self.elements)
369
370         self.elements.insert (idx, elt)
371         elt.parent = self
372         
373     def get_properties (self):
374         return ("'elements (list %s)"
375             % string.join (map (lambda x: x.lisp_expression(),
376                       self.elements)))
377
378     def get_subset_properties (self, predicate):
379         return ("'elements (list %s)"
380             % string.join (map (lambda x: x.lisp_expression(),
381                       filter ( predicate,  self.elements))))
382     def get_neighbor (self, music, dir):
383         assert music.parent == self
384         idx = self.elements.index (music)
385         idx += dir
386         idx = min (idx, len (self.elements) -1)
387         idx = max (idx, 0)
388
389         return self.elements[idx]
390
391     def delete_element (self, element):
392         assert element in self.elements
393         
394         self.elements.remove (element)
395         element.parent = None
396         
397     def set_start (self, start):
398         self.start = start
399         for e in self.elements:
400             e.set_start (start)
401
402     def find_first (self, predicate):
403         r = Music.find_first (self, predicate)
404         if r:
405             return r
406         
407         for e in self.elements:
408             r = e.find_first (predicate)
409             if r:
410                 return r
411         return None
412         
413 class SequentialMusic (NestedMusic):
414     def get_last_event_chord (self):
415         value = None
416         at = len( self.elements ) - 1
417         while (at >= 0 and
418                not isinstance (self.elements[at], EventChord) and
419                not isinstance (self.elements[at], BarLine)):
420             at -= 1
421
422         if (at >= 0 and isinstance (self.elements[at], EventChord)):
423             value = self.elements[at]
424         return value
425
426     def print_ly (self, printer, newline = True):
427         printer ('{')
428         if self.comment:
429             self.print_comment (printer)
430
431         if newline:
432             printer.newline()
433         for e in self.elements:
434             e.print_ly (printer)
435
436         printer ('}')
437         if newline:
438             printer.newline()
439             
440     def lisp_sub_expression (self, pred):
441         name = self.name()
442
443
444         props = self.get_subset_properties (pred)
445         
446         return "(make-music '%s %s)" % (name,  props)
447     
448     def set_start (self, start):
449         for e in self.elements:
450             e.set_start (start)
451             start += e.get_length()
452
453 class RepeatedMusic:
454     def __init__ (self):
455         self.repeat_type = "volta"
456         self.repeat_count = 2
457         self.endings = []
458         self.music = None
459     def set_music (self, music):
460         if isinstance (music, Music):
461             self.music = music
462         elif isinstance (music, list):
463             self.music = SequentialMusic ()
464             self.music.elements = music
465         else:
466             sys.stderr.write ("WARNING: Unable to set the music %s for the repeat %s" % (music, self))
467     def add_ending (self, music):
468         self.endings.append (music)
469     def print_ly (self, printer):
470         printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count))
471         if self.music:
472             self.music.print_ly (printer)
473         else:
474             sys.stderr.write ("WARNING: Encountered repeat without body\n")
475             printer.dump ('{}')
476         if self.endings:
477             printer.dump ('\\alternative {')
478             for e in self.endings:
479                 e.print_ly (printer)
480             printer.dump ('}')
481
482
483 class Lyrics:
484     def __init__ (self):
485         self.lyrics_syllables = []
486
487     def print_ly (self, printer):
488         printer.dump ("\lyricmode {")
489         for l in self.lyrics_syllables:
490             printer.dump ( "%s " % l )
491         printer.dump ("}")
492
493     def ly_expression (self):
494         lstr = "\lyricmode {\n  "
495         for l in self.lyrics_syllables:
496             lstr += l + " "
497         lstr += "\n}"
498         return lstr
499
500
501 class Header:
502     def __init__ (self):
503         self.header_fields = {}
504     def set_field (self, field, value):
505         self.header_fields[field] = value
506
507     def print_ly (self, printer):
508         printer.dump ("\header {")
509         printer.newline ()
510         for (k,v) in self.header_fields.items ():
511             if v:
512                 printer.dump ('%s = %s' % (k,v))
513                 printer.newline ()
514         printer.dump ("}")
515         printer.newline ()
516         printer.newline ()
517
518
519
520
521 class EventChord (NestedMusic):
522     def __init__ (self):
523         NestedMusic.__init__ (self)
524         self.grace_elements = None
525         self.grace_type = None
526     def append_grace (self, element):
527         if element:
528             if not self.grace_elements:
529                 self.grace_elements = SequentialMusic ()
530             self.grace_elements.append (element)
531
532     def get_length (self):
533         l = Rational (0)
534         for e in self.elements:
535             l = max(l, e.get_length())
536         return l
537     
538     def print_ly (self, printer):
539         note_events = [e for e in self.elements if
540                isinstance (e, NoteEvent)]
541
542         rest_events = [e for e in self.elements if
543                isinstance (e, RhythmicEvent)
544                and not isinstance (e, NoteEvent)]
545         
546         other_events = [e for e in self.elements if
547                 not isinstance (e, RhythmicEvent)]
548
549         if self.grace_elements and self.elements:
550             if self.grace_type:
551                 printer ('\\%s' % self.grace_type)
552             else:
553                 printer ('\\grace')
554             # don't print newlines after the { and } braces
555             self.grace_elements.print_ly (printer, False)
556
557         if rest_events:
558             rest_events[0].print_ly (printer)
559         elif len (note_events) == 1:
560             note_events[0].print_ly (printer)
561         elif note_events:
562             pitches = [x.pitch.ly_expression () for x in note_events]
563             printer ('<%s>' % string.join (pitches))
564             note_events[0].duration.print_ly (printer)
565         else:
566             pass
567         
568         for e in other_events:
569             e.print_ly (printer)
570
571         self.print_comment (printer)
572             
573 class Partial (Music):
574     def __init__ (self):
575         Music.__init__ (self)
576         self.partial = None
577     def print_ly (self, printer):
578         if self.partial:
579             printer.dump ("\\partial %s" % self.partial.ly_expression ())
580
581 class BarLine (Music):
582     def __init__ (self):
583         Music.__init__ (self)
584         self.bar_number = 0
585         self.type = None
586         
587     def print_ly (self, printer):
588         bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': ":",
589                        'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
590                        'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
591                        'short': "'", 'none': "" }.get (self.type, None)
592         if bar_symbol <> None:
593             printer.dump ('\\bar "%s"' % bar_symbol)
594         else:
595             printer.dump ("|")
596
597         if self.bar_number > 0 and (self.bar_number % 10) == 0:
598             printer.dump ("\\barNumberCheck #%d " % self.bar_number)
599         else:
600             printer.print_verbatim (' %% %d' % self.bar_number)
601         printer.newline ()
602
603     def ly_expression (self):
604         return " | "
605
606 class Event(Music):
607     pass
608
609 class SpanEvent (Event):
610     def __init__ (self):
611         Event.__init__ (self)
612         self.span_direction = 0 # start/stop
613         self.line_type = 'solid'
614         self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
615         self.size = 0 # size of e.g. ocrave shift
616     def wait_for_note (self):
617         return True
618     def get_properties(self):
619         return "'span-direction  %d" % self.span_direction
620     def set_span_type (self, type):
621         self.span_type = type
622
623 class SlurEvent (SpanEvent):
624     def ly_expression (self):
625         before = ''
626         after = ''
627         # TODO: setting dashed/dotted line style does not work, because that
628         #       command needs to be written before the note, not when the
629         #       event is observed after the note!
630         #before = {'dotted': '\\slurDotted', 
631         #          'dashed' : '\\slurDashed'}.get (self.line_type, '')
632         #if before:
633             #after = '\\slurSolid'
634
635         return {-1: before + '(' + after,
636             1:')'}.get (self.span_direction, '')
637
638 class BeamEvent (SpanEvent):
639     def ly_expression (self):
640         return {-1: '[',
641             1:']'}.get (self.span_direction, '')
642
643 class PedalEvent (SpanEvent):
644     def ly_expression (self):
645         return {-1: '\\sustainDown',
646             1:'\\sustainUp'}.get (self.span_direction, '')
647
648 # type==-1 means octave up, type==-2 means octave down
649 class OctaveShiftEvent (SpanEvent):
650     def wait_for_note (self):
651         return False;
652     def set_span_type (self, type):
653         self.span_type = {'up': 1, 'down': -1}.get (type, 0)
654     def ly_octave_shift_indicator (self):
655         # convert 8/15 to lilypond indicators (+-1/+-2)
656         value = {8: 1, 15: 2}.get (self.size, 0)
657         # negative values go up!
658         value *= -1*self.span_type
659         return value
660     def ly_expression (self):
661         dir = self.ly_octave_shift_indicator ()
662         value = ''
663         if dir:
664             value = '#(set-octavation %s)' % dir
665         return { 
666             -1: value,
667             1: '#(set-octavation 0)'}.get (self.span_direction, '')
668
669 class TrillSpanEvent (SpanEvent):
670     def ly_expression (self):
671         return {-1: '\\startTrillSpan',
672             0: '', # no need to write out anything for type='continue'
673             1:'\\stopTrillSpan'}.get (self.span_direction, '')
674
675 class GlissandoEvent (SpanEvent):
676     def ly_expression (self):
677         style = ''
678         # TODO: wavy-line glissandos don't work, becasue the style has to be
679         #       set before the note, at the \glissando it's already too late!
680         #if self.line_type == 'wavy':
681             #style = "\once\override Glissando #'style = #'zigzag"
682         # In lilypond, glissando is NOT a spanner, unlike MusicXML.
683         return {-1: style + '\\glissando',
684             1:''}.get (self.span_direction, '')
685
686 class ArpeggioEvent(Event):
687     def wait_for_note (self):
688         return True;
689     def ly_expression (self):
690         return ('\\arpeggio')
691
692
693 class TieEvent(Event):
694     def ly_expression (self):
695         return '~'
696
697
698 class HairpinEvent (SpanEvent):
699     def set_span_type (self, type):
700         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
701     def hairpin_to_ly (self):
702         if self.span_direction == 1:
703             return '\!'
704         else:
705             return {1: '\<', -1: '\>'}.get (self.span_type, '')
706     
707     def ly_expression (self):
708         return self.hairpin_to_ly ()
709     
710     def print_ly (self, printer):
711         val = self.hairpin_to_ly ()
712         if val:
713             printer.dump (val)
714
715
716
717 class DynamicsEvent (Event):
718     def __init__ (self):
719         self.type = None
720         self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p", 
721                                     "mp", "mf", 
722                                     "f", "ff", "fff", "ffff", 
723                                     "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
724     def wait_for_note (self):
725         return True;
726     def ly_expression (self):
727         if self.type == None:
728             return;
729         elif self.type in self.available_commands:
730             return '\%s' % self.type
731         else:
732             return '-\markup{ \dynamic %s }' % self.type
733         
734     def print_ly (self, printer):
735         if self.type == None:
736             return
737         elif self.type in self.available_commands:
738             printer.dump ("\\%s" % self.type)
739         else:
740             printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
741
742
743 class TextEvent (Event):
744     def __init__ (self):
745         self.Text = None
746         self.force_direction = None
747         self.markup = ''
748     def wait_for_note (self):
749         return True
750
751     def direction_mod (self):
752         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
753
754     def ly_expression (self):
755         base_string = '%s\"%s\"'
756         if self.markup:
757             base_string = '%s\markup{ ' + self.markup + ' {%s} }'
758         return base_string % (self.direction_mod (), self.text)
759
760 class ArticulationEvent (Event):
761     def __init__ (self):
762         self.type = None
763         self.force_direction = None
764     def wait_for_note (self):
765         return True;
766
767     def direction_mod (self):
768         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
769
770     def ly_expression (self):
771         return '%s\\%s' % (self.direction_mod (), self.type)
772
773 class ShortArticulationEvent (ArticulationEvent):
774     def direction_mod (self):
775         # default is -
776         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
777     def ly_expression (self):
778         return '%s%s' % (self.direction_mod (), self.type)
779
780 class NoDirectionArticulationEvent (ArticulationEvent):
781     def ly_expression (self):
782         return '\\%s' % self.type
783
784 class MarkupEvent (ShortArticulationEvent):
785     def __init__ (self):
786         ArticulationEvent.__init__ (self)
787         self.contents = None
788     def ly_expression (self):
789         if self.contents:
790             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
791         else:
792             return ''
793
794 class FretEvent (MarkupEvent):
795     def __init__ (self):
796         MarkupEvent.__init__ (self)
797         self.force_direction = 1
798         self.strings = 6
799         self.frets = 4
800         self.barre = None
801         self.elements = []
802     def ly_expression (self):
803         val = ""
804         if self.strings <> 6:
805             val += "w:%s;" % self.strings
806         if self.frets <> 4:
807             val += "h:%s;" % self.frets
808         if self.barre and len (self.barre) >= 3:
809             val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
810         have_fingering = False
811         for i in self.elements:
812             if len (i) > 1:
813                 val += "%s-%s" % (i[0], i[1])
814             if len (i) > 2:
815                 have_fingering = True
816                 val += "-%s" % i[2]
817             val += ";"
818         if have_fingering:
819             val = "f:1;" + val
820         if val:
821             return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
822         else:
823             return ''
824
825 class TremoloEvent (ArticulationEvent):
826     def __init__ (self):
827         Event.__init__ (self)
828         self.bars = 0
829
830     def ly_expression (self):
831         str=''
832         if self.bars and self.bars > 0:
833             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
834         return str
835
836 class BendEvent (ArticulationEvent):
837     def __init__ (self):
838         Event.__init__ (self)
839         self.alter = 0
840     def ly_expression (self):
841         if self.alter:
842             return "-\\bendAfter #%s" % self.alter
843         else:
844             return ''
845
846 class RhythmicEvent(Event):
847     def __init__ (self):
848         Event.__init__ (self)
849         self.duration = Duration()
850         
851     def get_length (self):
852         return self.duration.get_length()
853         
854     def get_properties (self):
855         return ("'duration %s"
856                 % self.duration.lisp_expression ())
857     
858 class RestEvent (RhythmicEvent):
859     def __init__ (self):
860         RhythmicEvent.__init__ (self)
861         self.pitch = None
862     def ly_expression (self):
863         if self.pitch:
864             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
865         else:
866             return 'r%s' % self.duration.ly_expression ()
867     
868     def print_ly (self, printer):
869         if self.pitch:
870             self.pitch.print_ly (printer)
871             self.duration.print_ly (printer)
872             printer ('\\rest')
873         else:
874             printer('r')
875             self.duration.print_ly (printer)
876
877 class SkipEvent (RhythmicEvent):
878     def ly_expression (self):
879         return 's%s' % self.duration.ly_expression () 
880
881 class NoteEvent(RhythmicEvent):
882     def  __init__ (self):
883         RhythmicEvent.__init__ (self)
884         self.pitch = None
885         self.drum_type = None
886         self.cautionary = False
887         self.forced_accidental = False
888         
889     def get_properties (self):
890         str = RhythmicEvent.get_properties (self)
891         
892         if self.pitch:
893             str += self.pitch.lisp_expression ()
894         elif self.drum_type:
895             str += "'drum-type '%s" % self.drum_type
896
897         return str
898     
899     def pitch_mods (self):
900         excl_question = ''
901         if self.cautionary:
902             excl_question += '?'
903         if self.forced_accidental:
904             excl_question += '!'
905
906         return excl_question
907     
908     def ly_expression (self):
909         if self.pitch:
910             return '%s%s%s' % (self.pitch.ly_expression (),
911                                self.pitch_mods(),
912                                self.duration.ly_expression ())
913         elif self.drum_type:
914             return '%s%s' (self.drum_type,
915                            self.duration.ly_expression ())
916
917     def print_ly (self, printer):
918         if self.pitch:
919             self.pitch.print_ly (printer)
920             printer (self.pitch_mods ())
921         else:
922             printer (self.drum_type)
923
924         self.duration.print_ly (printer)
925
926 class KeySignatureChange (Music):
927     def __init__ (self):
928         Music.__init__ (self)
929         self.scale = []
930         self.tonic = Pitch()
931         self.mode = 'major'
932         
933     def ly_expression (self):
934         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
935                      self.mode)
936     
937     def lisp_expression (self):
938         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
939         scale_str = ("'(%s)" % string.join (pairs))
940
941         return """ (make-music 'KeyChangeEvent
942      'pitch-alist %s) """ % scale_str
943
944 class TimeSignatureChange (Music):
945     def __init__ (self):
946         Music.__init__ (self)
947         self.fraction = (4,4)
948     def ly_expression (self):
949         return '\\time %d/%d ' % self.fraction
950     
951 class ClefChange (Music):
952     def __init__ (self):
953         Music.__init__ (self)
954         self.type = 'G'
955         self.position = 2
956         self.octave = 0
957
958     def octave_modifier (self):
959         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
960     def clef_name (self):
961         return {('G', 2): "treble",
962                 ('G', 1): "french",
963                 ('C', 1): "soprano",
964                 ('C', 2): "mezzosoprano",
965                 ('C', 3): "alto",
966                 ('C', 4): "tenor",
967                 ('C', 5): "baritone",
968                 ('F', 3): "varbaritone",
969                 ('F', 4): "bass",
970                 ('F', 5): "subbass",
971                 ("percussion", 2): "percussion",
972                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
973     def ly_expression (self):
974         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
975
976     clef_dict = {
977         "G": ("clefs.G", -2, -6),
978         "C": ("clefs.C", 0, 0),
979         "F": ("clefs.F", 2, 6),
980         }
981     
982     def lisp_expression (self):
983         try:
984             (glyph, pos, c0) = self.clef_dict[self.type]
985         except KeyError:
986             return ""
987         clefsetting = """
988         (make-music 'SequentialMusic
989         'elements (list
990    (context-spec-music
991    (make-property-set 'clefGlyph "%s") 'Staff)
992    (context-spec-music
993    (make-property-set 'clefPosition %d) 'Staff)
994    (context-spec-music
995    (make-property-set 'middleCPosition %d) 'Staff)))
996 """ % (glyph, pos, c0)
997         return clefsetting
998
999
1000 class MultiMeasureRest(Music):
1001
1002     def lisp_expression (self):
1003         return """
1004 (make-music
1005   'MultiMeasureRestMusicGroup
1006   'elements
1007   (list (make-music (quote BarCheck))
1008         (make-music
1009           'EventChord
1010           'elements
1011           (list (make-music
1012                   'MultiMeasureRestEvent
1013                   'duration
1014                   %s)))
1015         (make-music (quote BarCheck))))
1016 """ % self.duration.lisp_expression ()
1017
1018     def ly_expression (self):
1019         return 'R%s' % self.duration.ly_expression ()
1020
1021
1022 class StaffGroup:
1023     def __init__ (self, command = "StaffGroup"):
1024         self.stafftype = command
1025         self.id = None
1026         self.instrument_name = None
1027         self.short_instrument_name = None
1028         self.symbol = None
1029         self.spanbar = None
1030         self.children = []
1031         # part_information is a list with entries of the form
1032         #     [staffid, voicelist]
1033         # where voicelist is a list with entries of the form
1034         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1035         self.part_information = None
1036
1037     def appendStaff (self, staff):
1038         self.children.append (staff)
1039
1040     def setPartInformation (self, part_name, staves_info):
1041         if part_name == self.id:
1042             self.part_information = staves_info
1043         else:
1044             for c in self.children:
1045                 c.setPartInformation (part_name, staves_info)
1046
1047     def print_ly_contents (self, printer):
1048         for c in self.children:
1049             if c:
1050                 c.print_ly (printer)
1051     def print_ly_overrides (self, printer):
1052         needs_with = False
1053         needs_with |= self.spanbar == "no"
1054         needs_with |= self.instrument_name != None
1055         needs_with |= self.short_instrument_name != None
1056         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1057         if needs_with:
1058             printer.dump ("\\with {")
1059             if self.instrument_name or self.short_instrument_name:
1060                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1061             if self.spanbar == "no":
1062                 printer.dump ("\\override SpanBar #'transparent = ##t")
1063             brack = {"brace": "SystemStartBrace",
1064                      "none": "f",
1065                      "line": "SystemStartBar"}.get (self.symbol, None)
1066             if brack:
1067                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1068             printer.dump ("}")
1069
1070     def print_ly (self, printer):
1071         if self.stafftype:
1072             printer.dump ("\\new %s" % self.stafftype)
1073         self.print_ly_overrides (printer)
1074         printer.dump ("<<")
1075         printer.newline ()
1076         if self.stafftype and self.instrument_name:
1077             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1078                     escape_instrument_string (self.instrument_name)))
1079             printer.newline ()
1080         if self.stafftype and self.short_instrument_name:
1081             printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype, 
1082                     escape_instrument_string (self.short_instrument_name)))
1083             printer.newline ()
1084         self.print_ly_contents (printer)
1085         printer.newline ()
1086         printer.dump (">>")
1087         printer.newline ()
1088
1089
1090 class Staff (StaffGroup):
1091     def __init__ (self):
1092         StaffGroup.__init__ (self, "Staff")
1093         self.part = None
1094
1095     def print_ly_overrides (self, printer):
1096         pass
1097
1098     def print_ly_contents (self, printer):
1099         if not self.id or not self.part_information:
1100             return
1101
1102         for [staff_id, voices] in self.part_information:
1103             if staff_id:
1104                 printer ('\\context Staff = "%s" << ' % staff_id)
1105             else:
1106                 printer ('\\context Staff << ')
1107             printer.newline ()
1108             n = 0
1109             nr_voices = len (voices)
1110             for [v, lyrics] in voices:
1111                 n += 1
1112                 voice_count_text = ''
1113                 if nr_voices > 1:
1114                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1115                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1116                 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1117                 printer.newline ()
1118
1119                 for l in lyrics:
1120                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1121                     printer.newline()
1122             printer ('>>')
1123
1124     def print_ly (self, printer):
1125         if self.part_information and len (self.part_information) > 1:
1126             self.stafftype = "PianoStaff"
1127         StaffGroup.print_ly (self, printer)
1128
1129
1130 def test_pitch ():
1131     bflat = Pitch()
1132     bflat.alteration = -1
1133     bflat.step =  6
1134     bflat.octave = -1
1135     fifth = Pitch()
1136     fifth.step = 4
1137     down = Pitch ()
1138     down.step = -4
1139     down.normalize ()
1140     
1141     
1142     print bflat.semitones()
1143     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1144     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1145
1146     print bflat.semitones(), 'down'
1147     print bflat.transposed (down)
1148     print bflat.transposed (down).transposed (down)
1149     print bflat.transposed (down).transposed (down).transposed (down)
1150
1151
1152
1153 def test_printer ():
1154     def make_note ():
1155         evc = EventChord()
1156         n = NoteEvent()
1157         evc.append (n)
1158         return n
1159
1160     def make_tup ():
1161         m = SequentialMusic()
1162         m.append (make_note ())
1163         m.append (make_note ())
1164         m.append (make_note ())
1165
1166         
1167         t = TimeScaledMusic ()
1168         t.numerator = 2
1169         t.denominator = 3
1170         t.element = m
1171         return t
1172
1173     m = SequentialMusic ()
1174     m.append (make_tup ())
1175     m.append (make_tup ())
1176     m.append (make_tup ())
1177     
1178     printer = Output_printer()
1179     m.print_ly (printer)
1180     printer.newline ()
1181     
1182 def test_expr ():
1183     m = SequentialMusic()
1184     l = 2  
1185     evc = EventChord()
1186     n = NoteEvent()
1187     n.duration.duration_log = l
1188     n.pitch.step = 1
1189     evc.insert_around (None, n, 0)
1190     m.insert_around (None, evc, 0)
1191
1192     evc = EventChord()
1193     n = NoteEvent()
1194     n.duration.duration_log = l
1195     n.pitch.step = 3
1196     evc.insert_around (None, n, 0)
1197     m.insert_around (None, evc, 0)
1198
1199     evc = EventChord()
1200     n = NoteEvent()
1201     n.duration.duration_log = l
1202     n.pitch.step = 2 
1203     evc.insert_around (None, n, 0)
1204     m.insert_around (None, evc, 0)
1205
1206     evc = ClefChange()
1207     evc.type = 'treble'
1208     m.insert_around (None, evc, 0)
1209
1210     evc = EventChord()
1211     tonic = Pitch ()
1212     tonic.step = 2
1213     tonic.alteration = -2
1214     n = KeySignatureChange()
1215     n.tonic=tonic.copy()
1216     n.scale = [0, 0, -2, 0, 0,-2,-2]
1217     
1218     evc.insert_around (None, n, 0)
1219     m.insert_around (None, evc, 0)
1220
1221     return m
1222
1223
1224 if __name__ == '__main__':
1225     test_printer ()
1226     raise 'bla'
1227     test_pitch()
1228     
1229     expr = test_expr()
1230     expr.set_start (Rational (0))
1231     print expr.ly_expression()
1232     start = Rational (0,4)
1233     stop = Rational (4,2)
1234     def sub(x, start=start, stop=stop):
1235         ok = x.start >= start and x.start +x.get_length() <= stop
1236         return ok
1237     
1238     print expr.lisp_sub_expression(sub)
1239