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