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