]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
Merge branch 'master' of ssh+git://hanwen@git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / python / musicexp.py
1 import inspect
2 import sys
3 import string
4 import re
5
6 from rational import Rational
7
8
9 def escape_instrument_string (input_string):
10     retstring = string.replace (input_string, "\"", "\\\"")
11     if re.match ('.*\n.*', retstring):
12         strings = retstring.split ('\r\n')
13         retstring = "\\markup { \\column { "
14         for s in strings:
15             retstring += "\\line {\"" + s + "\"} "
16         retstring += "} }"
17     else:
18         retstring = "\"" + retstring + "\""
19     return retstring
20
21 class Output_stack_element:
22     def __init__ (self):
23         self.factor = Rational (1)
24     def copy (self):
25         o = Output_stack_element()
26         o.factor = self.factor
27         return o
28
29 class Output_printer:
30
31     """A class that takes care of formatting (eg.: indenting) a
32     Music expression as a .ly file.
33     
34     """
35     ## TODO: support for \relative.
36     
37     def __init__ (self):
38         self._line = ''
39         self._indent = 4
40         self._nesting = 0
41         self._file = sys.stdout
42         self._line_len = 72
43         self._output_state_stack = [Output_stack_element()]
44         self._skipspace = False
45         self._last_duration = None
46
47     def set_file (self, file):
48         self._file = file
49         
50     def dump_version (self):
51         self.newline ()
52         self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
53         self.newline ()
54         
55     def get_indent (self):
56         return self._nesting * self._indent
57     
58     def override (self):
59         last = self._output_state_stack[-1]
60         self._output_state_stack.append (last.copy())
61         
62     def add_factor (self, factor):
63         self.override()
64         self._output_state_stack[-1].factor *=  factor
65
66     def revert (self):
67         del self._output_state_stack[-1]
68         if not self._output_state_stack:
69             raise 'empty'
70
71     def duration_factor (self):
72         return self._output_state_stack[-1].factor
73
74     def print_verbatim (self, str):
75         self._line += str
76
77     def unformatted_output (self, str):
78         # don't indent on \< and indent only once on <<
79         self._nesting += ( str.count ('<') 
80                          - str.count ('\<') - str.count ('<<') 
81                          + str.count ('{') )
82         self._nesting -= ( str.count ('>') - str.count ('\>') - str.count ('>>')
83                                            - str.count ('->') - str.count ('_>')
84                                            - str.count ('^>')
85                          + str.count ('}') )
86         self.print_verbatim (str)
87         
88     def print_duration_string (self, str):
89         if self._last_duration == str:
90             return
91         
92         self.unformatted_output (str)
93                   
94     def add_word (self, str):
95         if (len (str) + 1 + len (self._line) > self._line_len):
96             self.newline()
97             self._skipspace = True
98
99         if not self._skipspace:
100             self._line += ' '
101         self.unformatted_output (str)
102         self._skipspace = False
103         
104     def newline (self):
105         self._file.write (self._line + '\n')
106         self._line = ' ' * self._indent * self._nesting
107         self._skipspace = True
108
109     def skipspace (self):
110         self._skipspace = True
111         
112     def __call__(self, arg):
113         self.dump (arg)
114     
115     def dump (self, str):
116         if self._skipspace:
117             self._skipspace = False
118             self.unformatted_output (str)
119         else:
120             words = string.split (str)
121             for w in words:
122                 self.add_word (w)
123
124
125     def close (self):
126         self.newline ()
127         self._file.close ()
128         self._file = None
129         
130         
131 class Duration:
132     def __init__ (self):
133         self.duration_log = 0
134         self.dots = 0
135         self.factor = Rational (1)
136         
137     def lisp_expression (self):
138         return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
139                              self.dots,
140                              self.factor.numerator (),
141                              self.factor.denominator ())
142
143
144     def ly_expression (self, factor = None):
145         if not factor:
146             factor = self.factor
147             
148         str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
149
150         if factor <> Rational (1,1):
151             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 NoDirectionArticulationEvent (ArticulationEvent):
715     def ly_expression (self):
716         return '\\%s' % self.type
717
718 class MarkupEvent (ShortArticulationEvent):
719     def __init__ (self):
720         ArticulationEvent.__init__ (self)
721         self.contents = None
722     def ly_expression (self):
723         if self.contents:
724             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
725
726 class TremoloEvent (ArticulationEvent):
727     def __init__ (self):
728         Event.__init__ (self)
729         self.bars = 0
730
731     def ly_expression (self):
732         str=''
733         if self.bars and self.bars > 0:
734             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
735         return str
736
737 class BendEvent (ArticulationEvent):
738     def __init__ (self):
739         Event.__init__ (self)
740         self.alter = 0
741     def ly_expression (self):
742         if self.alter:
743             return "-\\bendAfter #%s" % self.alter
744         else:
745             return ''
746
747 class RhythmicEvent(Event):
748     def __init__ (self):
749         Event.__init__ (self)
750         self.duration = Duration()
751         
752     def get_length (self):
753         return self.duration.get_length()
754         
755     def get_properties (self):
756         return ("'duration %s"
757                 % self.duration.lisp_expression ())
758     
759 class RestEvent (RhythmicEvent):
760     def __init__ (self):
761         RhythmicEvent.__init__ (self)
762         self.pitch = None
763     def ly_expression (self):
764         if self.pitch:
765             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
766         else:
767             return 'r%s' % self.duration.ly_expression ()
768     
769     def print_ly (self, printer):
770         if self.pitch:
771             self.pitch.print_ly (printer)
772             self.duration.print_ly (printer)
773             printer ('\\rest')
774         else:
775             printer('r')
776             self.duration.print_ly (printer)
777
778 class SkipEvent (RhythmicEvent):
779     def ly_expression (self):
780         return 's%s' % self.duration.ly_expression () 
781
782 class NoteEvent(RhythmicEvent):
783     def  __init__ (self):
784         RhythmicEvent.__init__ (self)
785         self.pitch = None
786         self.drum_type = None
787         self.cautionary = False
788         self.forced_accidental = False
789         
790     def get_properties (self):
791         str = RhythmicEvent.get_properties (self)
792         
793         if self.pitch:
794             str += self.pitch.lisp_expression ()
795         elif self.drum_type:
796             str += "'drum-type '%s" % self.drum_type
797
798         return str
799     
800     def pitch_mods (self):
801         excl_question = ''
802         if self.cautionary:
803             excl_question += '?'
804         if self.forced_accidental:
805             excl_question += '!'
806
807         return excl_question
808     
809     def ly_expression (self):
810         if self.pitch:
811             return '%s%s%s' % (self.pitch.ly_expression (),
812                                self.pitch_mods(),
813                                self.duration.ly_expression ())
814         elif self.drum_type:
815             return '%s%s' (self.drum_type,
816                            self.duration.ly_expression ())
817
818     def print_ly (self, printer):
819         if self.pitch:
820             self.pitch.print_ly (printer)
821             printer (self.pitch_mods ())
822         else:
823             printer (self.drum_type)
824
825         self.duration.print_ly (printer)
826
827 class KeySignatureChange (Music):
828     def __init__ (self):
829         Music.__init__ (self)
830         self.scale = []
831         self.tonic = Pitch()
832         self.mode = 'major'
833         
834     def ly_expression (self):
835         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
836                      self.mode)
837     
838     def lisp_expression (self):
839         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
840         scale_str = ("'(%s)" % string.join (pairs))
841
842         return """ (make-music 'KeyChangeEvent
843      'pitch-alist %s) """ % scale_str
844
845 class TimeSignatureChange (Music):
846     def __init__ (self):
847         Music.__init__ (self)
848         self.fraction = (4,4)
849     def ly_expression (self):
850         return '\\time %d/%d ' % self.fraction
851     
852 class ClefChange (Music):
853     def __init__ (self):
854         Music.__init__ (self)
855         self.type = 'G'
856         self.position = 2
857         self.octave = 0
858
859     def octave_modifier (self):
860         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
861     def clef_name (self):
862         return {('G', 2): "treble",
863                 ('G', 1): "french",
864                 ('C', 1): "soprano",
865                 ('C', 2): "mezzosoprano",
866                 ('C', 3): "alto",
867                 ('C', 4): "tenor",
868                 ('C', 5): "baritone",
869                 ('F', 3): "varbaritone",
870                 ('F', 4): "bass",
871                 ('F', 5): "subbass",
872                 ("percussion", 2): "percussion",
873                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
874     def ly_expression (self):
875         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
876
877     clef_dict = {
878         "G": ("clefs.G", -2, -6),
879         "C": ("clefs.C", 0, 0),
880         "F": ("clefs.F", 2, 6),
881         }
882     
883     def lisp_expression (self):
884         try:
885             (glyph, pos, c0) = self.clef_dict[self.type]
886         except KeyError:
887             return ""
888         clefsetting = """
889         (make-music 'SequentialMusic
890         'elements (list
891    (context-spec-music
892    (make-property-set 'clefGlyph "%s") 'Staff)
893    (context-spec-music
894    (make-property-set 'clefPosition %d) 'Staff)
895    (context-spec-music
896    (make-property-set 'middleCPosition %d) 'Staff)))
897 """ % (glyph, pos, c0)
898         return clefsetting
899
900
901 class MultiMeasureRest(Music):
902
903     def lisp_expression (self):
904         return """
905 (make-music
906   'MultiMeasureRestMusicGroup
907   'elements
908   (list (make-music (quote BarCheck))
909         (make-music
910           'EventChord
911           'elements
912           (list (make-music
913                   'MultiMeasureRestEvent
914                   'duration
915                   %s)))
916         (make-music (quote BarCheck))))
917 """ % self.duration.lisp_expression ()
918
919     def ly_expression (self):
920         return 'R%s' % self.duration.ly_expression ()
921
922
923 class StaffGroup:
924     def __init__ (self, command = "StaffGroup"):
925         self.stafftype = command
926         self.id = None
927         self.instrument_name = None
928         self.short_instrument_name = None
929         self.symbol = None
930         self.spanbar = None
931         self.children = []
932         # part_information is a list with entries of the form
933         #     [staffid, voicelist]
934         # where voicelist is a list with entries of the form
935         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
936         self.part_information = None
937
938     def appendStaff (self, staff):
939         self.children.append (staff)
940
941     def setPartInformation (self, part_name, staves_info):
942         if part_name == self.id:
943             self.part_information = staves_info
944         else:
945             for c in self.children:
946                 c.setPartInformation (part_name, staves_info)
947
948     def print_ly_contents (self, printer):
949         for c in self.children:
950             if c:
951                 c.print_ly (printer)
952     def print_ly_overrides (self, printer):
953         needs_with = False
954         needs_with |= self.spanbar == "no"
955         needs_with |= self.instrument_name != None
956         needs_with |= self.short_instrument_name != None
957         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
958         if needs_with:
959             printer.dump ("\\with {")
960             if self.instrument_name or self.short_instrument_name:
961                 printer.dump ("\\consists \"Instrument_name_engraver\"")
962             if self.spanbar == "no":
963                 printer.dump ("\\override SpanBar #'transparent = ##t")
964             brack = {"brace": "SystemStartBrace",
965                      "none": "f",
966                      "line": "SystemStartBar"}.get (self.symbol, None)
967             if brack:
968                 printer.dump ("systemStartDelimiter = #'%s" % brack)
969             printer.dump ("}")
970
971     def print_ly (self, printer):
972         if self.stafftype:
973             printer.dump ("\\new %s" % self.stafftype)
974         self.print_ly_overrides (printer)
975         printer.dump ("<<")
976         printer.newline ()
977         if self.stafftype and self.instrument_name:
978             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
979                     escape_instrument_string (self.instrument_name)))
980             printer.newline ()
981         if self.stafftype and self.short_instrument_name:
982             printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype, 
983                     escape_instrument_string (self.short_instrument_name)))
984             printer.newline ()
985         self.print_ly_contents (printer)
986         printer.newline ()
987         printer.dump (">>")
988         printer.newline ()
989
990
991 class Staff (StaffGroup):
992     def __init__ (self):
993         StaffGroup.__init__ (self, "Staff")
994         self.part = None
995
996     def print_ly_overrides (self, printer):
997         pass
998
999     def print_ly_contents (self, printer):
1000         if not self.id or not self.part_information:
1001             return
1002
1003         for [staff_id, voices] in self.part_information:
1004             if staff_id:
1005                 printer ('\\context Staff = "%s" << ' % staff_id)
1006             else:
1007                 printer ('\\context Staff << ')
1008             printer.newline ()
1009             n = 0
1010             nr_voices = len (voices)
1011             for [v, lyrics] in voices:
1012                 n += 1
1013                 voice_count_text = ''
1014                 if nr_voices > 1:
1015                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1016                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1017                 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1018                 printer.newline ()
1019
1020                 for l in lyrics:
1021                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1022                     printer.newline()
1023             printer ('>>')
1024
1025     def print_ly (self, printer):
1026         if self.part_information and len (self.part_information) > 1:
1027             self.stafftype = "PianoStaff"
1028         StaffGroup.print_ly (self, printer)
1029
1030
1031 def test_pitch ():
1032     bflat = Pitch()
1033     bflat.alteration = -1
1034     bflat.step =  6
1035     bflat.octave = -1
1036     fifth = Pitch()
1037     fifth.step = 4
1038     down = Pitch ()
1039     down.step = -4
1040     down.normalize ()
1041     
1042     
1043     print bflat.semitones()
1044     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1045     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1046
1047     print bflat.semitones(), 'down'
1048     print bflat.transposed (down)
1049     print bflat.transposed (down).transposed (down)
1050     print bflat.transposed (down).transposed (down).transposed (down)
1051
1052
1053
1054 def test_printer ():
1055     def make_note ():
1056         evc = EventChord()
1057         n = NoteEvent()
1058         evc.append (n)
1059         return n
1060
1061     def make_tup ():
1062         m = SequentialMusic()
1063         m.append (make_note ())
1064         m.append (make_note ())
1065         m.append (make_note ())
1066
1067         
1068         t = TimeScaledMusic ()
1069         t.numerator = 2
1070         t.denominator = 3
1071         t.element = m
1072         return t
1073
1074     m = SequentialMusic ()
1075     m.append (make_tup ())
1076     m.append (make_tup ())
1077     m.append (make_tup ())
1078     
1079     printer = Output_printer()
1080     m.print_ly (printer)
1081     printer.newline ()
1082     
1083 def test_expr ():
1084     m = SequentialMusic()
1085     l = 2  
1086     evc = EventChord()
1087     n = NoteEvent()
1088     n.duration.duration_log = l
1089     n.pitch.step = 1
1090     evc.insert_around (None, n, 0)
1091     m.insert_around (None, evc, 0)
1092
1093     evc = EventChord()
1094     n = NoteEvent()
1095     n.duration.duration_log = l
1096     n.pitch.step = 3
1097     evc.insert_around (None, n, 0)
1098     m.insert_around (None, evc, 0)
1099
1100     evc = EventChord()
1101     n = NoteEvent()
1102     n.duration.duration_log = l
1103     n.pitch.step = 2 
1104     evc.insert_around (None, n, 0)
1105     m.insert_around (None, evc, 0)
1106
1107     evc = ClefChange()
1108     evc.type = 'treble'
1109     m.insert_around (None, evc, 0)
1110
1111     evc = EventChord()
1112     tonic = Pitch ()
1113     tonic.step = 2
1114     tonic.alteration = -2
1115     n = KeySignatureChange()
1116     n.tonic=tonic.copy()
1117     n.scale = [0, 0, -2, 0, 0,-2,-2]
1118     
1119     evc.insert_around (None, n, 0)
1120     m.insert_around (None, evc, 0)
1121
1122     return m
1123
1124
1125 if __name__ == '__main__':
1126     test_printer ()
1127     raise 'bla'
1128     test_pitch()
1129     
1130     expr = test_expr()
1131     expr.set_start (Rational (0))
1132     print expr.ly_expression()
1133     start = Rational (0,4)
1134     stop = Rational (4,2)
1135     def sub(x, start=start, stop=stop):
1136         ok = x.start >= start and x.start +x.get_length() <= stop
1137         return ok
1138     
1139     print expr.lisp_sub_expression(sub)
1140