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