]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Implement staff changes in piano staves
[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 StaffChange (Music):
1001     def __init__ (self, staff):
1002         Music.__init__ (self)
1003         self.staff = staff
1004     def ly_expression (self):
1005         if self.staff:
1006             return "\\change Staff=\"%s\"" % self.staff
1007         else:
1008             return ''
1009
1010
1011 class MultiMeasureRest(Music):
1012
1013     def lisp_expression (self):
1014         return """
1015 (make-music
1016   'MultiMeasureRestMusicGroup
1017   'elements
1018   (list (make-music (quote BarCheck))
1019         (make-music
1020           'EventChord
1021           'elements
1022           (list (make-music
1023                   'MultiMeasureRestEvent
1024                   'duration
1025                   %s)))
1026         (make-music (quote BarCheck))))
1027 """ % self.duration.lisp_expression ()
1028
1029     def ly_expression (self):
1030         return 'R%s' % self.duration.ly_expression ()
1031
1032
1033 class StaffGroup:
1034     def __init__ (self, command = "StaffGroup"):
1035         self.stafftype = command
1036         self.id = None
1037         self.instrument_name = None
1038         self.short_instrument_name = None
1039         self.symbol = None
1040         self.spanbar = None
1041         self.children = []
1042         # part_information is a list with entries of the form
1043         #     [staffid, voicelist]
1044         # where voicelist is a list with entries of the form
1045         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1046         self.part_information = None
1047
1048     def appendStaff (self, staff):
1049         self.children.append (staff)
1050
1051     def setPartInformation (self, part_name, staves_info):
1052         if part_name == self.id:
1053             self.part_information = staves_info
1054         else:
1055             for c in self.children:
1056                 c.setPartInformation (part_name, staves_info)
1057
1058     def print_ly_contents (self, printer):
1059         for c in self.children:
1060             if c:
1061                 c.print_ly (printer)
1062     def print_ly_overrides (self, printer):
1063         needs_with = False
1064         needs_with |= self.spanbar == "no"
1065         needs_with |= self.instrument_name != None
1066         needs_with |= self.short_instrument_name != None
1067         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1068         if needs_with:
1069             printer.dump ("\\with {")
1070             if self.instrument_name or self.short_instrument_name:
1071                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1072             if self.spanbar == "no":
1073                 printer.dump ("\\override SpanBar #'transparent = ##t")
1074             brack = {"brace": "SystemStartBrace",
1075                      "none": "f",
1076                      "line": "SystemStartBar"}.get (self.symbol, None)
1077             if brack:
1078                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1079             printer.dump ("}")
1080
1081     def print_ly (self, printer):
1082         if self.stafftype:
1083             printer.dump ("\\new %s" % self.stafftype)
1084         self.print_ly_overrides (printer)
1085         printer.dump ("<<")
1086         printer.newline ()
1087         if self.stafftype and self.instrument_name:
1088             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1089                     escape_instrument_string (self.instrument_name)))
1090             printer.newline ()
1091         if self.stafftype and self.short_instrument_name:
1092             printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype, 
1093                     escape_instrument_string (self.short_instrument_name)))
1094             printer.newline ()
1095         self.print_ly_contents (printer)
1096         printer.newline ()
1097         printer.dump (">>")
1098         printer.newline ()
1099
1100
1101 class Staff (StaffGroup):
1102     def __init__ (self):
1103         StaffGroup.__init__ (self, "Staff")
1104         self.part = None
1105
1106     def print_ly_overrides (self, printer):
1107         pass
1108
1109     def print_ly_contents (self, printer):
1110         if not self.id or not self.part_information:
1111             return
1112
1113         for [staff_id, voices] in self.part_information:
1114             if staff_id:
1115                 printer ('\\context Staff = "%s" << ' % staff_id)
1116             else:
1117                 printer ('\\context Staff << ')
1118             printer.newline ()
1119             n = 0
1120             nr_voices = len (voices)
1121             for [v, lyrics] in voices:
1122                 n += 1
1123                 voice_count_text = ''
1124                 if nr_voices > 1:
1125                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1126                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1127                 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1128                 printer.newline ()
1129
1130                 for l in lyrics:
1131                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1132                     printer.newline()
1133             printer ('>>')
1134
1135     def print_ly (self, printer):
1136         if self.part_information and len (self.part_information) > 1:
1137             self.stafftype = "PianoStaff"
1138         StaffGroup.print_ly (self, printer)
1139
1140
1141 def test_pitch ():
1142     bflat = Pitch()
1143     bflat.alteration = -1
1144     bflat.step =  6
1145     bflat.octave = -1
1146     fifth = Pitch()
1147     fifth.step = 4
1148     down = Pitch ()
1149     down.step = -4
1150     down.normalize ()
1151     
1152     
1153     print bflat.semitones()
1154     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1155     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1156
1157     print bflat.semitones(), 'down'
1158     print bflat.transposed (down)
1159     print bflat.transposed (down).transposed (down)
1160     print bflat.transposed (down).transposed (down).transposed (down)
1161
1162
1163
1164 def test_printer ():
1165     def make_note ():
1166         evc = EventChord()
1167         n = NoteEvent()
1168         evc.append (n)
1169         return n
1170
1171     def make_tup ():
1172         m = SequentialMusic()
1173         m.append (make_note ())
1174         m.append (make_note ())
1175         m.append (make_note ())
1176
1177         
1178         t = TimeScaledMusic ()
1179         t.numerator = 2
1180         t.denominator = 3
1181         t.element = m
1182         return t
1183
1184     m = SequentialMusic ()
1185     m.append (make_tup ())
1186     m.append (make_tup ())
1187     m.append (make_tup ())
1188     
1189     printer = Output_printer()
1190     m.print_ly (printer)
1191     printer.newline ()
1192     
1193 def test_expr ():
1194     m = SequentialMusic()
1195     l = 2  
1196     evc = EventChord()
1197     n = NoteEvent()
1198     n.duration.duration_log = l
1199     n.pitch.step = 1
1200     evc.insert_around (None, n, 0)
1201     m.insert_around (None, evc, 0)
1202
1203     evc = EventChord()
1204     n = NoteEvent()
1205     n.duration.duration_log = l
1206     n.pitch.step = 3
1207     evc.insert_around (None, n, 0)
1208     m.insert_around (None, evc, 0)
1209
1210     evc = EventChord()
1211     n = NoteEvent()
1212     n.duration.duration_log = l
1213     n.pitch.step = 2 
1214     evc.insert_around (None, n, 0)
1215     m.insert_around (None, evc, 0)
1216
1217     evc = ClefChange()
1218     evc.type = 'treble'
1219     m.insert_around (None, evc, 0)
1220
1221     evc = EventChord()
1222     tonic = Pitch ()
1223     tonic.step = 2
1224     tonic.alteration = -2
1225     n = KeySignatureChange()
1226     n.tonic=tonic.copy()
1227     n.scale = [0, 0, -2, 0, 0,-2,-2]
1228     
1229     evc.insert_around (None, n, 0)
1230     m.insert_around (None, evc, 0)
1231
1232     return m
1233
1234
1235 if __name__ == '__main__':
1236     test_printer ()
1237     raise 'bla'
1238     test_pitch()
1239     
1240     expr = test_expr()
1241     expr.set_start (Rational (0))
1242     print expr.ly_expression()
1243     start = Rational (0,4)
1244     stop = Rational (4,2)
1245     def sub(x, start=start, stop=stop):
1246         ok = x.start >= start and x.start +x.get_length() <= stop
1247         return ok
1248     
1249     print expr.lisp_sub_expression(sub)
1250