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