]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Implement Barline styles and repeats in musicxml2ly
[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 ArticulationEvent (Event):
744     def __init__ (self):
745         self.type = None
746         self.force_direction = None
747
748     def direction_mod (self):
749         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
750
751     def ly_expression (self):
752         return '%s\\%s' % (self.direction_mod (), self.type)
753
754 class ShortArticulationEvent (ArticulationEvent):
755     def direction_mod (self):
756         # default is -
757         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
758     def ly_expression (self):
759         return '%s%s' % (self.direction_mod (), self.type)
760
761 class NoDirectionArticulationEvent (ArticulationEvent):
762     def ly_expression (self):
763         return '\\%s' % self.type
764
765 class MarkupEvent (ShortArticulationEvent):
766     def __init__ (self):
767         ArticulationEvent.__init__ (self)
768         self.contents = None
769     def ly_expression (self):
770         if self.contents:
771             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
772
773 class TremoloEvent (ArticulationEvent):
774     def __init__ (self):
775         Event.__init__ (self)
776         self.bars = 0
777
778     def ly_expression (self):
779         str=''
780         if self.bars and self.bars > 0:
781             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
782         return str
783
784 class BendEvent (ArticulationEvent):
785     def __init__ (self):
786         Event.__init__ (self)
787         self.alter = 0
788     def ly_expression (self):
789         if self.alter:
790             return "-\\bendAfter #%s" % self.alter
791         else:
792             return ''
793
794 class RhythmicEvent(Event):
795     def __init__ (self):
796         Event.__init__ (self)
797         self.duration = Duration()
798         
799     def get_length (self):
800         return self.duration.get_length()
801         
802     def get_properties (self):
803         return ("'duration %s"
804                 % self.duration.lisp_expression ())
805     
806 class RestEvent (RhythmicEvent):
807     def __init__ (self):
808         RhythmicEvent.__init__ (self)
809         self.pitch = None
810     def ly_expression (self):
811         if self.pitch:
812             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
813         else:
814             return 'r%s' % self.duration.ly_expression ()
815     
816     def print_ly (self, printer):
817         if self.pitch:
818             self.pitch.print_ly (printer)
819             self.duration.print_ly (printer)
820             printer ('\\rest')
821         else:
822             printer('r')
823             self.duration.print_ly (printer)
824
825 class SkipEvent (RhythmicEvent):
826     def ly_expression (self):
827         return 's%s' % self.duration.ly_expression () 
828
829 class NoteEvent(RhythmicEvent):
830     def  __init__ (self):
831         RhythmicEvent.__init__ (self)
832         self.pitch = None
833         self.drum_type = None
834         self.cautionary = False
835         self.forced_accidental = False
836         
837     def get_properties (self):
838         str = RhythmicEvent.get_properties (self)
839         
840         if self.pitch:
841             str += self.pitch.lisp_expression ()
842         elif self.drum_type:
843             str += "'drum-type '%s" % self.drum_type
844
845         return str
846     
847     def pitch_mods (self):
848         excl_question = ''
849         if self.cautionary:
850             excl_question += '?'
851         if self.forced_accidental:
852             excl_question += '!'
853
854         return excl_question
855     
856     def ly_expression (self):
857         if self.pitch:
858             return '%s%s%s' % (self.pitch.ly_expression (),
859                                self.pitch_mods(),
860                                self.duration.ly_expression ())
861         elif self.drum_type:
862             return '%s%s' (self.drum_type,
863                            self.duration.ly_expression ())
864
865     def print_ly (self, printer):
866         if self.pitch:
867             self.pitch.print_ly (printer)
868             printer (self.pitch_mods ())
869         else:
870             printer (self.drum_type)
871
872         self.duration.print_ly (printer)
873
874 class KeySignatureChange (Music):
875     def __init__ (self):
876         Music.__init__ (self)
877         self.scale = []
878         self.tonic = Pitch()
879         self.mode = 'major'
880         
881     def ly_expression (self):
882         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
883                      self.mode)
884     
885     def lisp_expression (self):
886         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
887         scale_str = ("'(%s)" % string.join (pairs))
888
889         return """ (make-music 'KeyChangeEvent
890      'pitch-alist %s) """ % scale_str
891
892 class TimeSignatureChange (Music):
893     def __init__ (self):
894         Music.__init__ (self)
895         self.fraction = (4,4)
896     def ly_expression (self):
897         return '\\time %d/%d ' % self.fraction
898     
899 class ClefChange (Music):
900     def __init__ (self):
901         Music.__init__ (self)
902         self.type = 'G'
903         self.position = 2
904         self.octave = 0
905
906     def octave_modifier (self):
907         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
908     def clef_name (self):
909         return {('G', 2): "treble",
910                 ('G', 1): "french",
911                 ('C', 1): "soprano",
912                 ('C', 2): "mezzosoprano",
913                 ('C', 3): "alto",
914                 ('C', 4): "tenor",
915                 ('C', 5): "baritone",
916                 ('F', 3): "varbaritone",
917                 ('F', 4): "bass",
918                 ('F', 5): "subbass",
919                 ("percussion", 2): "percussion",
920                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
921     def ly_expression (self):
922         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
923
924     clef_dict = {
925         "G": ("clefs.G", -2, -6),
926         "C": ("clefs.C", 0, 0),
927         "F": ("clefs.F", 2, 6),
928         }
929     
930     def lisp_expression (self):
931         try:
932             (glyph, pos, c0) = self.clef_dict[self.type]
933         except KeyError:
934             return ""
935         clefsetting = """
936         (make-music 'SequentialMusic
937         'elements (list
938    (context-spec-music
939    (make-property-set 'clefGlyph "%s") 'Staff)
940    (context-spec-music
941    (make-property-set 'clefPosition %d) 'Staff)
942    (context-spec-music
943    (make-property-set 'middleCPosition %d) 'Staff)))
944 """ % (glyph, pos, c0)
945         return clefsetting
946
947
948 class MultiMeasureRest(Music):
949
950     def lisp_expression (self):
951         return """
952 (make-music
953   'MultiMeasureRestMusicGroup
954   'elements
955   (list (make-music (quote BarCheck))
956         (make-music
957           'EventChord
958           'elements
959           (list (make-music
960                   'MultiMeasureRestEvent
961                   'duration
962                   %s)))
963         (make-music (quote BarCheck))))
964 """ % self.duration.lisp_expression ()
965
966     def ly_expression (self):
967         return 'R%s' % self.duration.ly_expression ()
968
969
970 class StaffGroup:
971     def __init__ (self, command = "StaffGroup"):
972         self.stafftype = command
973         self.id = None
974         self.instrument_name = None
975         self.short_instrument_name = None
976         self.symbol = None
977         self.spanbar = None
978         self.children = []
979         # part_information is a list with entries of the form
980         #     [staffid, voicelist]
981         # where voicelist is a list with entries of the form
982         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
983         self.part_information = None
984
985     def appendStaff (self, staff):
986         self.children.append (staff)
987
988     def setPartInformation (self, part_name, staves_info):
989         if part_name == self.id:
990             self.part_information = staves_info
991         else:
992             for c in self.children:
993                 c.setPartInformation (part_name, staves_info)
994
995     def print_ly_contents (self, printer):
996         for c in self.children:
997             if c:
998                 c.print_ly (printer)
999     def print_ly_overrides (self, printer):
1000         needs_with = False
1001         needs_with |= self.spanbar == "no"
1002         needs_with |= self.instrument_name != None
1003         needs_with |= self.short_instrument_name != None
1004         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1005         if needs_with:
1006             printer.dump ("\\with {")
1007             if self.instrument_name or self.short_instrument_name:
1008                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1009             if self.spanbar == "no":
1010                 printer.dump ("\\override SpanBar #'transparent = ##t")
1011             brack = {"brace": "SystemStartBrace",
1012                      "none": "f",
1013                      "line": "SystemStartBar"}.get (self.symbol, None)
1014             if brack:
1015                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1016             printer.dump ("}")
1017
1018     def print_ly (self, printer):
1019         if self.stafftype:
1020             printer.dump ("\\new %s" % self.stafftype)
1021         self.print_ly_overrides (printer)
1022         printer.dump ("<<")
1023         printer.newline ()
1024         if self.stafftype and self.instrument_name:
1025             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1026                     escape_instrument_string (self.instrument_name)))
1027             printer.newline ()
1028         if self.stafftype and self.short_instrument_name:
1029             printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype, 
1030                     escape_instrument_string (self.short_instrument_name)))
1031             printer.newline ()
1032         self.print_ly_contents (printer)
1033         printer.newline ()
1034         printer.dump (">>")
1035         printer.newline ()
1036
1037
1038 class Staff (StaffGroup):
1039     def __init__ (self):
1040         StaffGroup.__init__ (self, "Staff")
1041         self.part = None
1042
1043     def print_ly_overrides (self, printer):
1044         pass
1045
1046     def print_ly_contents (self, printer):
1047         if not self.id or not self.part_information:
1048             return
1049
1050         for [staff_id, voices] in self.part_information:
1051             if staff_id:
1052                 printer ('\\context Staff = "%s" << ' % staff_id)
1053             else:
1054                 printer ('\\context Staff << ')
1055             printer.newline ()
1056             n = 0
1057             nr_voices = len (voices)
1058             for [v, lyrics] in voices:
1059                 n += 1
1060                 voice_count_text = ''
1061                 if nr_voices > 1:
1062                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1063                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1064                 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1065                 printer.newline ()
1066
1067                 for l in lyrics:
1068                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1069                     printer.newline()
1070             printer ('>>')
1071
1072     def print_ly (self, printer):
1073         if self.part_information and len (self.part_information) > 1:
1074             self.stafftype = "PianoStaff"
1075         StaffGroup.print_ly (self, printer)
1076
1077
1078 def test_pitch ():
1079     bflat = Pitch()
1080     bflat.alteration = -1
1081     bflat.step =  6
1082     bflat.octave = -1
1083     fifth = Pitch()
1084     fifth.step = 4
1085     down = Pitch ()
1086     down.step = -4
1087     down.normalize ()
1088     
1089     
1090     print bflat.semitones()
1091     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1092     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1093
1094     print bflat.semitones(), 'down'
1095     print bflat.transposed (down)
1096     print bflat.transposed (down).transposed (down)
1097     print bflat.transposed (down).transposed (down).transposed (down)
1098
1099
1100
1101 def test_printer ():
1102     def make_note ():
1103         evc = EventChord()
1104         n = NoteEvent()
1105         evc.append (n)
1106         return n
1107
1108     def make_tup ():
1109         m = SequentialMusic()
1110         m.append (make_note ())
1111         m.append (make_note ())
1112         m.append (make_note ())
1113
1114         
1115         t = TimeScaledMusic ()
1116         t.numerator = 2
1117         t.denominator = 3
1118         t.element = m
1119         return t
1120
1121     m = SequentialMusic ()
1122     m.append (make_tup ())
1123     m.append (make_tup ())
1124     m.append (make_tup ())
1125     
1126     printer = Output_printer()
1127     m.print_ly (printer)
1128     printer.newline ()
1129     
1130 def test_expr ():
1131     m = SequentialMusic()
1132     l = 2  
1133     evc = EventChord()
1134     n = NoteEvent()
1135     n.duration.duration_log = l
1136     n.pitch.step = 1
1137     evc.insert_around (None, n, 0)
1138     m.insert_around (None, evc, 0)
1139
1140     evc = EventChord()
1141     n = NoteEvent()
1142     n.duration.duration_log = l
1143     n.pitch.step = 3
1144     evc.insert_around (None, n, 0)
1145     m.insert_around (None, evc, 0)
1146
1147     evc = EventChord()
1148     n = NoteEvent()
1149     n.duration.duration_log = l
1150     n.pitch.step = 2 
1151     evc.insert_around (None, n, 0)
1152     m.insert_around (None, evc, 0)
1153
1154     evc = ClefChange()
1155     evc.type = 'treble'
1156     m.insert_around (None, evc, 0)
1157
1158     evc = EventChord()
1159     tonic = Pitch ()
1160     tonic.step = 2
1161     tonic.alteration = -2
1162     n = KeySignatureChange()
1163     n.tonic=tonic.copy()
1164     n.scale = [0, 0, -2, 0, 0,-2,-2]
1165     
1166     evc.insert_around (None, n, 0)
1167     m.insert_around (None, evc, 0)
1168
1169     return m
1170
1171
1172 if __name__ == '__main__':
1173     test_printer ()
1174     raise 'bla'
1175     test_pitch()
1176     
1177     expr = test_expr()
1178     expr.set_start (Rational (0))
1179     print expr.ly_expression()
1180     start = Rational (0,4)
1181     stop = Rational (4,2)
1182     def sub(x, start=start, stop=stop):
1183         ok = x.start >= start and x.start +x.get_length() <= stop
1184         return ok
1185     
1186     print expr.lisp_sub_expression(sub)
1187