]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
Make relative-includes configurable with a -d option.
[lilypond.git] / python / musicexp.py
1 import inspect
2 import sys
3 import string
4 import re
5 import lilylib as ly
6
7 _ = ly._
8
9 from rational import Rational
10
11 # Store previously converted pitch for \relative conversion as a global state variable
12 previous_pitch = None
13 relative_pitches = False
14
15 def warning (str):
16     ly.stderr_write ((_ ("warning: %s") % str) + "\n")
17
18
19 def escape_instrument_string (input_string):
20     retstring = string.replace (input_string, "\"", "\\\"")
21     if re.match ('.*[\r\n]+.*', retstring):
22         rx = re.compile (r'[\n\r]+')
23         strings = rx.split (retstring)
24         retstring = "\\markup { \\column { "
25         for s in strings:
26             retstring += "\\line {\"" + s + "\"} "
27         retstring += "} }"
28     else:
29         retstring = "\"" + retstring + "\""
30     return retstring
31
32 class Output_stack_element:
33     def __init__ (self):
34         self.factor = Rational (1)
35     def copy (self):
36         o = Output_stack_element()
37         o.factor = self.factor
38         return o
39
40 class Output_printer:
41
42     """A class that takes care of formatting (eg.: indenting) a
43     Music expression as a .ly file.
44     
45     """
46     
47     def __init__ (self):
48         self._line = ''
49         self._indent = 4
50         self._nesting = 0
51         self._file = sys.stdout
52         self._line_len = 72
53         self._output_state_stack = [Output_stack_element()]
54         self._skipspace = False
55         self._last_duration = None
56
57     def set_file (self, file):
58         self._file = file
59         
60     def dump_version (self):
61         self.newline ()
62         self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
63         self.newline ()
64         
65     def get_indent (self):
66         return self._nesting * self._indent
67     
68     def override (self):
69         last = self._output_state_stack[-1]
70         self._output_state_stack.append (last.copy())
71         
72     def add_factor (self, factor):
73         self.override()
74         self._output_state_stack[-1].factor *=  factor
75
76     def revert (self):
77         del self._output_state_stack[-1]
78         if not self._output_state_stack:
79             raise 'empty'
80
81     def duration_factor (self):
82         return self._output_state_stack[-1].factor
83
84     def print_verbatim (self, str):
85         self._line += str
86
87     def unformatted_output (self, str):
88         # don't indent on \< and indent only once on <<
89         self._nesting += ( str.count ('<') 
90                          - str.count ('\<') - str.count ('<<') 
91                          + str.count ('{') )
92         self._nesting -= ( str.count ('>') - str.count ('\>') - str.count ('>>')
93                                            - str.count ('->') - str.count ('_>')
94                                            - str.count ('^>')
95                          + str.count ('}') )
96         self.print_verbatim (str)
97         
98     def print_duration_string (self, str):
99         if self._last_duration == str:
100             return
101         
102         self.unformatted_output (str)
103                   
104     def add_word (self, str):
105         if (len (str) + 1 + len (self._line) > self._line_len):
106             self.newline()
107             self._skipspace = True
108
109         if not self._skipspace:
110             self._line += ' '
111         self.unformatted_output (str)
112         self._skipspace = False
113         
114     def newline (self):
115         self._file.write (self._line + '\n')
116         self._line = ' ' * self._indent * self._nesting
117         self._skipspace = True
118
119     def skipspace (self):
120         self._skipspace = True
121         
122     def __call__(self, arg):
123         self.dump (arg)
124     
125     def dump (self, str):
126         if self._skipspace:
127             self._skipspace = False
128             self.unformatted_output (str)
129         else:
130             words = string.split (str)
131             for w in words:
132                 self.add_word (w)
133
134
135     def close (self):
136         self.newline ()
137         self._file.close ()
138         self._file = None
139
140
141 class Duration:
142     def __init__ (self):
143         self.duration_log = 0
144         self.dots = 0
145         self.factor = Rational (1)
146         
147     def lisp_expression (self):
148         return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
149                              self.dots,
150                              self.factor.numerator (),
151                              self.factor.denominator ())
152
153
154     def ly_expression (self, factor = None, scheme_mode = False):
155         if not factor:
156             factor = self.factor
157
158         if self.duration_log < 0:
159             if scheme_mode:
160                 longer_dict = {-1: "breve", -2: "longa"}
161             else:
162                 longer_dict = {-1: "\\breve", -2: "\\longa"}
163             str = longer_dict.get (self.duration_log, "1")
164         else:
165             str = '%d' % (1 << self.duration_log)
166         str += '.'*self.dots
167
168         if factor <> Rational (1,1):
169             if factor.denominator () <> 1:
170                 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
171             else:
172                 str += '*%d' % factor.numerator ()
173
174         return str
175     
176     def print_ly (self, outputter):
177         str = self.ly_expression (self.factor / outputter.duration_factor ())
178         outputter.print_duration_string (str)
179         
180     def __repr__(self):
181         return self.ly_expression()
182         
183     def copy (self):
184         d = Duration ()
185         d.dots = self.dots
186         d.duration_log = self.duration_log
187         d.factor = self.factor
188         return d
189
190     def get_length (self):
191         dot_fact = Rational( (1 << (1 + self.dots))-1,
192                              1 << self.dots)
193
194         log = abs (self.duration_log)
195         dur = 1 << log
196         if self.duration_log < 0:
197             base = Rational (dur)
198         else:
199             base = Rational (1, dur)
200
201         return base * dot_fact * self.factor
202
203
204 # Implement the different note names for the various languages
205 def pitch_generic (pitch, notenames, accidentals):
206     str = notenames[pitch.step]
207     halftones = int (pitch.alteration)
208     if halftones < 0:
209         str += accidentals[0] * (-halftones)
210     elif pitch.alteration > 0:
211         str += accidentals[3] * (halftones)
212     # Handle remaining fraction to pitch.alteration (for microtones)
213     if (halftones != pitch.alteration):
214         if None in accidentals[1:3]:
215             warning (_ ("Language does not support microtones contained in the piece"))
216         else:
217             try:
218                 str += {-0.5: accidentals[1], 0.5: accidentals[2]}[pitch.alteration-halftones]
219             except KeyError:
220                 warning (_ ("Language does not support microtones contained in the piece"))
221     return str
222
223 def pitch_general (pitch):
224     str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['es', 'eh', 'ih', 'is'])
225     return str.replace ('aes', 'as').replace ('ees', 'es')
226
227 def pitch_nederlands (pitch):
228     return pitch_general (pitch)
229
230 def pitch_english (pitch):
231     str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['f', 'qf', 'qs', 's'])
232     return str.replace ('aes', 'as').replace ('ees', 'es')
233
234 def pitch_deutsch (pitch):
235     str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['es', 'eh', 'ih', 'is'])
236     return str.replace ('hes', 'b').replace ('aes', 'as').replace ('ees', 'es')
237
238 def pitch_norsk (pitch):
239     return pitch_deutsch (pitch)
240
241 def pitch_svenska (pitch):
242     str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', None, None, 'iss'])
243     return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es')
244
245 def pitch_italiano (pitch):
246     str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd'])
247     return str
248
249 def pitch_catalan (pitch):
250     return pitch_italiano (pitch)
251
252 def pitch_espanol (pitch):
253     str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 's'])
254     return str
255
256 def pitch_vlaams (pitch):
257     str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 'k'])
258     return str
259
260 def set_pitch_language (language):
261     global pitch_generating_function
262     function_dict = {
263         "nederlands": pitch_nederlands,
264         "english": pitch_english,
265         "deutsch": pitch_deutsch,
266         "norsk": pitch_norsk,
267         "svenska": pitch_svenska,
268         "italiano": pitch_italiano,
269         "catalan": pitch_catalan,
270         "espanol": pitch_espanol,
271         "vlaams": pitch_vlaams}
272     pitch_generating_function = function_dict.get (language, pitch_general)
273
274 # global variable to hold the formatting function.
275 pitch_generating_function = pitch_general
276
277
278 class Pitch:
279     def __init__ (self):
280         self.alteration = 0
281         self.step = 0
282         self.octave = 0
283         self._force_absolute_pitch = False
284         
285     def __repr__(self):
286         return self.ly_expression()
287
288     def transposed (self, interval):
289         c = self.copy ()
290         c.alteration  += interval.alteration
291         c.step += interval.step
292         c.octave += interval.octave
293         c.normalize ()
294         
295         target_st = self.semitones()  + interval.semitones()
296         c.alteration += target_st - c.semitones()
297         return c
298
299     def normalize (c):
300         while c.step < 0:
301             c.step += 7
302             c.octave -= 1
303         c.octave += c.step / 7
304         c.step = c.step  % 7
305
306     def lisp_expression (self):
307         return '(ly:make-pitch %d %d %d)' % (self.octave,
308                                              self.step,
309                                              self.alteration)
310
311     def copy (self):
312         p = Pitch ()
313         p.alteration = self.alteration
314         p.step = self.step
315         p.octave = self.octave 
316         return p
317
318     def steps (self):
319         return self.step + self.octave *7
320
321     def semitones (self):
322         return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
323     
324     def ly_step_expression (self): 
325         return pitch_generating_function (self)
326
327     def absolute_pitch (self):
328         if self.octave >= 0:
329             return "'" * (self.octave + 1)
330         elif self.octave < -1:
331             return "," * (-self.octave - 1)
332         else:
333             return ''
334
335     def relative_pitch (self):
336         global previous_pitch
337         if not previous_pitch:
338             previous_pitch = self
339             return self.absolute_pitch ()
340         previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step
341         this_pitch_steps = self.octave * 7 + self.step
342         pitch_diff = (this_pitch_steps - previous_pitch_steps)
343         previous_pitch = self
344         if pitch_diff > 3:
345             return "'" * ((pitch_diff + 3) / 7)
346         elif pitch_diff < -3:
347             return "," * ((-pitch_diff + 3) / 7)
348         else:
349             return ""
350
351     def ly_expression (self):
352         str = self.ly_step_expression ()
353         if relative_pitches and not self._force_absolute_pitch:
354             str += self.relative_pitch ()
355         else:
356             str += self.absolute_pitch ()
357
358         return str
359     
360     def print_ly (self, outputter):
361         outputter (self.ly_expression())
362     
363 class Music:
364     def __init__ (self):
365         self.parent = None
366         self.start = Rational (0)
367         self.comment = ''
368         self.identifier = None
369         
370     def get_length(self):
371         return Rational (0)
372     
373     def get_properties (self):
374         return ''
375     
376     def has_children (self):
377         return False
378     
379     def get_index (self):
380         if self.parent:
381             return self.parent.elements.index (self)
382         else:
383             return None
384     def name (self):
385         return self.__class__.__name__
386     
387     def lisp_expression (self):
388         name = self.name()
389
390         props = self.get_properties ()
391         
392         return "(make-music '%s %s)" % (name,  props)
393
394     def set_start (self, start):
395         self.start = start
396
397     def find_first (self, predicate):
398         if predicate (self):
399             return self
400         return None
401
402     def print_comment (self, printer, text = None):
403         if not text:
404             text = self.comment
405
406         if not text:
407             return
408             
409         if text == '\n':
410             printer.newline ()
411             return
412         
413         lines = string.split (text, '\n')
414         for l in lines:
415             if l:
416                 printer.unformatted_output ('% ' + l)
417             printer.newline ()
418             
419
420     def print_with_identifier (self, printer):
421         if self.identifier: 
422             printer ("\\%s" % self.identifier)
423         else:
424             self.print_ly (printer)
425
426     def print_ly (self, printer):
427         printer (self.ly_expression ())
428
429 class MusicWrapper (Music):
430     def __init__ (self):
431         Music.__init__(self)
432         self.element = None
433     def print_ly (self, func):
434         self.element.print_ly (func)
435
436 class ModeChangingMusicWrapper (MusicWrapper):
437     def __init__ (self):
438         MusicWrapper.__init__ (self)
439         self.mode = 'notemode'
440
441     def print_ly (self, func):
442         func ('\\%s' % self.mode)
443         MusicWrapper.print_ly (self, func)
444
445 class RelativeMusic (MusicWrapper):
446     def __init__ (self):
447         MusicWrapper.__init__ (self)
448         self.basepitch = None
449
450     def print_ly (self, func):
451         global previous_pitch
452         global relative_pitches
453         prev_relative_pitches = relative_pitches
454         relative_pitches = True
455         previous_pitch = self.basepitch
456         if not previous_pitch:
457             previous_pitch = Pitch ()
458         func ('\\relative %s%s' % (pitch_generating_function (previous_pitch), 
459                                    previous_pitch.absolute_pitch ()))
460         MusicWrapper.print_ly (self, func)
461         relative_pitches = prev_relative_pitches
462
463 class TimeScaledMusic (MusicWrapper):
464     def __init__ (self):
465         MusicWrapper.__init__ (self)
466         self.numerator = 1
467         self.denominator = 1
468         self.display_number = "actual" # valid values "actual" | "both" | None
469         # Display the basic note length for the tuplet:
470         self.display_type = None       # value values "actual" | "both" | None
471         self.display_bracket = "bracket" # valid values "bracket" | "curved" | None
472         self.actual_type = None   # The actually played unit of the scaling
473         self.normal_type = None   # The basic unit of the scaling
474         self.display_numerator = None
475         self.display_denominator = None
476
477     def print_ly (self, func):
478         if self.display_bracket == None:
479             func ("\\once \\override TupletBracket #'stencil = ##f")
480             func.newline ()
481         elif self.display_bracket == "curved":
482             warning (_ ("Tuplet brackets of curved shape are not correctly implemented"))
483             func ("\\once \\override TupletBracket #'stencil = #ly:slur::print")
484             func.newline ()
485
486         base_number_function = {None: "#f", 
487              "actual": "tuplet-number::calc-denominator-text", 
488              "both": "tuplet-number::calc-fraction-text"}.get (self.display_number, None)
489         # If we have non-standard numerator/denominator, use our custom function
490         if self.display_number == "actual" and self.display_denominator:
491             base_number_function = "(tuplet-number::non-default-tuplet-denominator-text %s)" % self.display_denominator
492         elif self.display_number == "both" and (self.display_denominator or self.display_numerator):
493             if self.display_numerator:
494                 num = self.display_numerator
495             else:
496                 num = "#f"
497             if self.display_denominator:
498                 den = self.display_denominator
499             else:
500                 den = "#f"
501             base_number_function = "(tuplet-number::non-default-tuplet-fraction-text %s %s)" % (den, num)
502
503
504         if self.display_type == "actual" and self.normal_type:
505             # Obtain the note duration in scheme-mode, i.e. \longa as \\longa
506             base_duration = self.normal_type.ly_expression (None, True)
507             func ("\\once \\override TupletNumber #'text = #(tuplet-number::append-note-wrapper %s \"%s\")" %
508                 (base_number_function, base_duration))
509             func.newline ()
510         elif self.display_type == "both": # TODO: Implement this using actual_type and normal_type!
511             warning (_ ("Tuplet brackets displaying both note durations are not implemented, using default"))
512             if self.display_number == None:
513                 func ("\\once \\override TupletNumber #'stencil = ##f")
514                 func.newline ()
515             elif self.display_number == "both":
516                 func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
517                 func.newline ()
518         else:
519             if self.display_number == None:
520                 func ("\\once \\override TupletNumber #'stencil = ##f")
521                 func.newline ()
522             elif self.display_number == "both":
523                 func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
524                 func.newline ()
525
526         func ('\\times %d/%d ' %
527            (self.numerator, self.denominator))
528         func.add_factor (Rational (self.numerator, self.denominator))
529         MusicWrapper.print_ly (self, func)
530         func.revert ()
531
532 class NestedMusic(Music):
533     def __init__ (self):
534         Music.__init__ (self)
535         self.elements = []
536
537     def append (self, what):
538         if what:
539             self.elements.append (what)
540             
541     def has_children (self):
542         return self.elements
543
544     def insert_around (self, succ, elt, dir):
545         assert elt.parent == None
546         assert succ == None or succ in self.elements
547
548         
549         idx = 0
550         if succ:
551             idx = self.elements.index (succ)
552             if dir > 0:
553                 idx += 1
554         else:
555             if dir < 0:
556                 idx = 0
557             elif dir > 0:
558                 idx = len (self.elements)
559
560         self.elements.insert (idx, elt)
561         elt.parent = self
562         
563     def get_properties (self):
564         return ("'elements (list %s)"
565             % string.join (map (lambda x: x.lisp_expression(),
566                       self.elements)))
567
568     def get_subset_properties (self, predicate):
569         return ("'elements (list %s)"
570             % string.join (map (lambda x: x.lisp_expression(),
571                       filter ( predicate,  self.elements))))
572     def get_neighbor (self, music, dir):
573         assert music.parent == self
574         idx = self.elements.index (music)
575         idx += dir
576         idx = min (idx, len (self.elements) -1)
577         idx = max (idx, 0)
578
579         return self.elements[idx]
580
581     def delete_element (self, element):
582         assert element in self.elements
583         
584         self.elements.remove (element)
585         element.parent = None
586         
587     def set_start (self, start):
588         self.start = start
589         for e in self.elements:
590             e.set_start (start)
591
592     def find_first (self, predicate):
593         r = Music.find_first (self, predicate)
594         if r:
595             return r
596         
597         for e in self.elements:
598             r = e.find_first (predicate)
599             if r:
600                 return r
601         return None
602         
603 class SequentialMusic (NestedMusic):
604     def get_last_event_chord (self):
605         value = None
606         at = len( self.elements ) - 1
607         while (at >= 0 and
608                not isinstance (self.elements[at], ChordEvent) and
609                not isinstance (self.elements[at], BarLine)):
610             at -= 1
611
612         if (at >= 0 and isinstance (self.elements[at], ChordEvent)):
613             value = self.elements[at]
614         return value
615
616     def print_ly (self, printer, newline = True):
617         printer ('{')
618         if self.comment:
619             self.print_comment (printer)
620
621         if newline:
622             printer.newline()
623         for e in self.elements:
624             e.print_ly (printer)
625
626         printer ('}')
627         if newline:
628             printer.newline()
629             
630     def lisp_sub_expression (self, pred):
631         name = self.name()
632
633
634         props = self.get_subset_properties (pred)
635         
636         return "(make-music '%s %s)" % (name,  props)
637     
638     def set_start (self, start):
639         for e in self.elements:
640             e.set_start (start)
641             start += e.get_length()
642
643 class RepeatedMusic:
644     def __init__ (self):
645         self.repeat_type = "volta"
646         self.repeat_count = 2
647         self.endings = []
648         self.music = None
649     def set_music (self, music):
650         if isinstance (music, Music):
651             self.music = music
652         elif isinstance (music, list):
653             self.music = SequentialMusic ()
654             self.music.elements = music
655         else:
656             warning (_ ("unable to set the music %(music)s for the repeat %(repeat)s") % \
657                             {'music':music, 'repeat':self})
658     def add_ending (self, music):
659         self.endings.append (music)
660     def print_ly (self, printer):
661         printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count))
662         if self.music:
663             self.music.print_ly (printer)
664         else:
665             warning (_ ("encountered repeat without body"))
666             printer.dump ('{}')
667         if self.endings:
668             printer.dump ('\\alternative {')
669             for e in self.endings:
670                 e.print_ly (printer)
671             printer.dump ('}')
672
673
674 class Lyrics:
675     def __init__ (self):
676         self.lyrics_syllables = []
677
678     def print_ly (self, printer):
679         printer.dump ("\lyricmode {")
680         for l in self.lyrics_syllables:
681             printer.dump ( "%s " % l )
682         printer.dump ("}")
683
684     def ly_expression (self):
685         lstr = "\lyricmode {\n  "
686         for l in self.lyrics_syllables:
687             lstr += l + " "
688         lstr += "\n}"
689         return lstr
690
691
692 class Header:
693     def __init__ (self):
694         self.header_fields = {}
695     def set_field (self, field, value):
696         self.header_fields[field] = value
697
698     def print_ly (self, printer):
699         printer.dump ("\header {")
700         printer.newline ()
701         for (k,v) in self.header_fields.items ():
702             if v:
703                 printer.dump ('%s = %s' % (k,v))
704                 printer.newline ()
705         printer.dump ("}")
706         printer.newline ()
707         printer.newline ()
708
709
710 class Paper:
711     def __init__ (self):
712         self.global_staff_size = -1
713         # page size
714         self.page_width = -1
715         self.page_height = -1
716         # page margins
717         self.top_margin = -1
718         self.bottom_margin = -1
719         self.left_margin = -1
720         self.right_margin = -1
721         self.system_left_margin = -1
722         self.system_right_margin = -1
723         self.system_distance = -1
724         self.top_system_distance = -1
725
726     def print_length_field (self, printer, field, value):
727         if value >= 0:
728             printer.dump ("%s = %s\\cm" % (field, value))
729             printer.newline ()
730     def print_ly (self, printer):
731         if self.global_staff_size > 0:
732             printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size)
733             printer.newline ()
734         printer.dump ('\\paper {')
735         printer.newline ()
736         self.print_length_field (printer, "paper-width", self.page_width)
737         self.print_length_field (printer, "paper-height", self.page_height)
738         self.print_length_field (printer, "top-margin", self.top_margin)
739         self.print_length_field (printer, "botton-margin", self.bottom_margin)
740         self.print_length_field (printer, "left-margin", self.left_margin)
741         # TODO: maybe set line-width instead of right-margin?
742         self.print_length_field (printer, "right-margin", self.right_margin)
743         # TODO: What's the corresponding setting for system_left_margin and
744         #        system_right_margin in Lilypond?
745         self.print_length_field (printer, "between-system-space", self.system_distance)
746         self.print_length_field (printer, "page-top-space", self.top_system_distance)
747
748         printer.dump ('}')
749         printer.newline ()
750
751 class Layout:
752     def __init__ (self):
753         self.context_dict = {}
754     def add_context (self, context):
755         if not self.context_dict.has_key (context):
756             self.context_dict[context] = []
757     def set_context_item (self, context, item):
758         self.add_context (context)
759         if not item in self.context_dict[context]:
760             self.context_dict[context].append (item)
761     def print_ly (self, printer):
762         if self.context_dict.items ():
763             printer.dump ('\\layout {')
764             printer.newline ()
765             for (context, defs) in self.context_dict.items ():
766                 printer.dump ('\\context { \\%s' % context)
767                 printer.newline ()
768                 for d in defs:
769                     printer.dump (d)
770                     printer.newline ()
771                 printer.dump ('}')
772                 printer.newline ()
773             printer.dump ('}')
774             printer.newline ()
775
776
777 class ChordEvent (NestedMusic):
778     def __init__ (self):
779         NestedMusic.__init__ (self)
780         self.after_grace_elements = None
781         self.grace_elements = None
782         self.grace_type = None
783     def append_grace (self, element):
784         if element:
785             if not self.grace_elements:
786                 self.grace_elements = SequentialMusic ()
787             self.grace_elements.append (element)
788     def append_after_grace (self, element):
789         if element:
790             if not self.after_grace_elements:
791                 self.after_grace_elements = SequentialMusic ()
792             self.after_grace_elements.append (element)
793
794     def has_elements (self):
795         return [e for e in self.elements if
796                isinstance (e, NoteEvent) or isinstance (e, RestEvent)] != []
797
798
799     def get_length (self):
800         l = Rational (0)
801         for e in self.elements:
802             l = max(l, e.get_length())
803         return l
804
805     def get_duration (self):
806         note_events = [e for e in self.elements if
807                isinstance (e, NoteEvent) or isinstance (e, RestEvent)]
808         if note_events:
809             return note_events[0].duration
810         else:
811             return None
812
813     def print_ly (self, printer):
814         note_events = [e for e in self.elements if
815                isinstance (e, NoteEvent)]
816
817         rest_events = [e for e in self.elements if
818                isinstance (e, RhythmicEvent)
819                and not isinstance (e, NoteEvent)]
820         
821         other_events = [e for e in self.elements if
822                 not isinstance (e, RhythmicEvent)]
823
824         if self.after_grace_elements:
825             printer ('\\afterGrace {')
826
827         if self.grace_elements and self.elements:
828             if self.grace_type:
829                 printer ('\\%s' % self.grace_type)
830             else:
831                 printer ('\\grace')
832             # don't print newlines after the { and } braces
833             self.grace_elements.print_ly (printer, False)
834         elif self.grace_elements: # no self.elements!
835             warning (_ ("Grace note with no following music: %s") % self.grace_elements)
836             if self.grace_type:
837                 printer ('\\%s' % self.grace_type)
838             else:
839                 printer ('\\grace')
840             self.grace_elements.print_ly (printer, False)
841             printer ('{}')
842
843         # Print all overrides and other settings needed by the 
844         # articulations/ornaments before the note
845         for e in other_events:
846             e.print_before_note (printer)
847
848         if rest_events:
849             rest_events[0].print_ly (printer)
850         elif len (note_events) == 1:
851             note_events[0].print_ly (printer)
852         elif note_events:
853             global previous_pitch
854             pitches = []
855             basepitch = None
856             for x in note_events:
857                 pitches.append (x.chord_element_ly ())
858                 if not basepitch:
859                     basepitch = previous_pitch
860             printer ('<%s>' % string.join (pitches))
861             previous_pitch = basepitch
862             duration = self.get_duration ()
863             if duration:
864                 duration.print_ly (printer)
865         else:
866             pass
867         
868         for e in other_events:
869             e.print_ly (printer)
870
871         for e in other_events:
872             e.print_after_note (printer)
873
874         if self.after_grace_elements:
875             printer ('}')
876             self.after_grace_elements.print_ly (printer, False)
877
878         self.print_comment (printer)
879             
880 class Partial (Music):
881     def __init__ (self):
882         Music.__init__ (self)
883         self.partial = None
884     def print_ly (self, printer):
885         if self.partial:
886             printer.dump ("\\partial %s" % self.partial.ly_expression ())
887
888 class BarLine (Music):
889     def __init__ (self):
890         Music.__init__ (self)
891         self.bar_number = 0
892         self.type = None
893         
894     def print_ly (self, printer):
895         bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': ":",
896                        'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
897                        'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
898                        'short': "'", 'none': "" }.get (self.type, None)
899         if bar_symbol <> None:
900             printer.dump ('\\bar "%s"' % bar_symbol)
901         else:
902             printer.dump ("|")
903
904         if self.bar_number > 0 and (self.bar_number % 10) == 0:
905             printer.dump ("\\barNumberCheck #%d " % self.bar_number)
906         elif self.bar_number > 0:
907             printer.print_verbatim (' %% %d' % self.bar_number)
908         printer.newline ()
909
910     def ly_expression (self):
911         return " | "
912
913 class Event(Music):
914     def __init__ (self):
915         # strings to print before the note to which an event is attached.
916         # Ignored for notes etc.
917         self.before_note = None
918         self.after_note = None
919    # print something before the note to which an event is attached, e.g. overrides
920     def print_before_note (self, printer):
921         if self.before_note:
922             printer.dump (self.before_note)
923    # print something after the note to which an event is attached, e.g. resetting
924     def print_after_note (self, printer):
925         if self.after_note:
926             printer.dump (self.after_note)
927     pass
928
929 class SpanEvent (Event):
930     def __init__ (self):
931         Event.__init__ (self)
932         self.span_direction = 0 # start/stop
933         self.line_type = 'solid'
934         self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
935         self.size = 0 # size of e.g. ocrave shift
936     def wait_for_note (self):
937         return True
938     def get_properties(self):
939         return "'span-direction  %d" % self.span_direction
940     def set_span_type (self, type):
941         self.span_type = type
942
943 class SlurEvent (SpanEvent):
944     def print_before_note (self, printer):
945         command = {'dotted': '\\slurDotted', 
946                   'dashed' : '\\slurDashed'}.get (self.line_type, '')
947         if command and self.span_direction == -1:
948             printer.dump (command)
949     def print_after_note (self, printer):
950         # reset non-solid slur types!
951         command = {'dotted': '\\slurSolid', 
952                   'dashed' : '\\slurSolid'}.get (self.line_type, '')
953         if command and self.span_direction == -1:
954             printer.dump (command)
955     def ly_expression (self):
956         return {-1: '(', 1:')'}.get (self.span_direction, '')
957
958 class BeamEvent (SpanEvent):
959     def ly_expression (self):
960         return {-1: '[', 1:']'}.get (self.span_direction, '')
961
962 class PedalEvent (SpanEvent):
963     def ly_expression (self):
964         return {-1: '\\sustainOn',
965             0:'\\sustainOff\\sustainOn',
966             1:'\\sustainOff'}.get (self.span_direction, '')
967
968 class TextSpannerEvent (SpanEvent):
969     def ly_expression (self):
970         return {-1: '\\startTextSpan',
971             1:'\\stopTextSpan'}.get (self.span_direction, '')
972
973 class BracketSpannerEvent (SpanEvent):
974     # Ligature brackets use prefix-notation!!!
975     def print_before_note (self, printer):
976         if self.span_direction == -1:
977             printer.dump ('\[')
978     # the the bracket after the last note
979     def print_after_note (self, printer):
980         if self.span_direction == 1:
981             printer.dump ('\]')
982     # we're printing everything in print_(before|after)_note...
983     def ly_expression (self):
984         return '';
985
986
987 class OctaveShiftEvent (SpanEvent):
988     def wait_for_note (self):
989         return False
990     def set_span_type (self, type):
991         self.span_type = {'up': 1, 'down': -1}.get (type, 0)
992     def ly_octave_shift_indicator (self):
993         # convert 8/15 to lilypond indicators (+-1/+-2)
994         try:
995             value = {8: 1, 15: 2}[self.size]
996         except KeyError:
997             warning (_ ("Invalid octave shift size found: %s. Using no shift.") % self.size)
998             value = 0
999         # negative values go up!
1000         value *= -1*self.span_type
1001         return value
1002     def ly_expression (self):
1003         dir = self.ly_octave_shift_indicator ()
1004         value = ''
1005         if dir:
1006             value = '\ottava #%s' % dir
1007         return { 
1008             -1: value,
1009             1: '\ottava #0'}.get (self.span_direction, '')
1010
1011 class TrillSpanEvent (SpanEvent):
1012     def ly_expression (self):
1013         return {-1: '\\startTrillSpan',
1014             0: '', # no need to write out anything for type='continue'
1015             1:'\\stopTrillSpan'}.get (self.span_direction, '')
1016
1017 class GlissandoEvent (SpanEvent):
1018     def print_before_note (self, printer):
1019         if self.span_direction == -1:
1020             style= {
1021                 "dashed" : "dashed-line",
1022                 "dotted" : "dotted-line",
1023                 "wavy"   : "zigzag" 
1024             }. get (self.line_type, None)
1025             if style:
1026                 printer.dump ("\once \override Glissando #'style = #'%s" % style)
1027     def ly_expression (self):
1028         return {-1: '\\glissando',
1029             1:''}.get (self.span_direction, '')
1030
1031 class ArpeggioEvent(Event):
1032     def __init__ (self):
1033         Event.__init__ (self)
1034         self.direction = 0
1035         self.non_arpeggiate = False
1036     def wait_for_note (self):
1037         return True
1038     def print_before_note (self, printer):
1039         if self.non_arpeggiate:
1040             printer.dump ("\\arpeggioBracket")
1041         else:
1042           dir = { -1: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.get (self.direction, '')
1043           if dir:
1044               printer.dump (dir)
1045     def print_after_note (self, printer):
1046         if self.non_arpeggiate or self.direction:
1047             printer.dump ("\\arpeggioNormal")
1048     def ly_expression (self):
1049         return ('\\arpeggio')
1050
1051
1052 class TieEvent(Event):
1053     def ly_expression (self):
1054         return '~'
1055
1056
1057 class HairpinEvent (SpanEvent):
1058     def set_span_type (self, type):
1059         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
1060     def hairpin_to_ly (self):
1061         if self.span_direction == 1:
1062             return '\!'
1063         else:
1064             return {1: '\<', -1: '\>'}.get (self.span_type, '')
1065     
1066     def ly_expression (self):
1067         return self.hairpin_to_ly ()
1068     
1069     def print_ly (self, printer):
1070         val = self.hairpin_to_ly ()
1071         if val:
1072             printer.dump (val)
1073
1074
1075
1076 class DynamicsEvent (Event):
1077     def __init__ (self):
1078         Event.__init__ (self)
1079         self.type = None
1080     def wait_for_note (self):
1081         return True
1082     def ly_expression (self):
1083         if self.type:
1084             return '\%s' % self.type
1085         else:
1086             return
1087
1088     def print_ly (self, printer):
1089         if self.type:
1090             printer.dump ("\\%s" % self.type)
1091
1092 class MarkEvent (Event):
1093     def __init__ (self, text="\\default"):
1094         Event.__init__ (self)
1095         self.mark = text
1096     def wait_for_note (self):
1097         return False
1098     def ly_contents (self):
1099         if self.mark:
1100             return '%s' % self.mark
1101         else:
1102             return "\"ERROR\""
1103     def ly_expression (self):
1104         return '\\mark %s' % self.ly_contents ()
1105
1106 class MusicGlyphMarkEvent (MarkEvent):
1107     def ly_contents (self):
1108         if self.mark:
1109             return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
1110         else:
1111             return ''
1112
1113
1114 class TextEvent (Event):
1115     def __init__ (self):
1116         Event.__init__ (self)
1117         self.Text = None
1118         self.force_direction = None
1119         self.markup = ''
1120     def wait_for_note (self):
1121         return True
1122
1123     def direction_mod (self):
1124         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1125
1126     def ly_expression (self):
1127         base_string = '%s\"%s\"'
1128         if self.markup:
1129             base_string = '%s\markup{ ' + self.markup + ' {%s} }'
1130         return base_string % (self.direction_mod (), self.text)
1131
1132 class ArticulationEvent (Event):
1133     def __init__ (self):
1134         Event.__init__ (self)
1135         self.type = None
1136         self.force_direction = None
1137     def wait_for_note (self):
1138         return True
1139
1140     def direction_mod (self):
1141         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
1142
1143     def ly_expression (self):
1144         return '%s\\%s' % (self.direction_mod (), self.type)
1145
1146 class ShortArticulationEvent (ArticulationEvent):
1147     def direction_mod (self):
1148         # default is -
1149         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1150     def ly_expression (self):
1151         if self.type:
1152             return '%s%s' % (self.direction_mod (), self.type)
1153         else:
1154             return ''
1155
1156 class NoDirectionArticulationEvent (ArticulationEvent):
1157     def ly_expression (self):
1158         if self.type:
1159             return '\\%s' % self.type
1160         else:
1161             return ''
1162
1163 class MarkupEvent (ShortArticulationEvent):
1164     def __init__ (self):
1165         ArticulationEvent.__init__ (self)
1166         self.contents = None
1167     def ly_expression (self):
1168         if self.contents:
1169             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
1170         else:
1171             return ''
1172
1173 class FretEvent (MarkupEvent):
1174     def __init__ (self):
1175         MarkupEvent.__init__ (self)
1176         self.force_direction = 1
1177         self.strings = 6
1178         self.frets = 4
1179         self.barre = None
1180         self.elements = []
1181     def ly_expression (self):
1182         val = ""
1183         if self.strings <> 6:
1184             val += "w:%s;" % self.strings
1185         if self.frets <> 4:
1186             val += "h:%s;" % self.frets
1187         if self.barre and len (self.barre) >= 3:
1188             val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
1189         have_fingering = False
1190         for i in self.elements:
1191             if len (i) > 1:
1192                 val += "%s-%s" % (i[0], i[1])
1193             if len (i) > 2:
1194                 have_fingering = True
1195                 val += "-%s" % i[2]
1196             val += ";"
1197         if have_fingering:
1198             val = "f:1;" + val
1199         if val:
1200             return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
1201         else:
1202             return ''
1203
1204
1205 class FunctionWrapperEvent (Event):
1206     def __init__ (self, function_name = None):
1207         Event.__init__ (self)
1208         self.function_name = function_name
1209     def pre_note_ly (self, is_chord_element):
1210         if self.function_name:
1211             return "\\%s" % self.function_name
1212         else:
1213             return ''
1214     def pre_chord_ly (self):
1215         return ''
1216     def ly_expression (self):
1217         if self.function_name:
1218             return "\\%s" % self.function_name
1219         else:
1220             return ''
1221
1222 class ParenthesizeEvent (FunctionWrapperEvent):
1223     def __init__ (self):
1224         FunctionWrapperEvent.__init__ (self, "parenthesize")
1225
1226 class NotestyleEvent (Event):
1227     def __init__ (self):
1228         Event.__init__ (self)
1229         self.style = None
1230         self.filled = None
1231     def pre_chord_ly (self):
1232         if self.style:
1233             return "\\once \\override NoteHead #'style = #%s" % self.style
1234         else:
1235             return ''
1236     def pre_note_ly (self, is_chord_element):
1237         if self.style and is_chord_element:
1238             return "\\tweak #'style #%s" % self.style
1239         else:
1240             return ''
1241     def ly_expression (self):
1242         return self.pre_chord_ly ()
1243
1244
1245 class ChordPitch:
1246     def __init__ (self):
1247         self.alteration = 0
1248         self.step = 0
1249     def __repr__(self):
1250         return self.ly_expression()
1251     def ly_expression (self): 
1252         return pitch_generating_function (self)
1253
1254 class ChordModification:
1255     def __init__ (self):
1256         self.alteration = 0
1257         self.step = 0
1258         self.type = 0
1259     def ly_expression (self):
1260         if self.type:
1261             val = {1: ".", -1: "^" }.get (self.type, "")
1262             val += "%s" % self.step
1263             val += {1: "+", -1: "-"}.get (self.alteration, "")
1264             return val
1265         else:
1266             return ''
1267
1268 class ChordNameEvent (Event):
1269     def __init__ (self):
1270         Event.__init__ (self)
1271         self.root = None
1272         self.kind = None
1273         self.duration = None
1274         self.modifications = []
1275         self.bass = None
1276     def add_modification (self, mod):
1277         self.modifications.append (mod)
1278     def ly_expression (self):
1279         if not self.root:
1280             return ''
1281         value = self.root.ly_expression ()
1282         if self.duration:
1283             value += self.duration.ly_expression ()
1284         if self.kind:
1285             value += ":"
1286             value += self.kind
1287         # First print all additions/changes, and only afterwards all subtractions
1288         for m in self.modifications:
1289             if m.type == 1:
1290               value += m.ly_expression ()
1291         for m in self.modifications:
1292             if m.type == -1:
1293               value += m.ly_expression ()
1294         if self.bass:
1295             value += "/+%s" % self.bass.ly_expression ()
1296         return value
1297
1298
1299 class TremoloEvent (ArticulationEvent):
1300     def __init__ (self):
1301         Event.__init__ (self)
1302         self.bars = 0
1303
1304     def ly_expression (self):
1305         str=''
1306         if self.bars and self.bars > 0:
1307             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
1308         return str
1309
1310 class BendEvent (ArticulationEvent):
1311     def __init__ (self):
1312         Event.__init__ (self)
1313         self.alter = None
1314     def ly_expression (self):
1315         if self.alter != None:
1316             return "-\\bendAfter #%s" % self.alter
1317         else:
1318             return ''
1319
1320 class RhythmicEvent(Event):
1321     def __init__ (self):
1322         Event.__init__ (self)
1323         self.duration = Duration()
1324         self.associated_events = []
1325
1326     def add_associated_event (self, ev):
1327         if ev:
1328             self.associated_events.append (ev)
1329
1330     def pre_chord_ly (self):
1331         return [ev.pre_chord_ly () for ev in self.associated_events]
1332
1333     def pre_note_ly (self, is_chord_element):
1334         return [ev.pre_note_ly (is_chord_element) for ev in self.associated_events]
1335
1336     def ly_expression_pre_note (self, is_chord_element):
1337         res = string.join (self.pre_note_ly (is_chord_element), ' ')
1338         if res != '':
1339             res = res + ' '
1340         return res
1341
1342     def get_length (self):
1343         return self.duration.get_length()
1344         
1345     def get_properties (self):
1346         return ("'duration %s"
1347                 % self.duration.lisp_expression ())
1348     
1349 class RestEvent (RhythmicEvent):
1350     def __init__ (self):
1351         RhythmicEvent.__init__ (self)
1352         self.pitch = None
1353
1354     def ly_expression (self):
1355         res = self.ly_expression_pre_note (False)
1356         if self.pitch:
1357             return res + "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1358         else:
1359             return 'r%s' % self.duration.ly_expression ()
1360     
1361     def print_ly (self, printer):
1362         for ev in self.associated_events:
1363             ev.print_ly (printer)
1364         if self.pitch:
1365             self.pitch.print_ly (printer)
1366             self.duration.print_ly (printer)
1367             printer ('\\rest')
1368         else:
1369             printer('r')
1370             self.duration.print_ly (printer)
1371
1372 class SkipEvent (RhythmicEvent):
1373     def ly_expression (self):
1374         return 's%s' % self.duration.ly_expression () 
1375
1376 class NoteEvent(RhythmicEvent):
1377     def  __init__ (self):
1378         RhythmicEvent.__init__ (self)
1379         self.pitch = None
1380         self.drum_type = None
1381         self.cautionary = False
1382         self.forced_accidental = False
1383
1384     def get_properties (self):
1385         str = RhythmicEvent.get_properties (self)
1386         
1387         if self.pitch:
1388             str += self.pitch.lisp_expression ()
1389         elif self.drum_type:
1390             str += "'drum-type '%s" % self.drum_type
1391
1392         return str
1393     
1394     def pitch_mods (self):
1395         excl_question = ''
1396         if self.cautionary:
1397             excl_question += '?'
1398         if self.forced_accidental:
1399             excl_question += '!'
1400
1401         return excl_question
1402
1403     def ly_expression (self):
1404         # obtain all stuff that needs to be printed before the note:
1405         res = self.ly_expression_pre_note (True)
1406         if self.pitch:
1407             return res + '%s%s%s' % (self.pitch.ly_expression (),
1408                                self.pitch_mods(),
1409                                self.duration.ly_expression ())
1410         elif self.drum_type:
1411             return res + '%s%s' (self.drum_type,
1412                            self.duration.ly_expression ())
1413
1414     def chord_element_ly (self):
1415         # obtain all stuff that needs to be printed before the note:
1416         res = self.ly_expression_pre_note (True)
1417         if self.pitch:
1418             return res + '%s%s' % (self.pitch.ly_expression (),
1419                                self.pitch_mods())
1420         elif self.drum_type:
1421             return res + '%s%s' (self.drum_type)
1422
1423
1424     def print_ly (self, printer):
1425         for ev in self.associated_events:
1426             ev.print_ly (printer)
1427         if self.pitch:
1428             self.pitch.print_ly (printer)
1429             printer (self.pitch_mods ())
1430         else:
1431             printer (self.drum_type)
1432
1433         self.duration.print_ly (printer)
1434
1435 class KeySignatureChange (Music):
1436     def __init__ (self):
1437         Music.__init__ (self)
1438         self.tonic = None
1439         self.mode = 'major'
1440         self.non_standard_alterations = None
1441
1442     def format_non_standard_alteration (self, a):
1443         alter_dict = { -2:   ",DOUBLE-FLAT",
1444                        -1.5: ",THREE-Q-FLAT",
1445                        -1:   ",FLAT",
1446                        -0.5: ",SEMI-FLAT",
1447                         0:   ",NATURAL",
1448                         0.5: ",SEMI-SHARP",
1449                         1:   ",SHARP",
1450                         1.5: ",THREE-Q-SHARP",
1451                         2:   ",DOUBLE-SHARP"}
1452         try:
1453             accidental = alter_dict[a[1]]
1454         except KeyError:
1455             warning (_ ("Unable to convert alteration %s to a lilypond expression") % a[1])
1456             return ''
1457         if len (a) == 2:
1458             return "( %s . %s )" % (a[0], accidental)
1459         elif len (a) == 3:
1460             return "(( %s . %s ) . %s )" % (a[2], a[0], accidental)
1461         else:
1462             return ''
1463
1464     def ly_expression (self):
1465         if self.tonic:
1466             return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1467                      self.mode)
1468         elif self.non_standard_alterations:
1469             alterations = [self.format_non_standard_alteration (a) for
1470                                         a in self.non_standard_alterations]
1471             # TODO: Check if the alterations should really be given in reverse
1472             #       order of if that's just a bug in Lilypond. If it's a bug,
1473             #       fix it and remove the following call, otherwise add a
1474             #       proper comment here!
1475             alterations.reverse ()
1476             return "\\set Staff.keySignature = #`(%s)" % string.join (alterations, " ")
1477         else:
1478             return ''
1479
1480 class TimeSignatureChange (Music):
1481     def __init__ (self):
1482         Music.__init__ (self)
1483         self.fractions = [4,4]
1484         self.style = None
1485     def format_fraction (self, frac):
1486         if isinstance (frac, list):
1487             l = [self.format_fraction (f) for f in frac]
1488             return "(" + string.join (l, " ") + ")"
1489         else:
1490             return "%s" % frac
1491
1492     def ly_expression (self):
1493         st = ''
1494         # Print out the style if we have ome, but the '() should only be 
1495         # forced for 2/2 or 4/4, since in all other cases we'll get numeric 
1496         # signatures anyway despite the default 'C signature style!
1497         is_common_signature = self.fractions in ([2,2], [4,4], [4,2])
1498         if self.style:
1499             if self.style == "common":
1500                 st = "\\defaultTimeSignature"
1501             elif (self.style != "'()"):
1502                 st = "\\once \\override Staff.TimeSignature #'style = #%s " % self.style
1503             elif (self.style != "'()") or is_common_signature:
1504                 st = "\\numericTimeSignature"
1505
1506         # Easy case: self.fractions = [n,d] => normal \time n/d call:
1507         if len (self.fractions) == 2 and isinstance (self.fractions[0], int):
1508             return st + '\\time %d/%d ' % tuple (self.fractions)
1509         elif self.fractions:
1510             return st + "\\compoundMeter #'%s" % self.format_fraction (self.fractions)
1511         else:
1512             return st + ''
1513     
1514 class ClefChange (Music):
1515     def __init__ (self):
1516         Music.__init__ (self)
1517         self.type = 'G'
1518         self.position = 2
1519         self.octave = 0
1520
1521     def octave_modifier (self):
1522         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1523     def clef_name (self):
1524         return {('G', 2): "treble",
1525                 ('G', 1): "french",
1526                 ('C', 1): "soprano",
1527                 ('C', 2): "mezzosoprano",
1528                 ('C', 3): "alto",
1529                 ('C', 4): "tenor",
1530                 ('C', 5): "baritone",
1531                 ('F', 3): "varbaritone",
1532                 ('F', 4): "bass",
1533                 ('F', 5): "subbass",
1534                 ("percussion", 2): "percussion",
1535                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1536     def ly_expression (self):
1537         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1538
1539     clef_dict = {
1540         "G": ("clefs.G", -2, -6),
1541         "C": ("clefs.C", 0, 0),
1542         "F": ("clefs.F", 2, 6),
1543         }
1544     
1545     def lisp_expression (self):
1546         try:
1547             (glyph, pos, c0) = self.clef_dict[self.type]
1548         except KeyError:
1549             return ""
1550         clefsetting = """
1551         (make-music 'SequentialMusic
1552         'elements (list
1553    (context-spec-music
1554    (make-property-set 'clefGlyph "%s") 'Staff)
1555    (context-spec-music
1556    (make-property-set 'clefPosition %d) 'Staff)
1557    (context-spec-music
1558    (make-property-set 'middleCPosition %d) 'Staff)))
1559 """ % (glyph, pos, c0)
1560         return clefsetting
1561
1562 class Transposition (Music):
1563     def __init__ (self):
1564         Music.__init__ (self)
1565         self.pitch = None
1566     def ly_expression (self):
1567         self.pitch._force_absolute_pitch = True
1568         return '\\transposition %s' % self.pitch.ly_expression ()
1569
1570 class StaffChange (Music):
1571     def __init__ (self, staff):
1572         Music.__init__ (self)
1573         self.staff = staff
1574     def ly_expression (self):
1575         if self.staff:
1576             return "\\change Staff=\"%s\"" % self.staff
1577         else:
1578             return ''
1579
1580
1581 class TempoMark (Music):
1582     def __init__ (self):
1583         Music.__init__ (self)
1584         self.baseduration = None
1585         self.newduration = None
1586         self.beats = None
1587         self.parentheses = False
1588     def set_base_duration (self, dur):
1589         self.baseduration = dur
1590     def set_new_duration (self, dur):
1591         self.newduration = dur
1592     def set_beats_per_minute (self, beats):
1593         self.beats = beats
1594     def set_parentheses (self, parentheses):
1595         self.parentheses = parentheses
1596     def wait_for_note (self):
1597         return False
1598     def duration_to_markup (self, dur):
1599         if dur:
1600             # Generate the markup to print the note, use scheme mode for 
1601             # ly_expression to get longa and not \longa (which causes an error)
1602             return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
1603         else:
1604             return ''
1605     def tempo_markup_template (self):
1606         return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
1607     def ly_expression (self):
1608         res = ''
1609         if not self.baseduration:
1610             return res
1611         if self.beats:
1612             if self.parentheses:
1613                 res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
1614             else:
1615                 res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
1616         elif self.newduration:
1617             dm = self.duration_to_markup (self.baseduration)
1618             ndm = self.duration_to_markup (self.newduration)
1619             if self.parentheses:
1620                 contents = "\"(\" %s = %s \")\"" % (dm, ndm)
1621             else:
1622                 contents = " %s = %s " % (dm, ndm)
1623             res += self.tempo_markup_template() % contents
1624         else:
1625             return ''
1626         return res
1627
1628 class FiguredBassNote (Music):
1629     def __init__ (self):
1630         Music.__init__ (self)
1631         self.number = ''
1632         self.prefix = ''
1633         self.suffix = ''
1634     def set_prefix (self, prefix):
1635         self.prefix = prefix
1636     def set_suffix (self, suffix):
1637         self.prefix = suffix
1638     def set_number (self, number):
1639         self.number = number
1640     def ly_expression (self):
1641         res = ''
1642         if self.number:
1643             res += self.number
1644         else:
1645             res += '_'
1646         if self.prefix:
1647             res += self.prefix
1648         if self.suffix:
1649             res += self.suffix
1650         return res
1651
1652
1653 class FiguredBassEvent (NestedMusic):
1654     def __init__ (self):
1655         NestedMusic.__init__ (self)
1656         self.duration = None
1657         self.real_duration = 0
1658         self.parentheses = False
1659         return
1660     def set_duration (self, dur):
1661         self.duration = dur
1662     def set_parentheses (self, par):
1663         self.parentheses = par
1664     def set_real_duration (self, dur):
1665         self.real_duration = dur
1666
1667     def print_ly (self, printer):
1668         figured_bass_events = [e for e in self.elements if
1669                isinstance (e, FiguredBassNote)]
1670         if figured_bass_events:
1671           notes = []
1672           for x in figured_bass_events:
1673               notes.append (x.ly_expression ())
1674           contents = string.join (notes)
1675           if self.parentheses:
1676               contents = '[%s]' % contents
1677           printer ('<%s>' % contents)
1678           self.duration.print_ly (printer)
1679
1680
1681 class MultiMeasureRest(Music):
1682
1683     def lisp_expression (self):
1684         return """
1685 (make-music
1686   'MultiMeasureRestMusicGroup
1687   'elements
1688   (list (make-music (quote BarCheck))
1689         (make-music
1690           'ChordEvent
1691           'elements
1692           (list (make-music
1693                   'MultiMeasureRestEvent
1694                   'duration
1695                   %s)))
1696         (make-music (quote BarCheck))))
1697 """ % self.duration.lisp_expression ()
1698
1699     def ly_expression (self):
1700         return 'R%s' % self.duration.ly_expression ()
1701
1702
1703 class StaffGroup:
1704     def __init__ (self, command = "StaffGroup"):
1705         self.stafftype = command
1706         self.id = None
1707         self.instrument_name = None
1708         self.short_instrument_name = None
1709         self.symbol = None
1710         self.spanbar = None
1711         self.children = []
1712         self.is_group = True
1713         # part_information is a list with entries of the form
1714         #     [staffid, voicelist]
1715         # where voicelist is a list with entries of the form
1716         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1717         self.part_information = None
1718
1719     def append_staff (self, staff):
1720         self.children.append (staff)
1721
1722     def set_part_information (self, part_name, staves_info):
1723         if part_name == self.id:
1724             self.part_information = staves_info
1725         else:
1726             for c in self.children:
1727                 c.set_part_information (part_name, staves_info)
1728
1729     def print_ly_contents (self, printer):
1730         for c in self.children:
1731             if c:
1732                 c.print_ly (printer)
1733     def print_ly_overrides (self, printer):
1734         needs_with = False
1735         needs_with |= self.spanbar == "no"
1736         needs_with |= self.instrument_name != None
1737         needs_with |= self.short_instrument_name != None
1738         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1739         if needs_with:
1740             printer.dump ("\\with {")
1741             if self.instrument_name or self.short_instrument_name:
1742                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1743             if self.spanbar == "no":
1744                 printer.dump ("\\override SpanBar #'transparent = ##t")
1745             brack = {"brace": "SystemStartBrace",
1746                      "none": "f",
1747                      "line": "SystemStartSquare"}.get (self.symbol, None)
1748             if brack:
1749                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1750             printer.dump ("}")
1751
1752     def print_ly (self, printer):
1753         if self.stafftype:
1754             printer.dump ("\\new %s" % self.stafftype)
1755         self.print_ly_overrides (printer)
1756         printer.dump ("<<")
1757         printer.newline ()
1758         if self.stafftype and self.instrument_name:
1759             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1760                     escape_instrument_string (self.instrument_name)))
1761             printer.newline ()
1762         if self.stafftype and self.short_instrument_name:
1763             printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1764                     escape_instrument_string (self.short_instrument_name)))
1765             printer.newline ()
1766         self.print_ly_contents (printer)
1767         printer.newline ()
1768         printer.dump (">>")
1769         printer.newline ()
1770
1771
1772 class Staff (StaffGroup):
1773     def __init__ (self, command = "Staff"):
1774         StaffGroup.__init__ (self, command)
1775         self.is_group = False
1776         self.part = None
1777         self.voice_command = "Voice"
1778         self.substafftype = None
1779
1780     def print_ly_overrides (self, printer):
1781         pass
1782
1783     def print_ly_contents (self, printer):
1784         if not self.id or not self.part_information:
1785             return
1786         sub_staff_type = self.substafftype
1787         if not sub_staff_type:
1788             sub_staff_type = self.stafftype
1789
1790         for [staff_id, voices] in self.part_information:
1791             # Chord names need to come before the staff itself!
1792             for [v, lyrics, figuredbass, chordnames] in voices:
1793                 if chordnames:
1794                     printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
1795
1796             # now comes the real staff definition:
1797             if staff_id:
1798                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1799             else:
1800                 printer ('\\context %s << ' % sub_staff_type)
1801             printer.newline ()
1802             n = 0
1803             nr_voices = len (voices)
1804             for [v, lyrics, figuredbass, chordnames] in voices:
1805                 n += 1
1806                 voice_count_text = ''
1807                 if nr_voices > 1:
1808                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1809                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1810                 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1811                 printer.newline ()
1812
1813                 for l in lyrics:
1814                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1815                     printer.newline()
1816                 if figuredbass:
1817                     printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass))
1818             printer ('>>')
1819
1820     def print_ly (self, printer):
1821         if self.part_information and len (self.part_information) > 1:
1822             self.stafftype = "PianoStaff"
1823             self.substafftype = "Staff"
1824         StaffGroup.print_ly (self, printer)
1825
1826 class TabStaff (Staff):
1827     def __init__ (self, command = "TabStaff"):
1828         Staff.__init__ (self, command)
1829         self.string_tunings = []
1830         self.tablature_format = None
1831         self.voice_command = "TabVoice"
1832     def print_ly_overrides (self, printer):
1833         if self.string_tunings or self.tablature_format:
1834             printer.dump ("\\with {")
1835             if self.string_tunings:
1836                 printer.dump ("stringTunings = #'(")
1837                 for i in self.string_tunings:
1838                     printer.dump ("%s" % i.semitones ())
1839                 printer.dump (")")
1840             if self.tablature_format:
1841                 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1842             printer.dump ("}")
1843
1844
1845 class DrumStaff (Staff):
1846     def __init__ (self, command = "DrumStaff"):
1847         Staff.__init__ (self, command)
1848         self.drum_style_table = None
1849         self.voice_command = "DrumVoice"
1850     def print_ly_overrides (self, printer):
1851         if self.drum_style_table:
1852             printer.dump ("\with {")
1853             printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1854             printer.dump ("}")
1855
1856 class RhythmicStaff (Staff):
1857     def __init__ (self, command = "RhythmicStaff"):
1858         Staff.__init__ (self, command)
1859         
1860 class Score:
1861     def __init__ (self):
1862         self.contents = None
1863         self.create_midi = False
1864
1865     def set_contents (self, contents):
1866         self.contents = contents
1867     
1868     def set_part_information (self, part_id, staves_info):
1869         if self.contents:
1870           self.contents.set_part_information (part_id, staves_info)
1871
1872     def print_ly (self, printer):
1873         printer.dump ("\\score {");
1874         printer.newline ()
1875         if self.contents:
1876             self.contents.print_ly (printer);
1877         printer.dump ("\\layout {}");
1878         printer.newline ()
1879         if not self.create_midi:
1880             printer.dump ("% To create MIDI output, uncomment the following line:");
1881             printer.newline ();
1882             printer.dump ("% ");
1883         printer.dump ("\\midi {}");
1884         printer.newline ()
1885         printer.dump ("}");
1886         printer.newline ()
1887
1888
1889 def test_pitch ():
1890     bflat = Pitch()
1891     bflat.alteration = -1
1892     bflat.step =  6
1893     bflat.octave = -1
1894     fifth = Pitch()
1895     fifth.step = 4
1896     down = Pitch ()
1897     down.step = -4
1898     down.normalize ()
1899     
1900     
1901     print bflat.semitones()
1902     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1903     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1904
1905     print bflat.semitones(), 'down'
1906     print bflat.transposed (down)
1907     print bflat.transposed (down).transposed (down)
1908     print bflat.transposed (down).transposed (down).transposed (down)
1909
1910
1911
1912 def test_printer ():
1913     def make_note ():
1914         evc = ChordEvent()
1915         n = NoteEvent()
1916         evc.append (n)
1917         return n
1918
1919     def make_tup ():
1920         m = SequentialMusic()
1921         m.append (make_note ())
1922         m.append (make_note ())
1923         m.append (make_note ())
1924
1925         
1926         t = TimeScaledMusic ()
1927         t.numerator = 2
1928         t.denominator = 3
1929         t.element = m
1930         return t
1931
1932     m = SequentialMusic ()
1933     m.append (make_tup ())
1934     m.append (make_tup ())
1935     m.append (make_tup ())
1936     
1937     printer = Output_printer()
1938     m.print_ly (printer)
1939     printer.newline ()
1940     
1941 def test_expr ():
1942     m = SequentialMusic()
1943     l = 2  
1944     evc = ChordEvent()
1945     n = NoteEvent()
1946     n.duration.duration_log = l
1947     n.pitch.step = 1
1948     evc.insert_around (None, n, 0)
1949     m.insert_around (None, evc, 0)
1950
1951     evc = ChordEvent()
1952     n = NoteEvent()
1953     n.duration.duration_log = l
1954     n.pitch.step = 3
1955     evc.insert_around (None, n, 0)
1956     m.insert_around (None, evc, 0)
1957
1958     evc = ChordEvent()
1959     n = NoteEvent()
1960     n.duration.duration_log = l
1961     n.pitch.step = 2 
1962     evc.insert_around (None, n, 0)
1963     m.insert_around (None, evc, 0)
1964
1965     evc = ClefChange()
1966     evc.type = 'treble'
1967     m.insert_around (None, evc, 0)
1968
1969     evc = ChordEvent()
1970     tonic = Pitch ()
1971     tonic.step = 2
1972     tonic.alteration = -2
1973     n = KeySignatureChange()
1974     n.tonic=tonic.copy()
1975     n.scale = [0, 0, -2, 0, 0,-2,-2]
1976     
1977     evc.insert_around (None, n, 0)
1978     m.insert_around (None, evc, 0)
1979
1980     return m
1981
1982
1983 if __name__ == '__main__':
1984     test_printer ()
1985     raise 'bla'
1986     test_pitch()
1987     
1988     expr = test_expr()
1989     expr.set_start (Rational (0))
1990     print expr.ly_expression()
1991     start = Rational (0,4)
1992     stop = Rational (4,2)
1993     def sub(x, start=start, stop=stop):
1994         ok = x.start >= start and x.start +x.get_length() <= stop
1995         return ok
1996     
1997     print expr.lisp_sub_expression(sub)
1998