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