]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Support for upbeats and partial/implicit measures
[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], BarCheck)):
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 Lyrics:
454     def __init__ (self):
455         self.lyrics_syllables = []
456
457     def print_ly (self, printer):
458         printer.dump ("\lyricmode {")
459         for l in self.lyrics_syllables:
460             printer.dump ( "%s " % l )
461         printer.dump ("}")
462
463     def ly_expression (self):
464         lstr = "\lyricmode {\n  "
465         for l in self.lyrics_syllables:
466             lstr += l + " "
467         lstr += "\n}"
468         return lstr
469
470
471 class Header:
472     def __init__ (self):
473         self.header_fields = {}
474     def set_field (self, field, value):
475         self.header_fields[field] = value
476
477     def print_ly (self, printer):
478         printer.dump ("\header {")
479         printer.newline ()
480         for (k,v) in self.header_fields.items ():
481             if v:
482                 printer.dump ('%s = %s' % (k,v))
483                 printer.newline ()
484         printer.dump ("}")
485         printer.newline ()
486         printer.newline ()
487
488
489
490
491 class EventChord (NestedMusic):
492     def __init__ (self):
493         NestedMusic.__init__ (self)
494         self.grace_elements = None
495         self.grace_type = None
496     def append_grace (self, element):
497         if element:
498             if not self.grace_elements:
499                 self.grace_elements = SequentialMusic ()
500             self.grace_elements.append (element)
501
502     def get_length (self):
503         l = Rational (0)
504         for e in self.elements:
505             l = max(l, e.get_length())
506         return l
507     
508     def print_ly (self, printer):
509         note_events = [e for e in self.elements if
510                isinstance (e, NoteEvent)]
511
512         rest_events = [e for e in self.elements if
513                isinstance (e, RhythmicEvent)
514                and not isinstance (e, NoteEvent)]
515         
516         other_events = [e for e in self.elements if
517                 not isinstance (e, RhythmicEvent)]
518
519         if self.grace_elements and self.elements:
520             if self.grace_type:
521                 printer ('\\%s' % self.grace_type)
522             else:
523                 printer ('\\grace')
524             # don't print newlines after the { and } braces
525             self.grace_elements.print_ly (printer, False)
526
527         if rest_events:
528             rest_events[0].print_ly (printer)
529         elif len (note_events) == 1:
530             note_events[0].print_ly (printer)
531         elif note_events:
532             pitches = [x.pitch.ly_expression () for x in note_events]
533             printer ('<%s>' % string.join (pitches))
534             note_events[0].duration.print_ly (printer)
535         else:
536             pass
537         
538         for e in other_events:
539             e.print_ly (printer)
540
541         self.print_comment (printer)
542             
543 class Partial (Music):
544     def __init__ (self):
545         Music.__init__ (self)
546         self.partial = None
547     def print_ly (self, printer):
548         if self.partial:
549             printer.dump ("\\partial %s" % self.partial.ly_expression ())
550
551 class BarCheck (Music):
552     def __init__ (self):
553         Music.__init__ (self)
554         self.bar_number = 0
555         
556     def print_ly (self, printer):
557         if self.bar_number > 0 and (self.bar_number % 10) == 0:
558             printer.dump ("|  \\barNumberCheck #%d " % self.bar_number)
559             printer.newline ()
560         else:
561             printer.dump ("| ")
562             printer.print_verbatim (' %% %d' % self.bar_number)
563             printer.newline ()
564  
565
566     def ly_expression (self):
567         return " | "
568
569 class Event(Music):
570     pass
571
572 class SpanEvent (Event):
573     def __init__ (self):
574         Event.__init__ (self)
575         self.span_direction = 0 # start/stop
576         self.line_type = 'solid'
577         self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
578         self.size = 0 # size of e.g. ocrave shift
579     def wait_for_note (self):
580         return True
581     def get_properties(self):
582         return "'span-direction  %d" % self.span_direction
583     def set_span_type (self, type):
584         self.span_type = type
585
586 class SlurEvent (SpanEvent):
587     def ly_expression (self):
588         before = ''
589         after = ''
590         # TODO: setting dashed/dotted line style does not work, because that
591         #       command needs to be written before the note, not when the
592         #       event is observed after the note!
593         #before = {'dotted': '\\slurDotted', 
594         #          'dashed' : '\\slurDashed'}.get (self.line_type, '')
595         #if before:
596             #after = '\\slurSolid'
597
598         return {-1: before + '(' + after,
599             1:')'}.get (self.span_direction, '')
600
601 class BeamEvent (SpanEvent):
602     def ly_expression (self):
603         return {-1: '[',
604             1:']'}.get (self.span_direction, '')
605
606 class PedalEvent (SpanEvent):
607     def ly_expression (self):
608         return {-1: '\\sustainDown',
609             1:'\\sustainUp'}.get (self.span_direction, '')
610
611 # type==-1 means octave up, type==-2 means octave down
612 class OctaveShiftEvent (SpanEvent):
613     def wait_for_note (self):
614         return False;
615     def set_span_type (self, type):
616         self.span_type = {'up': 1, 'down': -1}.get (type, 0)
617     def ly_octave_shift_indicator (self):
618         # convert 8/15 to lilypond indicators (+-1/+-2)
619         value = {8: 1, 15: 2}.get (self.size, 0)
620         # negative values go up!
621         value *= -1*self.span_type
622         return value
623     def ly_expression (self):
624         dir = self.ly_octave_shift_indicator ()
625         value = ''
626         if dir:
627             value = '#(set-octavation %s)' % dir
628         return { 
629             -1: value,
630             1: '#(set-octavation 0)'}.get (self.span_direction, '')
631
632 class TrillSpanEvent (SpanEvent):
633     def ly_expression (self):
634         return {-1: '\\startTrillSpan',
635             0: '', # no need to write out anything for type='continue'
636             1:'\\stopTrillSpan'}.get (self.span_direction, '')
637
638 class GlissandoEvent (SpanEvent):
639     def ly_expression (self):
640         style = ''
641         # TODO: wavy-line glissandos don't work, becasue the style has to be
642         #       set before the note, at the \glissando it's already too late!
643         #if self.line_type == 'wavy':
644             #style = "\once\override Glissando #'style = #'zigzag"
645         # In lilypond, glissando is NOT a spanner, unlike MusicXML.
646         return {-1: style + '\\glissando',
647             1:''}.get (self.span_direction, '')
648
649 class ArpeggioEvent(Event):
650     def wait_for_note (self):
651         return True;
652     def ly_expression (self):
653         return ('\\arpeggio')
654
655
656 class TieEvent(Event):
657     def ly_expression (self):
658         return '~'
659
660
661 class HairpinEvent (SpanEvent):
662     def set_span_type (self, type):
663         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
664     def hairpin_to_ly (self):
665         if self.span_direction == 1:
666             return '\!'
667         else:
668             return {1: '\<', -1: '\>'}.get (self.span_type, '')
669     
670     def ly_expression (self):
671         return self.hairpin_to_ly ()
672     
673     def print_ly (self, printer):
674         val = self.hairpin_to_ly ()
675         if val:
676             printer.dump (val)
677
678
679
680 class DynamicsEvent (Event):
681     def __init__ (self):
682         self.type = None
683         self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p", 
684                                     "mp", "mf", 
685                                     "f", "ff", "fff", "ffff", 
686                                     "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
687     def wait_for_note (self):
688         return True;
689     def ly_expression (self):
690         if self.type == None:
691             return;
692         elif self.type in self.available_commands:
693             return '\%s' % self.type
694         else:
695             return '-\markup{ \dynamic %s }' % self.type
696         
697     def print_ly (self, printer):
698         if self.type == None:
699             return
700         elif self.type in self.available_commands:
701             printer.dump ("\\%s" % self.type)
702         else:
703             printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
704
705
706 class ArticulationEvent (Event):
707     def __init__ (self):
708         self.type = None
709         self.force_direction = None
710
711     def direction_mod (self):
712         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
713
714     def ly_expression (self):
715         return '%s\\%s' % (self.direction_mod (), self.type)
716
717 class ShortArticulationEvent (ArticulationEvent):
718     def direction_mod (self):
719         # default is -
720         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
721     def ly_expression (self):
722         return '%s%s' % (self.direction_mod (), self.type)
723
724 class NoDirectionArticulationEvent (ArticulationEvent):
725     def ly_expression (self):
726         return '\\%s' % self.type
727
728 class MarkupEvent (ShortArticulationEvent):
729     def __init__ (self):
730         ArticulationEvent.__init__ (self)
731         self.contents = None
732     def ly_expression (self):
733         if self.contents:
734             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
735
736 class TremoloEvent (ArticulationEvent):
737     def __init__ (self):
738         Event.__init__ (self)
739         self.bars = 0
740
741     def ly_expression (self):
742         str=''
743         if self.bars and self.bars > 0:
744             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
745         return str
746
747 class BendEvent (ArticulationEvent):
748     def __init__ (self):
749         Event.__init__ (self)
750         self.alter = 0
751     def ly_expression (self):
752         if self.alter:
753             return "-\\bendAfter #%s" % self.alter
754         else:
755             return ''
756
757 class RhythmicEvent(Event):
758     def __init__ (self):
759         Event.__init__ (self)
760         self.duration = Duration()
761         
762     def get_length (self):
763         return self.duration.get_length()
764         
765     def get_properties (self):
766         return ("'duration %s"
767                 % self.duration.lisp_expression ())
768     
769 class RestEvent (RhythmicEvent):
770     def __init__ (self):
771         RhythmicEvent.__init__ (self)
772         self.pitch = None
773     def ly_expression (self):
774         if self.pitch:
775             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
776         else:
777             return 'r%s' % self.duration.ly_expression ()
778     
779     def print_ly (self, printer):
780         if self.pitch:
781             self.pitch.print_ly (printer)
782             self.duration.print_ly (printer)
783             printer ('\\rest')
784         else:
785             printer('r')
786             self.duration.print_ly (printer)
787
788 class SkipEvent (RhythmicEvent):
789     def ly_expression (self):
790         return 's%s' % self.duration.ly_expression () 
791
792 class NoteEvent(RhythmicEvent):
793     def  __init__ (self):
794         RhythmicEvent.__init__ (self)
795         self.pitch = None
796         self.drum_type = None
797         self.cautionary = False
798         self.forced_accidental = False
799         
800     def get_properties (self):
801         str = RhythmicEvent.get_properties (self)
802         
803         if self.pitch:
804             str += self.pitch.lisp_expression ()
805         elif self.drum_type:
806             str += "'drum-type '%s" % self.drum_type
807
808         return str
809     
810     def pitch_mods (self):
811         excl_question = ''
812         if self.cautionary:
813             excl_question += '?'
814         if self.forced_accidental:
815             excl_question += '!'
816
817         return excl_question
818     
819     def ly_expression (self):
820         if self.pitch:
821             return '%s%s%s' % (self.pitch.ly_expression (),
822                                self.pitch_mods(),
823                                self.duration.ly_expression ())
824         elif self.drum_type:
825             return '%s%s' (self.drum_type,
826                            self.duration.ly_expression ())
827
828     def print_ly (self, printer):
829         if self.pitch:
830             self.pitch.print_ly (printer)
831             printer (self.pitch_mods ())
832         else:
833             printer (self.drum_type)
834
835         self.duration.print_ly (printer)
836
837 class KeySignatureChange (Music):
838     def __init__ (self):
839         Music.__init__ (self)
840         self.scale = []
841         self.tonic = Pitch()
842         self.mode = 'major'
843         
844     def ly_expression (self):
845         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
846                      self.mode)
847     
848     def lisp_expression (self):
849         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
850         scale_str = ("'(%s)" % string.join (pairs))
851
852         return """ (make-music 'KeyChangeEvent
853      'pitch-alist %s) """ % scale_str
854
855 class TimeSignatureChange (Music):
856     def __init__ (self):
857         Music.__init__ (self)
858         self.fraction = (4,4)
859     def ly_expression (self):
860         return '\\time %d/%d ' % self.fraction
861     
862 class ClefChange (Music):
863     def __init__ (self):
864         Music.__init__ (self)
865         self.type = 'G'
866         self.position = 2
867         self.octave = 0
868
869     def octave_modifier (self):
870         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
871     def clef_name (self):
872         return {('G', 2): "treble",
873                 ('G', 1): "french",
874                 ('C', 1): "soprano",
875                 ('C', 2): "mezzosoprano",
876                 ('C', 3): "alto",
877                 ('C', 4): "tenor",
878                 ('C', 5): "baritone",
879                 ('F', 3): "varbaritone",
880                 ('F', 4): "bass",
881                 ('F', 5): "subbass",
882                 ("percussion", 2): "percussion",
883                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
884     def ly_expression (self):
885         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
886
887     clef_dict = {
888         "G": ("clefs.G", -2, -6),
889         "C": ("clefs.C", 0, 0),
890         "F": ("clefs.F", 2, 6),
891         }
892     
893     def lisp_expression (self):
894         try:
895             (glyph, pos, c0) = self.clef_dict[self.type]
896         except KeyError:
897             return ""
898         clefsetting = """
899         (make-music 'SequentialMusic
900         'elements (list
901    (context-spec-music
902    (make-property-set 'clefGlyph "%s") 'Staff)
903    (context-spec-music
904    (make-property-set 'clefPosition %d) 'Staff)
905    (context-spec-music
906    (make-property-set 'middleCPosition %d) 'Staff)))
907 """ % (glyph, pos, c0)
908         return clefsetting
909
910
911 class MultiMeasureRest(Music):
912
913     def lisp_expression (self):
914         return """
915 (make-music
916   'MultiMeasureRestMusicGroup
917   'elements
918   (list (make-music (quote BarCheck))
919         (make-music
920           'EventChord
921           'elements
922           (list (make-music
923                   'MultiMeasureRestEvent
924                   'duration
925                   %s)))
926         (make-music (quote BarCheck))))
927 """ % self.duration.lisp_expression ()
928
929     def ly_expression (self):
930         return 'R%s' % self.duration.ly_expression ()
931
932
933 class StaffGroup:
934     def __init__ (self, command = "StaffGroup"):
935         self.stafftype = command
936         self.id = None
937         self.instrument_name = None
938         self.short_instrument_name = None
939         self.symbol = None
940         self.spanbar = None
941         self.children = []
942         # part_information is a list with entries of the form
943         #     [staffid, voicelist]
944         # where voicelist is a list with entries of the form
945         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
946         self.part_information = None
947
948     def appendStaff (self, staff):
949         self.children.append (staff)
950
951     def setPartInformation (self, part_name, staves_info):
952         if part_name == self.id:
953             self.part_information = staves_info
954         else:
955             for c in self.children:
956                 c.setPartInformation (part_name, staves_info)
957
958     def print_ly_contents (self, printer):
959         for c in self.children:
960             if c:
961                 c.print_ly (printer)
962     def print_ly_overrides (self, printer):
963         needs_with = False
964         needs_with |= self.spanbar == "no"
965         needs_with |= self.instrument_name != None
966         needs_with |= self.short_instrument_name != None
967         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
968         if needs_with:
969             printer.dump ("\\with {")
970             if self.instrument_name or self.short_instrument_name:
971                 printer.dump ("\\consists \"Instrument_name_engraver\"")
972             if self.spanbar == "no":
973                 printer.dump ("\\override SpanBar #'transparent = ##t")
974             brack = {"brace": "SystemStartBrace",
975                      "none": "f",
976                      "line": "SystemStartBar"}.get (self.symbol, None)
977             if brack:
978                 printer.dump ("systemStartDelimiter = #'%s" % brack)
979             printer.dump ("}")
980
981     def print_ly (self, printer):
982         if self.stafftype:
983             printer.dump ("\\new %s" % self.stafftype)
984         self.print_ly_overrides (printer)
985         printer.dump ("<<")
986         printer.newline ()
987         if self.stafftype and self.instrument_name:
988             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
989                     escape_instrument_string (self.instrument_name)))
990             printer.newline ()
991         if self.stafftype and self.short_instrument_name:
992             printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype, 
993                     escape_instrument_string (self.short_instrument_name)))
994             printer.newline ()
995         self.print_ly_contents (printer)
996         printer.newline ()
997         printer.dump (">>")
998         printer.newline ()
999
1000
1001 class Staff (StaffGroup):
1002     def __init__ (self):
1003         StaffGroup.__init__ (self, "Staff")
1004         self.part = None
1005
1006     def print_ly_overrides (self, printer):
1007         pass
1008
1009     def print_ly_contents (self, printer):
1010         if not self.id or not self.part_information:
1011             return
1012
1013         for [staff_id, voices] in self.part_information:
1014             if staff_id:
1015                 printer ('\\context Staff = "%s" << ' % staff_id)
1016             else:
1017                 printer ('\\context Staff << ')
1018             printer.newline ()
1019             n = 0
1020             nr_voices = len (voices)
1021             for [v, lyrics] in voices:
1022                 n += 1
1023                 voice_count_text = ''
1024                 if nr_voices > 1:
1025                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1026                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1027                 printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
1028                 printer.newline ()
1029
1030                 for l in lyrics:
1031                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1032                     printer.newline()
1033             printer ('>>')
1034
1035     def print_ly (self, printer):
1036         if self.part_information and len (self.part_information) > 1:
1037             self.stafftype = "PianoStaff"
1038         StaffGroup.print_ly (self, printer)
1039
1040
1041 def test_pitch ():
1042     bflat = Pitch()
1043     bflat.alteration = -1
1044     bflat.step =  6
1045     bflat.octave = -1
1046     fifth = Pitch()
1047     fifth.step = 4
1048     down = Pitch ()
1049     down.step = -4
1050     down.normalize ()
1051     
1052     
1053     print bflat.semitones()
1054     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1055     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1056
1057     print bflat.semitones(), 'down'
1058     print bflat.transposed (down)
1059     print bflat.transposed (down).transposed (down)
1060     print bflat.transposed (down).transposed (down).transposed (down)
1061
1062
1063
1064 def test_printer ():
1065     def make_note ():
1066         evc = EventChord()
1067         n = NoteEvent()
1068         evc.append (n)
1069         return n
1070
1071     def make_tup ():
1072         m = SequentialMusic()
1073         m.append (make_note ())
1074         m.append (make_note ())
1075         m.append (make_note ())
1076
1077         
1078         t = TimeScaledMusic ()
1079         t.numerator = 2
1080         t.denominator = 3
1081         t.element = m
1082         return t
1083
1084     m = SequentialMusic ()
1085     m.append (make_tup ())
1086     m.append (make_tup ())
1087     m.append (make_tup ())
1088     
1089     printer = Output_printer()
1090     m.print_ly (printer)
1091     printer.newline ()
1092     
1093 def test_expr ():
1094     m = SequentialMusic()
1095     l = 2  
1096     evc = EventChord()
1097     n = NoteEvent()
1098     n.duration.duration_log = l
1099     n.pitch.step = 1
1100     evc.insert_around (None, n, 0)
1101     m.insert_around (None, evc, 0)
1102
1103     evc = EventChord()
1104     n = NoteEvent()
1105     n.duration.duration_log = l
1106     n.pitch.step = 3
1107     evc.insert_around (None, n, 0)
1108     m.insert_around (None, evc, 0)
1109
1110     evc = EventChord()
1111     n = NoteEvent()
1112     n.duration.duration_log = l
1113     n.pitch.step = 2 
1114     evc.insert_around (None, n, 0)
1115     m.insert_around (None, evc, 0)
1116
1117     evc = ClefChange()
1118     evc.type = 'treble'
1119     m.insert_around (None, evc, 0)
1120
1121     evc = EventChord()
1122     tonic = Pitch ()
1123     tonic.step = 2
1124     tonic.alteration = -2
1125     n = KeySignatureChange()
1126     n.tonic=tonic.copy()
1127     n.scale = [0, 0, -2, 0, 0,-2,-2]
1128     
1129     evc.insert_around (None, n, 0)
1130     m.insert_around (None, evc, 0)
1131
1132     return m
1133
1134
1135 if __name__ == '__main__':
1136     test_printer ()
1137     raise 'bla'
1138     test_pitch()
1139     
1140     expr = test_expr()
1141     expr.set_start (Rational (0))
1142     print expr.ly_expression()
1143     start = Rational (0,4)
1144     stop = Rational (4,2)
1145     def sub(x, start=start, stop=stop):
1146         ok = x.start >= start and x.start +x.get_length() <= stop
1147         return ok
1148     
1149     print expr.lisp_sub_expression(sub)
1150