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