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