]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
use correct left to right ordering in keySignature list
[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': "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             return "\\set Staff.keySignature = #`(%s)" % string.join (alterations, " ")
1472         else:
1473             return ''
1474
1475 class TimeSignatureChange (Music):
1476     def __init__ (self):
1477         Music.__init__ (self)
1478         self.fractions = [4,4]
1479         self.style = None
1480     def format_fraction (self, frac):
1481         if isinstance (frac, list):
1482             l = [self.format_fraction (f) for f in frac]
1483             return "(" + string.join (l, " ") + ")"
1484         else:
1485             return "%s" % frac
1486
1487     def ly_expression (self):
1488         st = ''
1489         # Print out the style if we have ome, but the '() should only be 
1490         # forced for 2/2 or 4/4, since in all other cases we'll get numeric 
1491         # signatures anyway despite the default 'C signature style!
1492         is_common_signature = self.fractions in ([2,2], [4,4], [4,2])
1493         if self.style:
1494             if self.style == "common":
1495                 st = "\\defaultTimeSignature"
1496             elif (self.style != "'()"):
1497                 st = "\\once \\override Staff.TimeSignature #'style = #%s " % self.style
1498             elif (self.style != "'()") or is_common_signature:
1499                 st = "\\numericTimeSignature"
1500
1501         # Easy case: self.fractions = [n,d] => normal \time n/d call:
1502         if len (self.fractions) == 2 and isinstance (self.fractions[0], int):
1503             return st + '\\time %d/%d ' % tuple (self.fractions)
1504         elif self.fractions:
1505             return st + "\\compoundMeter #'%s" % self.format_fraction (self.fractions)
1506         else:
1507             return st + ''
1508     
1509 class ClefChange (Music):
1510     def __init__ (self):
1511         Music.__init__ (self)
1512         self.type = 'G'
1513         self.position = 2
1514         self.octave = 0
1515
1516     def octave_modifier (self):
1517         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1518     def clef_name (self):
1519         return {('G', 2): "treble",
1520                 ('G', 1): "french",
1521                 ('C', 1): "soprano",
1522                 ('C', 2): "mezzosoprano",
1523                 ('C', 3): "alto",
1524                 ('C', 4): "tenor",
1525                 ('C', 5): "baritone",
1526                 ('F', 3): "varbaritone",
1527                 ('F', 4): "bass",
1528                 ('F', 5): "subbass",
1529                 ("percussion", 2): "percussion",
1530                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1531     def ly_expression (self):
1532         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1533
1534     clef_dict = {
1535         "G": ("clefs.G", -2, -6),
1536         "C": ("clefs.C", 0, 0),
1537         "F": ("clefs.F", 2, 6),
1538         }
1539     
1540     def lisp_expression (self):
1541         try:
1542             (glyph, pos, c0) = self.clef_dict[self.type]
1543         except KeyError:
1544             return ""
1545         clefsetting = """
1546         (make-music 'SequentialMusic
1547         'elements (list
1548    (context-spec-music
1549    (make-property-set 'clefGlyph "%s") 'Staff)
1550    (context-spec-music
1551    (make-property-set 'clefPosition %d) 'Staff)
1552    (context-spec-music
1553    (make-property-set 'middleCPosition %d) 'Staff)))
1554 """ % (glyph, pos, c0)
1555         return clefsetting
1556
1557 class Transposition (Music):
1558     def __init__ (self):
1559         Music.__init__ (self)
1560         self.pitch = None
1561     def ly_expression (self):
1562         self.pitch._force_absolute_pitch = True
1563         return '\\transposition %s' % self.pitch.ly_expression ()
1564
1565 class StaffChange (Music):
1566     def __init__ (self, staff):
1567         Music.__init__ (self)
1568         self.staff = staff
1569     def ly_expression (self):
1570         if self.staff:
1571             return "\\change Staff=\"%s\"" % self.staff
1572         else:
1573             return ''
1574
1575
1576 class TempoMark (Music):
1577     def __init__ (self):
1578         Music.__init__ (self)
1579         self.baseduration = None
1580         self.newduration = None
1581         self.beats = None
1582         self.parentheses = False
1583     def set_base_duration (self, dur):
1584         self.baseduration = dur
1585     def set_new_duration (self, dur):
1586         self.newduration = dur
1587     def set_beats_per_minute (self, beats):
1588         self.beats = beats
1589     def set_parentheses (self, parentheses):
1590         self.parentheses = parentheses
1591     def wait_for_note (self):
1592         return False
1593     def duration_to_markup (self, dur):
1594         if dur:
1595             # Generate the markup to print the note, use scheme mode for 
1596             # ly_expression to get longa and not \longa (which causes an error)
1597             return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
1598         else:
1599             return ''
1600     def tempo_markup_template (self):
1601         return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
1602     def ly_expression (self):
1603         res = ''
1604         if not self.baseduration:
1605             return res
1606         if self.beats:
1607             if self.parentheses:
1608                 res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
1609             else:
1610                 res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
1611         elif self.newduration:
1612             dm = self.duration_to_markup (self.baseduration)
1613             ndm = self.duration_to_markup (self.newduration)
1614             if self.parentheses:
1615                 contents = "\"(\" %s = %s \")\"" % (dm, ndm)
1616             else:
1617                 contents = " %s = %s " % (dm, ndm)
1618             res += self.tempo_markup_template() % contents
1619         else:
1620             return ''
1621         return res
1622
1623 class FiguredBassNote (Music):
1624     def __init__ (self):
1625         Music.__init__ (self)
1626         self.number = ''
1627         self.prefix = ''
1628         self.suffix = ''
1629     def set_prefix (self, prefix):
1630         self.prefix = prefix
1631     def set_suffix (self, suffix):
1632         self.prefix = suffix
1633     def set_number (self, number):
1634         self.number = number
1635     def ly_expression (self):
1636         res = ''
1637         if self.number:
1638             res += self.number
1639         else:
1640             res += '_'
1641         if self.prefix:
1642             res += self.prefix
1643         if self.suffix:
1644             res += self.suffix
1645         return res
1646
1647
1648 class FiguredBassEvent (NestedMusic):
1649     def __init__ (self):
1650         NestedMusic.__init__ (self)
1651         self.duration = None
1652         self.real_duration = 0
1653         self.parentheses = False
1654         return
1655     def set_duration (self, dur):
1656         self.duration = dur
1657     def set_parentheses (self, par):
1658         self.parentheses = par
1659     def set_real_duration (self, dur):
1660         self.real_duration = dur
1661
1662     def print_ly (self, printer):
1663         figured_bass_events = [e for e in self.elements if
1664                isinstance (e, FiguredBassNote)]
1665         if figured_bass_events:
1666           notes = []
1667           for x in figured_bass_events:
1668               notes.append (x.ly_expression ())
1669           contents = string.join (notes)
1670           if self.parentheses:
1671               contents = '[%s]' % contents
1672           printer ('<%s>' % contents)
1673           self.duration.print_ly (printer)
1674
1675
1676 class MultiMeasureRest(Music):
1677
1678     def lisp_expression (self):
1679         return """
1680 (make-music
1681   'MultiMeasureRestMusicGroup
1682   'elements
1683   (list (make-music (quote BarCheck))
1684         (make-music
1685           'ChordEvent
1686           'elements
1687           (list (make-music
1688                   'MultiMeasureRestEvent
1689                   'duration
1690                   %s)))
1691         (make-music (quote BarCheck))))
1692 """ % self.duration.lisp_expression ()
1693
1694     def ly_expression (self):
1695         return 'R%s' % self.duration.ly_expression ()
1696
1697
1698 class StaffGroup:
1699     def __init__ (self, command = "StaffGroup"):
1700         self.stafftype = command
1701         self.id = None
1702         self.instrument_name = None
1703         self.short_instrument_name = None
1704         self.symbol = None
1705         self.spanbar = None
1706         self.children = []
1707         self.is_group = True
1708         # part_information is a list with entries of the form
1709         #     [staffid, voicelist]
1710         # where voicelist is a list with entries of the form
1711         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1712         self.part_information = None
1713
1714     def append_staff (self, staff):
1715         self.children.append (staff)
1716
1717     def set_part_information (self, part_name, staves_info):
1718         if part_name == self.id:
1719             self.part_information = staves_info
1720         else:
1721             for c in self.children:
1722                 c.set_part_information (part_name, staves_info)
1723
1724     def print_ly_contents (self, printer):
1725         for c in self.children:
1726             if c:
1727                 c.print_ly (printer)
1728     def print_ly_overrides (self, printer):
1729         needs_with = False
1730         needs_with |= self.spanbar == "no"
1731         needs_with |= self.instrument_name != None
1732         needs_with |= self.short_instrument_name != None
1733         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1734         if needs_with:
1735             printer.dump ("\\with {")
1736             if self.instrument_name or self.short_instrument_name:
1737                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1738             if self.spanbar == "no":
1739                 printer.dump ("\\override SpanBar #'transparent = ##t")
1740             brack = {"brace": "SystemStartBrace",
1741                      "none": "f",
1742                      "line": "SystemStartSquare"}.get (self.symbol, None)
1743             if brack:
1744                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1745             printer.dump ("}")
1746
1747     def print_ly (self, printer):
1748         if self.stafftype:
1749             printer.dump ("\\new %s" % self.stafftype)
1750         self.print_ly_overrides (printer)
1751         printer.dump ("<<")
1752         printer.newline ()
1753         if self.stafftype and self.instrument_name:
1754             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1755                     escape_instrument_string (self.instrument_name)))
1756             printer.newline ()
1757         if self.stafftype and self.short_instrument_name:
1758             printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1759                     escape_instrument_string (self.short_instrument_name)))
1760             printer.newline ()
1761         self.print_ly_contents (printer)
1762         printer.newline ()
1763         printer.dump (">>")
1764         printer.newline ()
1765
1766
1767 class Staff (StaffGroup):
1768     def __init__ (self, command = "Staff"):
1769         StaffGroup.__init__ (self, command)
1770         self.is_group = False
1771         self.part = None
1772         self.voice_command = "Voice"
1773         self.substafftype = None
1774
1775     def print_ly_overrides (self, printer):
1776         pass
1777
1778     def print_ly_contents (self, printer):
1779         if not self.id or not self.part_information:
1780             return
1781         sub_staff_type = self.substafftype
1782         if not sub_staff_type:
1783             sub_staff_type = self.stafftype
1784
1785         for [staff_id, voices] in self.part_information:
1786             # Chord names need to come before the staff itself!
1787             for [v, lyrics, figuredbass, chordnames] in voices:
1788                 if chordnames:
1789                     printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
1790
1791             # now comes the real staff definition:
1792             if staff_id:
1793                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1794             else:
1795                 printer ('\\context %s << ' % sub_staff_type)
1796             printer.newline ()
1797             n = 0
1798             nr_voices = len (voices)
1799             for [v, lyrics, figuredbass, chordnames] in voices:
1800                 n += 1
1801                 voice_count_text = ''
1802                 if nr_voices > 1:
1803                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1804                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1805                 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1806                 printer.newline ()
1807
1808                 for l in lyrics:
1809                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1810                     printer.newline()
1811                 if figuredbass:
1812                     printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass))
1813             printer ('>>')
1814
1815     def print_ly (self, printer):
1816         if self.part_information and len (self.part_information) > 1:
1817             self.stafftype = "PianoStaff"
1818             self.substafftype = "Staff"
1819         StaffGroup.print_ly (self, printer)
1820
1821 class TabStaff (Staff):
1822     def __init__ (self, command = "TabStaff"):
1823         Staff.__init__ (self, command)
1824         self.string_tunings = []
1825         self.tablature_format = None
1826         self.voice_command = "TabVoice"
1827     def print_ly_overrides (self, printer):
1828         if self.string_tunings or self.tablature_format:
1829             printer.dump ("\\with {")
1830             if self.string_tunings:
1831                 printer.dump ("stringTunings = #'(")
1832                 for i in self.string_tunings:
1833                     printer.dump ("%s" % i.semitones ())
1834                 printer.dump (")")
1835             if self.tablature_format:
1836                 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1837             printer.dump ("}")
1838
1839
1840 class DrumStaff (Staff):
1841     def __init__ (self, command = "DrumStaff"):
1842         Staff.__init__ (self, command)
1843         self.drum_style_table = None
1844         self.voice_command = "DrumVoice"
1845     def print_ly_overrides (self, printer):
1846         if self.drum_style_table:
1847             printer.dump ("\with {")
1848             printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1849             printer.dump ("}")
1850
1851 class RhythmicStaff (Staff):
1852     def __init__ (self, command = "RhythmicStaff"):
1853         Staff.__init__ (self, command)
1854         
1855 class Score:
1856     def __init__ (self):
1857         self.contents = None
1858         self.create_midi = False
1859
1860     def set_contents (self, contents):
1861         self.contents = contents
1862     
1863     def set_part_information (self, part_id, staves_info):
1864         if self.contents:
1865           self.contents.set_part_information (part_id, staves_info)
1866
1867     def print_ly (self, printer):
1868         printer.dump ("\\score {");
1869         printer.newline ()
1870         if self.contents:
1871             self.contents.print_ly (printer);
1872         printer.dump ("\\layout {}");
1873         printer.newline ()
1874         if not self.create_midi:
1875             printer.dump ("% To create MIDI output, uncomment the following line:");
1876             printer.newline ();
1877             printer.dump ("% ");
1878         printer.dump ("\\midi {}");
1879         printer.newline ()
1880         printer.dump ("}");
1881         printer.newline ()
1882
1883
1884 def test_pitch ():
1885     bflat = Pitch()
1886     bflat.alteration = -1
1887     bflat.step =  6
1888     bflat.octave = -1
1889     fifth = Pitch()
1890     fifth.step = 4
1891     down = Pitch ()
1892     down.step = -4
1893     down.normalize ()
1894     
1895     
1896     print bflat.semitones()
1897     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1898     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1899
1900     print bflat.semitones(), 'down'
1901     print bflat.transposed (down)
1902     print bflat.transposed (down).transposed (down)
1903     print bflat.transposed (down).transposed (down).transposed (down)
1904
1905
1906
1907 def test_printer ():
1908     def make_note ():
1909         evc = ChordEvent()
1910         n = NoteEvent()
1911         evc.append (n)
1912         return n
1913
1914     def make_tup ():
1915         m = SequentialMusic()
1916         m.append (make_note ())
1917         m.append (make_note ())
1918         m.append (make_note ())
1919
1920         
1921         t = TimeScaledMusic ()
1922         t.numerator = 2
1923         t.denominator = 3
1924         t.element = m
1925         return t
1926
1927     m = SequentialMusic ()
1928     m.append (make_tup ())
1929     m.append (make_tup ())
1930     m.append (make_tup ())
1931     
1932     printer = Output_printer()
1933     m.print_ly (printer)
1934     printer.newline ()
1935     
1936 def test_expr ():
1937     m = SequentialMusic()
1938     l = 2  
1939     evc = ChordEvent()
1940     n = NoteEvent()
1941     n.duration.duration_log = l
1942     n.pitch.step = 1
1943     evc.insert_around (None, n, 0)
1944     m.insert_around (None, evc, 0)
1945
1946     evc = ChordEvent()
1947     n = NoteEvent()
1948     n.duration.duration_log = l
1949     n.pitch.step = 3
1950     evc.insert_around (None, n, 0)
1951     m.insert_around (None, evc, 0)
1952
1953     evc = ChordEvent()
1954     n = NoteEvent()
1955     n.duration.duration_log = l
1956     n.pitch.step = 2 
1957     evc.insert_around (None, n, 0)
1958     m.insert_around (None, evc, 0)
1959
1960     evc = ClefChange()
1961     evc.type = 'treble'
1962     m.insert_around (None, evc, 0)
1963
1964     evc = ChordEvent()
1965     tonic = Pitch ()
1966     tonic.step = 2
1967     tonic.alteration = -2
1968     n = KeySignatureChange()
1969     n.tonic=tonic.copy()
1970     n.scale = [0, 0, -2, 0, 0,-2,-2]
1971     
1972     evc.insert_around (None, n, 0)
1973     m.insert_around (None, evc, 0)
1974
1975     return m
1976
1977
1978 if __name__ == '__main__':
1979     test_printer ()
1980     raise 'bla'
1981     test_pitch()
1982     
1983     expr = test_expr()
1984     expr.set_start (Rational (0))
1985     print expr.ly_expression()
1986     start = Rational (0,4)
1987     stop = Rational (4,2)
1988     def sub(x, start=start, stop=stop):
1989         ok = x.start >= start and x.start +x.get_length() <= stop
1990         return ok
1991     
1992     print expr.lisp_sub_expression(sub)
1993