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