]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Add support for microtone (pitches and key signatures)
[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         value = {8: 1, 15: 2}.get (self.size, 0)
995         # negative values go up!
996         value *= -1*self.span_type
997         return value
998     def ly_expression (self):
999         dir = self.ly_octave_shift_indicator ()
1000         value = ''
1001         if dir:
1002             value = '\ottava #%s' % dir
1003         return { 
1004             -1: value,
1005             1: '\ottava #0'}.get (self.span_direction, '')
1006
1007 class TrillSpanEvent (SpanEvent):
1008     def ly_expression (self):
1009         return {-1: '\\startTrillSpan',
1010             0: '', # no need to write out anything for type='continue'
1011             1:'\\stopTrillSpan'}.get (self.span_direction, '')
1012
1013 class GlissandoEvent (SpanEvent):
1014     def print_before_note (self, printer):
1015         if self.span_direction == -1:
1016             style= {
1017                 "dashed" : "dashed-line",
1018                 "dotted" : "dotted-line",
1019                 "wavy"   : "zigzag" 
1020             }. get (self.line_type, None)
1021             if style:
1022                 printer.dump ("\once \override Glissando #'style = #'%s" % style)
1023     def ly_expression (self):
1024         return {-1: '\\glissando',
1025             1:''}.get (self.span_direction, '')
1026
1027 class ArpeggioEvent(Event):
1028     def __init__ (self):
1029         Event.__init__ (self)
1030         self.direction = 0
1031         self.non_arpeggiate = False
1032     def wait_for_note (self):
1033         return True
1034     def print_before_note (self, printer):
1035         if self.non_arpeggiate:
1036             printer.dump ("\\arpeggioBracket")
1037         else:
1038           dir = { -1: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.get (self.direction, '')
1039           if dir:
1040               printer.dump (dir)
1041     def print_after_note (self, printer):
1042         if self.non_arpeggiate or self.direction:
1043             printer.dump ("\\arpeggioNormal")
1044     def ly_expression (self):
1045         return ('\\arpeggio')
1046
1047
1048 class TieEvent(Event):
1049     def ly_expression (self):
1050         return '~'
1051
1052
1053 class HairpinEvent (SpanEvent):
1054     def set_span_type (self, type):
1055         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
1056     def hairpin_to_ly (self):
1057         if self.span_direction == 1:
1058             return '\!'
1059         else:
1060             return {1: '\<', -1: '\>'}.get (self.span_type, '')
1061     
1062     def ly_expression (self):
1063         return self.hairpin_to_ly ()
1064     
1065     def print_ly (self, printer):
1066         val = self.hairpin_to_ly ()
1067         if val:
1068             printer.dump (val)
1069
1070
1071
1072 class DynamicsEvent (Event):
1073     def __init__ (self):
1074         Event.__init__ (self)
1075         self.type = None
1076     def wait_for_note (self):
1077         return True
1078     def ly_expression (self):
1079         if self.type:
1080             return '\%s' % self.type
1081         else:
1082             return
1083
1084     def print_ly (self, printer):
1085         if self.type:
1086             printer.dump ("\\%s" % self.type)
1087
1088 class MarkEvent (Event):
1089     def __init__ (self, text="\\default"):
1090         Event.__init__ (self)
1091         self.mark = text
1092     def wait_for_note (self):
1093         return False
1094     def ly_contents (self):
1095         if self.mark:
1096             return '%s' % self.mark
1097         else:
1098             return "\"ERROR\""
1099     def ly_expression (self):
1100         return '\\mark %s' % self.ly_contents ()
1101
1102 class MusicGlyphMarkEvent (MarkEvent):
1103     def ly_contents (self):
1104         if self.mark:
1105             return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
1106         else:
1107             return ''
1108
1109
1110 class TextEvent (Event):
1111     def __init__ (self):
1112         Event.__init__ (self)
1113         self.Text = None
1114         self.force_direction = None
1115         self.markup = ''
1116     def wait_for_note (self):
1117         return True
1118
1119     def direction_mod (self):
1120         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1121
1122     def ly_expression (self):
1123         base_string = '%s\"%s\"'
1124         if self.markup:
1125             base_string = '%s\markup{ ' + self.markup + ' {%s} }'
1126         return base_string % (self.direction_mod (), self.text)
1127
1128 class ArticulationEvent (Event):
1129     def __init__ (self):
1130         Event.__init__ (self)
1131         self.type = None
1132         self.force_direction = None
1133     def wait_for_note (self):
1134         return True
1135
1136     def direction_mod (self):
1137         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
1138
1139     def ly_expression (self):
1140         return '%s\\%s' % (self.direction_mod (), self.type)
1141
1142 class ShortArticulationEvent (ArticulationEvent):
1143     def direction_mod (self):
1144         # default is -
1145         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1146     def ly_expression (self):
1147         if self.type:
1148             return '%s%s' % (self.direction_mod (), self.type)
1149         else:
1150             return ''
1151
1152 class NoDirectionArticulationEvent (ArticulationEvent):
1153     def ly_expression (self):
1154         if self.type:
1155             return '\\%s' % self.type
1156         else:
1157             return ''
1158
1159 class MarkupEvent (ShortArticulationEvent):
1160     def __init__ (self):
1161         ArticulationEvent.__init__ (self)
1162         self.contents = None
1163     def ly_expression (self):
1164         if self.contents:
1165             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
1166         else:
1167             return ''
1168
1169 class FretEvent (MarkupEvent):
1170     def __init__ (self):
1171         MarkupEvent.__init__ (self)
1172         self.force_direction = 1
1173         self.strings = 6
1174         self.frets = 4
1175         self.barre = None
1176         self.elements = []
1177     def ly_expression (self):
1178         val = ""
1179         if self.strings <> 6:
1180             val += "w:%s;" % self.strings
1181         if self.frets <> 4:
1182             val += "h:%s;" % self.frets
1183         if self.barre and len (self.barre) >= 3:
1184             val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
1185         have_fingering = False
1186         for i in self.elements:
1187             if len (i) > 1:
1188                 val += "%s-%s" % (i[0], i[1])
1189             if len (i) > 2:
1190                 have_fingering = True
1191                 val += "-%s" % i[2]
1192             val += ";"
1193         if have_fingering:
1194             val = "f:1;" + val
1195         if val:
1196             return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
1197         else:
1198             return ''
1199
1200
1201 class FunctionWrapperEvent (Event):
1202     def __init__ (self, function_name = None):
1203         Event.__init__ (self)
1204         self.function_name = function_name
1205     def pre_note_ly (self, is_chord_element):
1206         if self.function_name:
1207             return "\\%s" % self.function_name
1208         else:
1209             return ''
1210     def pre_chord_ly (self):
1211         return ''
1212     def ly_expression (self):
1213         if self.function_name:
1214             return "\\%s" % self.function_name
1215         else:
1216             return ''
1217
1218 class ParenthesizeEvent (FunctionWrapperEvent):
1219     def __init__ (self):
1220         FunctionWrapperEvent.__init__ (self, "parenthesize")
1221
1222 class NotestyleEvent (Event):
1223     def __init__ (self):
1224         Event.__init__ (self)
1225         self.style = None
1226         self.filled = None
1227     def pre_chord_ly (self):
1228         if self.style:
1229             return "\\once \\override NoteHead #'style = #%s" % self.style
1230         else:
1231             return ''
1232     def pre_note_ly (self, is_chord_element):
1233         if self.style and is_chord_element:
1234             return "\\tweak #'style #%s" % self.style
1235         else:
1236             return ''
1237     def ly_expression (self):
1238         return self.pre_chord_ly ()
1239
1240
1241 class ChordPitch:
1242     def __init__ (self):
1243         self.alteration = 0
1244         self.step = 0
1245     def __repr__(self):
1246         return self.ly_expression()
1247     def ly_expression (self): 
1248         return pitch_generating_function (self)
1249
1250 class ChordModification:
1251     def __init__ (self):
1252         self.alteration = 0
1253         self.step = 0
1254         self.type = 0
1255     def ly_expression (self):
1256         if self.type:
1257             val = {1: ".", -1: "^" }.get (self.type, "")
1258             val += "%s" % self.step
1259             val += {1: "+", -1: "-"}.get (self.alteration, "")
1260             return val
1261         else:
1262             return ''
1263
1264 class ChordNameEvent (Event):
1265     def __init__ (self):
1266         Event.__init__ (self)
1267         self.root = None
1268         self.kind = None
1269         self.duration = None
1270         self.modifications = []
1271         self.bass = None
1272     def add_modification (self, mod):
1273         self.modifications.append (mod)
1274     def ly_expression (self):
1275         if not self.root:
1276             return ''
1277         value = self.root.ly_expression ()
1278         if self.duration:
1279             value += self.duration.ly_expression ()
1280         if self.kind:
1281             value += ":"
1282             value += self.kind
1283         # First print all additions/changes, and only afterwards all subtractions
1284         for m in self.modifications:
1285             if m.type == 1:
1286               value += m.ly_expression ()
1287         for m in self.modifications:
1288             if m.type == -1:
1289               value += m.ly_expression ()
1290         if self.bass:
1291             value += "/+%s" % self.bass.ly_expression ()
1292         return value
1293
1294
1295 class TremoloEvent (ArticulationEvent):
1296     def __init__ (self):
1297         Event.__init__ (self)
1298         self.bars = 0
1299
1300     def ly_expression (self):
1301         str=''
1302         if self.bars and self.bars > 0:
1303             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
1304         return str
1305
1306 class BendEvent (ArticulationEvent):
1307     def __init__ (self):
1308         Event.__init__ (self)
1309         self.alter = None
1310     def ly_expression (self):
1311         if self.alter != None:
1312             return "-\\bendAfter #%s" % self.alter
1313         else:
1314             return ''
1315
1316 class RhythmicEvent(Event):
1317     def __init__ (self):
1318         Event.__init__ (self)
1319         self.duration = Duration()
1320         self.associated_events = []
1321
1322     def add_associated_event (self, ev):
1323         if ev:
1324             self.associated_events.append (ev)
1325
1326     def pre_chord_ly (self):
1327         return [ev.pre_chord_ly () for ev in self.associated_events]
1328
1329     def pre_note_ly (self, is_chord_element):
1330         return [ev.pre_note_ly (is_chord_element) for ev in self.associated_events]
1331
1332     def ly_expression_pre_note (self, is_chord_element):
1333         res = string.join (self.pre_note_ly (is_chord_element), ' ')
1334         if res != '':
1335             res = res + ' '
1336         return res
1337
1338     def get_length (self):
1339         return self.duration.get_length()
1340         
1341     def get_properties (self):
1342         return ("'duration %s"
1343                 % self.duration.lisp_expression ())
1344     
1345 class RestEvent (RhythmicEvent):
1346     def __init__ (self):
1347         RhythmicEvent.__init__ (self)
1348         self.pitch = None
1349
1350     def ly_expression (self):
1351         res = self.ly_expression_pre_note (False)
1352         if self.pitch:
1353             return res + "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1354         else:
1355             return 'r%s' % self.duration.ly_expression ()
1356     
1357     def print_ly (self, printer):
1358         for ev in self.associated_events:
1359             ev.print_ly (printer)
1360         if self.pitch:
1361             self.pitch.print_ly (printer)
1362             self.duration.print_ly (printer)
1363             printer ('\\rest')
1364         else:
1365             printer('r')
1366             self.duration.print_ly (printer)
1367
1368 class SkipEvent (RhythmicEvent):
1369     def ly_expression (self):
1370         return 's%s' % self.duration.ly_expression () 
1371
1372 class NoteEvent(RhythmicEvent):
1373     def  __init__ (self):
1374         RhythmicEvent.__init__ (self)
1375         self.pitch = None
1376         self.drum_type = None
1377         self.cautionary = False
1378         self.forced_accidental = False
1379
1380     def get_properties (self):
1381         str = RhythmicEvent.get_properties (self)
1382         
1383         if self.pitch:
1384             str += self.pitch.lisp_expression ()
1385         elif self.drum_type:
1386             str += "'drum-type '%s" % self.drum_type
1387
1388         return str
1389     
1390     def pitch_mods (self):
1391         excl_question = ''
1392         if self.cautionary:
1393             excl_question += '?'
1394         if self.forced_accidental:
1395             excl_question += '!'
1396
1397         return excl_question
1398
1399     def ly_expression (self):
1400         # obtain all stuff that needs to be printed before the note:
1401         res = self.ly_expression_pre_note (True)
1402         if self.pitch:
1403             return res + '%s%s%s' % (self.pitch.ly_expression (),
1404                                self.pitch_mods(),
1405                                self.duration.ly_expression ())
1406         elif self.drum_type:
1407             return res + '%s%s' (self.drum_type,
1408                            self.duration.ly_expression ())
1409
1410     def chord_element_ly (self):
1411         # obtain all stuff that needs to be printed before the note:
1412         res = self.ly_expression_pre_note (True)
1413         if self.pitch:
1414             return res + '%s%s' % (self.pitch.ly_expression (),
1415                                self.pitch_mods())
1416         elif self.drum_type:
1417             return res + '%s%s' (self.drum_type)
1418
1419
1420     def print_ly (self, printer):
1421         for ev in self.associated_events:
1422             ev.print_ly (printer)
1423         if self.pitch:
1424             self.pitch.print_ly (printer)
1425             printer (self.pitch_mods ())
1426         else:
1427             printer (self.drum_type)
1428
1429         self.duration.print_ly (printer)
1430
1431 class KeySignatureChange (Music):
1432     def __init__ (self):
1433         Music.__init__ (self)
1434         self.tonic = None
1435         self.mode = 'major'
1436         self.non_standard_alterations = None
1437
1438     def format_non_standard_alteration (self, a):
1439         alter_dict = { -2:   ",DOUBLE-FLAT",
1440                        -1.5: ",THREE-Q-FLAT",
1441                        -1:   ",FLAT",
1442                        -0.5: ",SEMI-FLAT",
1443                         0:   ",NATURAL",
1444                         0.5: ",SEMI-SHARP",
1445                         1:   ",SHARP",
1446                         1.5: ",THREE-Q-SHARP",
1447                         2:   ",DOUBLE-SHARP"}
1448         try:
1449             accidental = alter_dict[a[1]]
1450         except KeyError:
1451             warning (_ ("Unable to convert alteration %s to a lilypond expression") % a[1])
1452             return ''
1453         if len (a) == 2:
1454             return "( %s . %s )" % (a[0], accidental)
1455         elif len (a) == 3:
1456             return "(( %s . %s ) . %s )" % (a[2], a[0], accidental)
1457         else:
1458             return ''
1459
1460     def ly_expression (self):
1461         if self.tonic:
1462             return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1463                      self.mode)
1464         elif self.non_standard_alterations:
1465             alterations = [self.format_non_standard_alteration (a) for
1466                                         a in self.non_standard_alterations]
1467             # TODO: Check if the alterations should really be given in reverse
1468             #       order of if that's just a bug in Lilypond. If it's a bug,
1469             #       fix it and remove the following call, otherwise add a
1470             #       proper comment here!
1471             alterations.reverse ()
1472             print "Non-Standard alterations printed out: %s" % alterations
1473             return "\\set Staff.keySignature = #`(%s)" % string.join (alterations, " ")
1474         else:
1475             return ''
1476
1477 class TimeSignatureChange (Music):
1478     def __init__ (self):
1479         Music.__init__ (self)
1480         self.fractions = [4,4]
1481         self.style = None
1482     def ly_expression (self):
1483         st = ''
1484         # Print out the style if we have ome, but the '() should only be 
1485         # forced for 2/2 or 4/4, since in all other cases we'll get numeric 
1486         # signatures anyway despite the default 'C signature style!
1487         is_common_signature = self.fractions in ([2,2], [4,4], [4,2])
1488         if self.style:
1489             if (self.style != "'()") or is_common_signature:
1490                 st = "\\once \\override Staff.TimeSignature #'style = #%s " % self.style
1491
1492         # Easy case: self.fractions = [n,d] => normal \time n/d call:
1493         if len (self.fractions) == 2 and isinstance (self.fractions[0], int):
1494             return st + '\\time %d/%d ' % tuple (self.fractions)
1495         elif self.fractions and not isinstance (self.fractions[0], list):
1496             # TODO: Implement non-standard time-signatures
1497             return st + ''
1498         else:
1499             # TODO: Implement non-standard time-signatures
1500             return st + ''
1501     
1502 class ClefChange (Music):
1503     def __init__ (self):
1504         Music.__init__ (self)
1505         self.type = 'G'
1506         self.position = 2
1507         self.octave = 0
1508
1509     def octave_modifier (self):
1510         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1511     def clef_name (self):
1512         return {('G', 2): "treble",
1513                 ('G', 1): "french",
1514                 ('C', 1): "soprano",
1515                 ('C', 2): "mezzosoprano",
1516                 ('C', 3): "alto",
1517                 ('C', 4): "tenor",
1518                 ('C', 5): "baritone",
1519                 ('F', 3): "varbaritone",
1520                 ('F', 4): "bass",
1521                 ('F', 5): "subbass",
1522                 ("percussion", 2): "percussion",
1523                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1524     def ly_expression (self):
1525         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1526
1527     clef_dict = {
1528         "G": ("clefs.G", -2, -6),
1529         "C": ("clefs.C", 0, 0),
1530         "F": ("clefs.F", 2, 6),
1531         }
1532     
1533     def lisp_expression (self):
1534         try:
1535             (glyph, pos, c0) = self.clef_dict[self.type]
1536         except KeyError:
1537             return ""
1538         clefsetting = """
1539         (make-music 'SequentialMusic
1540         'elements (list
1541    (context-spec-music
1542    (make-property-set 'clefGlyph "%s") 'Staff)
1543    (context-spec-music
1544    (make-property-set 'clefPosition %d) 'Staff)
1545    (context-spec-music
1546    (make-property-set 'middleCPosition %d) 'Staff)))
1547 """ % (glyph, pos, c0)
1548         return clefsetting
1549
1550 class Transposition (Music):
1551     def __init__ (self):
1552         Music.__init__ (self)
1553         self.pitch = None
1554     def ly_expression (self):
1555         self.pitch._force_absolute_pitch = True
1556         return '\\transposition %s' % self.pitch.ly_expression ()
1557
1558 class StaffChange (Music):
1559     def __init__ (self, staff):
1560         Music.__init__ (self)
1561         self.staff = staff
1562     def ly_expression (self):
1563         if self.staff:
1564             return "\\change Staff=\"%s\"" % self.staff
1565         else:
1566             return ''
1567
1568
1569 class TempoMark (Music):
1570     def __init__ (self):
1571         Music.__init__ (self)
1572         self.baseduration = None
1573         self.newduration = None
1574         self.beats = None
1575         self.parentheses = False
1576     def set_base_duration (self, dur):
1577         self.baseduration = dur
1578     def set_new_duration (self, dur):
1579         self.newduration = dur
1580     def set_beats_per_minute (self, beats):
1581         self.beats = beats
1582     def set_parentheses (self, parentheses):
1583         self.parentheses = parentheses
1584     def wait_for_note (self):
1585         return False
1586     def duration_to_markup (self, dur):
1587         if dur:
1588             # Generate the markup to print the note, use scheme mode for 
1589             # ly_expression to get longa and not \longa (which causes an error)
1590             return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
1591         else:
1592             return ''
1593     def tempo_markup_template (self):
1594         return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
1595     def ly_expression (self):
1596         res = ''
1597         if not self.baseduration:
1598             return res
1599         if self.beats:
1600             if self.parentheses:
1601                 res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
1602             else:
1603                 res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
1604         elif self.newduration:
1605             dm = self.duration_to_markup (self.baseduration)
1606             ndm = self.duration_to_markup (self.newduration)
1607             if self.parentheses:
1608                 contents = "\"(\" %s = %s \")\"" % (dm, ndm)
1609             else:
1610                 contents = " %s = %s " % (dm, ndm)
1611             res += self.tempo_markup_template() % contents
1612         else:
1613             return ''
1614         return res
1615
1616 class FiguredBassNote (Music):
1617     def __init__ (self):
1618         Music.__init__ (self)
1619         self.number = ''
1620         self.prefix = ''
1621         self.suffix = ''
1622     def set_prefix (self, prefix):
1623         self.prefix = prefix
1624     def set_suffix (self, suffix):
1625         self.prefix = suffix
1626     def set_number (self, number):
1627         self.number = number
1628     def ly_expression (self):
1629         res = ''
1630         if self.number:
1631             res += self.number
1632         else:
1633             res += '_'
1634         if self.prefix:
1635             res += self.prefix
1636         if self.suffix:
1637             res += self.suffix
1638         return res
1639
1640
1641 class FiguredBassEvent (NestedMusic):
1642     def __init__ (self):
1643         NestedMusic.__init__ (self)
1644         self.duration = None
1645         self.real_duration = 0
1646         self.parentheses = False
1647         return
1648     def set_duration (self, dur):
1649         self.duration = dur
1650     def set_parentheses (self, par):
1651         self.parentheses = par
1652     def set_real_duration (self, dur):
1653         self.real_duration = dur
1654
1655     def print_ly (self, printer):
1656         figured_bass_events = [e for e in self.elements if
1657                isinstance (e, FiguredBassNote)]
1658         if figured_bass_events:
1659           notes = []
1660           for x in figured_bass_events:
1661               notes.append (x.ly_expression ())
1662           contents = string.join (notes)
1663           if self.parentheses:
1664               contents = '[%s]' % contents
1665           printer ('<%s>' % contents)
1666           self.duration.print_ly (printer)
1667
1668
1669 class MultiMeasureRest(Music):
1670
1671     def lisp_expression (self):
1672         return """
1673 (make-music
1674   'MultiMeasureRestMusicGroup
1675   'elements
1676   (list (make-music (quote BarCheck))
1677         (make-music
1678           'ChordEvent
1679           'elements
1680           (list (make-music
1681                   'MultiMeasureRestEvent
1682                   'duration
1683                   %s)))
1684         (make-music (quote BarCheck))))
1685 """ % self.duration.lisp_expression ()
1686
1687     def ly_expression (self):
1688         return 'R%s' % self.duration.ly_expression ()
1689
1690
1691 class StaffGroup:
1692     def __init__ (self, command = "StaffGroup"):
1693         self.stafftype = command
1694         self.id = None
1695         self.instrument_name = None
1696         self.short_instrument_name = None
1697         self.symbol = None
1698         self.spanbar = None
1699         self.children = []
1700         self.is_group = True
1701         # part_information is a list with entries of the form
1702         #     [staffid, voicelist]
1703         # where voicelist is a list with entries of the form
1704         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1705         self.part_information = None
1706
1707     def append_staff (self, staff):
1708         self.children.append (staff)
1709
1710     def set_part_information (self, part_name, staves_info):
1711         if part_name == self.id:
1712             self.part_information = staves_info
1713         else:
1714             for c in self.children:
1715                 c.set_part_information (part_name, staves_info)
1716
1717     def print_ly_contents (self, printer):
1718         for c in self.children:
1719             if c:
1720                 c.print_ly (printer)
1721     def print_ly_overrides (self, printer):
1722         needs_with = False
1723         needs_with |= self.spanbar == "no"
1724         needs_with |= self.instrument_name != None
1725         needs_with |= self.short_instrument_name != None
1726         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1727         if needs_with:
1728             printer.dump ("\\with {")
1729             if self.instrument_name or self.short_instrument_name:
1730                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1731             if self.spanbar == "no":
1732                 printer.dump ("\\override SpanBar #'transparent = ##t")
1733             brack = {"brace": "SystemStartBrace",
1734                      "none": "f",
1735                      "line": "SystemStartSquare"}.get (self.symbol, None)
1736             if brack:
1737                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1738             printer.dump ("}")
1739
1740     def print_ly (self, printer):
1741         if self.stafftype:
1742             printer.dump ("\\new %s" % self.stafftype)
1743         self.print_ly_overrides (printer)
1744         printer.dump ("<<")
1745         printer.newline ()
1746         if self.stafftype and self.instrument_name:
1747             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1748                     escape_instrument_string (self.instrument_name)))
1749             printer.newline ()
1750         if self.stafftype and self.short_instrument_name:
1751             printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1752                     escape_instrument_string (self.short_instrument_name)))
1753             printer.newline ()
1754         self.print_ly_contents (printer)
1755         printer.newline ()
1756         printer.dump (">>")
1757         printer.newline ()
1758
1759
1760 class Staff (StaffGroup):
1761     def __init__ (self, command = "Staff"):
1762         StaffGroup.__init__ (self, command)
1763         self.is_group = False
1764         self.part = None
1765         self.voice_command = "Voice"
1766         self.substafftype = None
1767
1768     def print_ly_overrides (self, printer):
1769         pass
1770
1771     def print_ly_contents (self, printer):
1772         if not self.id or not self.part_information:
1773             return
1774         sub_staff_type = self.substafftype
1775         if not sub_staff_type:
1776             sub_staff_type = self.stafftype
1777
1778         for [staff_id, voices] in self.part_information:
1779             # Chord names need to come before the staff itself!
1780             for [v, lyrics, figuredbass, chordnames] in voices:
1781                 if chordnames:
1782                     printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
1783
1784             # now comes the real staff definition:
1785             if staff_id:
1786                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1787             else:
1788                 printer ('\\context %s << ' % sub_staff_type)
1789             printer.newline ()
1790             n = 0
1791             nr_voices = len (voices)
1792             for [v, lyrics, figuredbass, chordnames] in voices:
1793                 n += 1
1794                 voice_count_text = ''
1795                 if nr_voices > 1:
1796                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1797                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1798                 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1799                 printer.newline ()
1800
1801                 for l in lyrics:
1802                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1803                     printer.newline()
1804                 if figuredbass:
1805                     printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass))
1806             printer ('>>')
1807
1808     def print_ly (self, printer):
1809         if self.part_information and len (self.part_information) > 1:
1810             self.stafftype = "PianoStaff"
1811             self.substafftype = "Staff"
1812         StaffGroup.print_ly (self, printer)
1813
1814 class TabStaff (Staff):
1815     def __init__ (self, command = "TabStaff"):
1816         Staff.__init__ (self, command)
1817         self.string_tunings = []
1818         self.tablature_format = None
1819         self.voice_command = "TabVoice"
1820     def print_ly_overrides (self, printer):
1821         if self.string_tunings or self.tablature_format:
1822             printer.dump ("\\with {")
1823             if self.string_tunings:
1824                 printer.dump ("stringTunings = #'(")
1825                 for i in self.string_tunings:
1826                     printer.dump ("%s" % i.semitones ())
1827                 printer.dump (")")
1828             if self.tablature_format:
1829                 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1830             printer.dump ("}")
1831
1832
1833 class DrumStaff (Staff):
1834     def __init__ (self, command = "DrumStaff"):
1835         Staff.__init__ (self, command)
1836         self.drum_style_table = None
1837         self.voice_command = "DrumVoice"
1838     def print_ly_overrides (self, printer):
1839         if self.drum_style_table:
1840             printer.dump ("\with {")
1841             printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1842             printer.dump ("}")
1843
1844 class RhythmicStaff (Staff):
1845     def __init__ (self, command = "RhythmicStaff"):
1846         Staff.__init__ (self, command)
1847         
1848 class Score:
1849     def __init__ (self):
1850         self.contents = None
1851         self.create_midi = False
1852
1853     def set_contents (self, contents):
1854         self.contents = contents
1855     
1856     def set_part_information (self, part_id, staves_info):
1857         if self.contents:
1858           self.contents.set_part_information (part_id, staves_info)
1859
1860     def print_ly (self, printer):
1861         printer.dump ("\\score {");
1862         printer.newline ()
1863         if self.contents:
1864             self.contents.print_ly (printer);
1865         printer.dump ("\\layout {}");
1866         printer.newline ()
1867         if not self.create_midi:
1868             printer.dump ("% To create MIDI output, uncomment the following line:");
1869             printer.newline ();
1870             printer.dump ("% ");
1871         printer.dump ("\\midi {}");
1872         printer.newline ()
1873         printer.dump ("}");
1874         printer.newline ()
1875
1876
1877 def test_pitch ():
1878     bflat = Pitch()
1879     bflat.alteration = -1
1880     bflat.step =  6
1881     bflat.octave = -1
1882     fifth = Pitch()
1883     fifth.step = 4
1884     down = Pitch ()
1885     down.step = -4
1886     down.normalize ()
1887     
1888     
1889     print bflat.semitones()
1890     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1891     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1892
1893     print bflat.semitones(), 'down'
1894     print bflat.transposed (down)
1895     print bflat.transposed (down).transposed (down)
1896     print bflat.transposed (down).transposed (down).transposed (down)
1897
1898
1899
1900 def test_printer ():
1901     def make_note ():
1902         evc = ChordEvent()
1903         n = NoteEvent()
1904         evc.append (n)
1905         return n
1906
1907     def make_tup ():
1908         m = SequentialMusic()
1909         m.append (make_note ())
1910         m.append (make_note ())
1911         m.append (make_note ())
1912
1913         
1914         t = TimeScaledMusic ()
1915         t.numerator = 2
1916         t.denominator = 3
1917         t.element = m
1918         return t
1919
1920     m = SequentialMusic ()
1921     m.append (make_tup ())
1922     m.append (make_tup ())
1923     m.append (make_tup ())
1924     
1925     printer = Output_printer()
1926     m.print_ly (printer)
1927     printer.newline ()
1928     
1929 def test_expr ():
1930     m = SequentialMusic()
1931     l = 2  
1932     evc = ChordEvent()
1933     n = NoteEvent()
1934     n.duration.duration_log = l
1935     n.pitch.step = 1
1936     evc.insert_around (None, n, 0)
1937     m.insert_around (None, evc, 0)
1938
1939     evc = ChordEvent()
1940     n = NoteEvent()
1941     n.duration.duration_log = l
1942     n.pitch.step = 3
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 = 2 
1950     evc.insert_around (None, n, 0)
1951     m.insert_around (None, evc, 0)
1952
1953     evc = ClefChange()
1954     evc.type = 'treble'
1955     m.insert_around (None, evc, 0)
1956
1957     evc = ChordEvent()
1958     tonic = Pitch ()
1959     tonic.step = 2
1960     tonic.alteration = -2
1961     n = KeySignatureChange()
1962     n.tonic=tonic.copy()
1963     n.scale = [0, 0, -2, 0, 0,-2,-2]
1964     
1965     evc.insert_around (None, n, 0)
1966     m.insert_around (None, evc, 0)
1967
1968     return m
1969
1970
1971 if __name__ == '__main__':
1972     test_printer ()
1973     raise 'bla'
1974     test_pitch()
1975     
1976     expr = test_expr()
1977     expr.set_start (Rational (0))
1978     print expr.ly_expression()
1979     start = Rational (0,4)
1980     stop = Rational (4,2)
1981     def sub(x, start=start, stop=stop):
1982         ok = x.start >= start and x.start +x.get_length() <= stop
1983         return ok
1984     
1985     print expr.lisp_sub_expression(sub)
1986