]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Implement different styles of tuplet brackets and numbers
[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.display_number = "actual" # valid values "actual" | "both" | None
458         # Display the basic note length for the tuplet:
459         self.display_type = None       # value values "actual" | "both" | None
460         self.display_bracket = "bracket" # valid values "bracket" | "curved" | None
461
462     def print_ly (self, func):
463         if self.display_bracket == None:
464             func ("\\once \\override TupletBracket #'stencil = ##f")
465             func.newline ()
466         elif self.display_bracket == "curved":
467             warning (_ ("Tuplet brackets of curved shape are not correctly implemented"))
468             func ("\\once \\override TupletBracket #'stencil = #ly:slur::print")
469             func.newline ()
470
471         base_number_function = {None: "#f", 
472              "actual": "tuplet-number::calc-denominator-text", 
473              "both": "tuplet-number::calc-fraction-text"}.get (self.display_number, None)
474
475         if self.display_type == "actual":
476             base_duration = "8" # TODO!!!
477             func ("\\once \\override TupletNumber #'text = #(tuplet-number::append-note-wrapper %s \"%s\")" %
478                 (base_number_function, base_duration))
479             func.newline ()
480         elif self.display_type == None:
481             if self.display_number == None:
482                 func ("\\once \\override TupletNumber #'stencil = ##f")
483                 func.newline ()
484             elif self.display_number == "both":
485                 func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
486                 func.newline ()
487         elif self.display_type == "both":
488             warning (_ ("Tuplet brackets displaying both note durations are not implemented, using default"))
489             if self.display_number == None:
490                 func ("\\once \\override TupletNumber #'stencil = ##f")
491                 func.newline ()
492             elif self.display_number == "both":
493                 func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
494                 func.newline ()
495
496         func ('\\times %d/%d ' %
497            (self.numerator, self.denominator))
498         func.add_factor (Rational (self.numerator, self.denominator))
499         MusicWrapper.print_ly (self, func)
500         func.revert ()
501
502 class NestedMusic(Music):
503     def __init__ (self):
504         Music.__init__ (self)
505         self.elements = []
506
507     def append (self, what):
508         if what:
509             self.elements.append (what)
510             
511     def has_children (self):
512         return self.elements
513
514     def insert_around (self, succ, elt, dir):
515         assert elt.parent == None
516         assert succ == None or succ in self.elements
517
518         
519         idx = 0
520         if succ:
521             idx = self.elements.index (succ)
522             if dir > 0:
523                 idx += 1
524         else:
525             if dir < 0:
526                 idx = 0
527             elif dir > 0:
528                 idx = len (self.elements)
529
530         self.elements.insert (idx, elt)
531         elt.parent = self
532         
533     def get_properties (self):
534         return ("'elements (list %s)"
535             % string.join (map (lambda x: x.lisp_expression(),
536                       self.elements)))
537
538     def get_subset_properties (self, predicate):
539         return ("'elements (list %s)"
540             % string.join (map (lambda x: x.lisp_expression(),
541                       filter ( predicate,  self.elements))))
542     def get_neighbor (self, music, dir):
543         assert music.parent == self
544         idx = self.elements.index (music)
545         idx += dir
546         idx = min (idx, len (self.elements) -1)
547         idx = max (idx, 0)
548
549         return self.elements[idx]
550
551     def delete_element (self, element):
552         assert element in self.elements
553         
554         self.elements.remove (element)
555         element.parent = None
556         
557     def set_start (self, start):
558         self.start = start
559         for e in self.elements:
560             e.set_start (start)
561
562     def find_first (self, predicate):
563         r = Music.find_first (self, predicate)
564         if r:
565             return r
566         
567         for e in self.elements:
568             r = e.find_first (predicate)
569             if r:
570                 return r
571         return None
572         
573 class SequentialMusic (NestedMusic):
574     def get_last_event_chord (self):
575         value = None
576         at = len( self.elements ) - 1
577         while (at >= 0 and
578                not isinstance (self.elements[at], ChordEvent) and
579                not isinstance (self.elements[at], BarLine)):
580             at -= 1
581
582         if (at >= 0 and isinstance (self.elements[at], ChordEvent)):
583             value = self.elements[at]
584         return value
585
586     def print_ly (self, printer, newline = True):
587         printer ('{')
588         if self.comment:
589             self.print_comment (printer)
590
591         if newline:
592             printer.newline()
593         for e in self.elements:
594             e.print_ly (printer)
595
596         printer ('}')
597         if newline:
598             printer.newline()
599             
600     def lisp_sub_expression (self, pred):
601         name = self.name()
602
603
604         props = self.get_subset_properties (pred)
605         
606         return "(make-music '%s %s)" % (name,  props)
607     
608     def set_start (self, start):
609         for e in self.elements:
610             e.set_start (start)
611             start += e.get_length()
612
613 class RepeatedMusic:
614     def __init__ (self):
615         self.repeat_type = "volta"
616         self.repeat_count = 2
617         self.endings = []
618         self.music = None
619     def set_music (self, music):
620         if isinstance (music, Music):
621             self.music = music
622         elif isinstance (music, list):
623             self.music = SequentialMusic ()
624             self.music.elements = music
625         else:
626             warning (_ ("unable to set the music %(music)s for the repeat %(repeat)s") % \
627                             {'music':music, 'repeat':self})
628     def add_ending (self, music):
629         self.endings.append (music)
630     def print_ly (self, printer):
631         printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count))
632         if self.music:
633             self.music.print_ly (printer)
634         else:
635             warning (_ ("encountered repeat without body"))
636             printer.dump ('{}')
637         if self.endings:
638             printer.dump ('\\alternative {')
639             for e in self.endings:
640                 e.print_ly (printer)
641             printer.dump ('}')
642
643
644 class Lyrics:
645     def __init__ (self):
646         self.lyrics_syllables = []
647
648     def print_ly (self, printer):
649         printer.dump ("\lyricmode {")
650         for l in self.lyrics_syllables:
651             printer.dump ( "%s " % l )
652         printer.dump ("}")
653
654     def ly_expression (self):
655         lstr = "\lyricmode {\n  "
656         for l in self.lyrics_syllables:
657             lstr += l + " "
658         lstr += "\n}"
659         return lstr
660
661
662 class Header:
663     def __init__ (self):
664         self.header_fields = {}
665     def set_field (self, field, value):
666         self.header_fields[field] = value
667
668     def print_ly (self, printer):
669         printer.dump ("\header {")
670         printer.newline ()
671         for (k,v) in self.header_fields.items ():
672             if v:
673                 printer.dump ('%s = %s' % (k,v))
674                 printer.newline ()
675         printer.dump ("}")
676         printer.newline ()
677         printer.newline ()
678
679
680 class Paper:
681     def __init__ (self):
682         self.global_staff_size = -1
683         # page size
684         self.page_width = -1
685         self.page_height = -1
686         # page margins
687         self.top_margin = -1
688         self.bottom_margin = -1
689         self.left_margin = -1
690         self.right_margin = -1
691         self.system_left_margin = -1
692         self.system_right_margin = -1
693         self.system_distance = -1
694         self.top_system_distance = -1
695
696     def print_length_field (self, printer, field, value):
697         if value >= 0:
698             printer.dump ("%s = %s\\cm" % (field, value))
699             printer.newline ()
700     def print_ly (self, printer):
701         if self.global_staff_size > 0:
702             printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size)
703             printer.newline ()
704         printer.dump ('\\paper {')
705         printer.newline ()
706         self.print_length_field (printer, "paper-width", self.page_width)
707         self.print_length_field (printer, "paper-height", self.page_height)
708         self.print_length_field (printer, "top-margin", self.top_margin)
709         self.print_length_field (printer, "botton-margin", self.bottom_margin)
710         self.print_length_field (printer, "left-margin", self.left_margin)
711         # TODO: maybe set line-width instead of right-margin?
712         self.print_length_field (printer, "right-margin", self.right_margin)
713         # TODO: What's the corresponding setting for system_left_margin and
714         #        system_right_margin in Lilypond?
715         self.print_length_field (printer, "between-system-space", self.system_distance)
716         self.print_length_field (printer, "page-top-space", self.top_system_distance)
717
718         printer.dump ('}')
719         printer.newline ()
720
721 class Layout:
722     def __init__ (self):
723         self.context_dict = {}
724     def add_context (self, context):
725         if not self.context_dict.has_key (context):
726             self.context_dict[context] = []
727     def set_context_item (self, context, item):
728         self.add_context (context)
729         if not item in self.context_dict[context]:
730             self.context_dict[context].append (item)
731     def print_ly (self, printer):
732         if self.context_dict.items ():
733             printer.dump ('\\layout {')
734             printer.newline ()
735             for (context, defs) in self.context_dict.items ():
736                 printer.dump ('\\context { \\%s' % context)
737                 printer.newline ()
738                 for d in defs:
739                     printer.dump (d)
740                     printer.newline ()
741                 printer.dump ('}')
742                 printer.newline ()
743             printer.dump ('}')
744             printer.newline ()
745
746
747 class ChordEvent (NestedMusic):
748     def __init__ (self):
749         NestedMusic.__init__ (self)
750         self.after_grace_elements = None
751         self.grace_elements = None
752         self.grace_type = None
753     def append_grace (self, element):
754         if element:
755             if not self.grace_elements:
756                 self.grace_elements = SequentialMusic ()
757             self.grace_elements.append (element)
758     def append_after_grace (self, element):
759         if element:
760             if not self.after_grace_elements:
761                 self.after_grace_elements = SequentialMusic ()
762             self.after_grace_elements.append (element)
763
764     def has_elements (self):
765         return [e for e in self.elements if
766                isinstance (e, NoteEvent) or isinstance (e, RestEvent)] != []
767
768
769     def get_length (self):
770         l = Rational (0)
771         for e in self.elements:
772             l = max(l, e.get_length())
773         return l
774
775     def get_duration (self):
776         note_events = [e for e in self.elements if
777                isinstance (e, NoteEvent) or isinstance (e, RestEvent)]
778         if note_events:
779             return note_events[0].duration
780         else:
781             return None
782
783     def print_ly (self, printer):
784         note_events = [e for e in self.elements if
785                isinstance (e, NoteEvent)]
786
787         rest_events = [e for e in self.elements if
788                isinstance (e, RhythmicEvent)
789                and not isinstance (e, NoteEvent)]
790         
791         other_events = [e for e in self.elements if
792                 not isinstance (e, RhythmicEvent)]
793
794         if self.after_grace_elements:
795             printer ('\\afterGrace {')
796
797         if self.grace_elements and self.elements:
798             if self.grace_type:
799                 printer ('\\%s' % self.grace_type)
800             else:
801                 printer ('\\grace')
802             # don't print newlines after the { and } braces
803             self.grace_elements.print_ly (printer, False)
804         elif self.grace_elements: # no self.elements!
805             warning (_ ("Grace note with no following music: %s") % self.grace_elements)
806             if self.grace_type:
807                 printer ('\\%s' % self.grace_type)
808             else:
809                 printer ('\\grace')
810             self.grace_elements.print_ly (printer, False)
811             printer ('{}')
812
813         # Print all overrides and other settings needed by the 
814         # articulations/ornaments before the note
815         for e in other_events:
816             e.print_before_note (printer)
817
818         if rest_events:
819             rest_events[0].print_ly (printer)
820         elif len (note_events) == 1:
821             note_events[0].print_ly (printer)
822         elif note_events:
823             global previous_pitch
824             pitches = []
825             basepitch = None
826             for x in note_events:
827                 pitches.append (x.pitch.ly_expression ())
828                 if not basepitch:
829                     basepitch = previous_pitch
830             printer ('<%s>' % string.join (pitches))
831             previous_pitch = basepitch
832             duration = self.get_duration ()
833             if duration:
834                 duration.print_ly (printer)
835         else:
836             pass
837         
838         for e in other_events:
839             e.print_ly (printer)
840
841         for e in other_events:
842             e.print_after_note (printer)
843
844         if self.after_grace_elements:
845             printer ('}')
846             self.after_grace_elements.print_ly (printer, False)
847
848         self.print_comment (printer)
849             
850 class Partial (Music):
851     def __init__ (self):
852         Music.__init__ (self)
853         self.partial = None
854     def print_ly (self, printer):
855         if self.partial:
856             printer.dump ("\\partial %s" % self.partial.ly_expression ())
857
858 class BarLine (Music):
859     def __init__ (self):
860         Music.__init__ (self)
861         self.bar_number = 0
862         self.type = None
863         
864     def print_ly (self, printer):
865         bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': ":",
866                        'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
867                        'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
868                        'short': "'", 'none': "" }.get (self.type, None)
869         if bar_symbol <> None:
870             printer.dump ('\\bar "%s"' % bar_symbol)
871         else:
872             printer.dump ("|")
873
874         if self.bar_number > 0 and (self.bar_number % 10) == 0:
875             printer.dump ("\\barNumberCheck #%d " % self.bar_number)
876         elif self.bar_number > 0:
877             printer.print_verbatim (' %% %d' % self.bar_number)
878         printer.newline ()
879
880     def ly_expression (self):
881         return " | "
882
883 class Event(Music):
884     def __init__ (self):
885         # strings to print before the note to which an event is attached.
886         # Ignored for notes etc.
887         self.before_note = None
888         self.after_note = None
889    # print something before the note to which an event is attached, e.g. overrides
890     def print_before_note (self, printer):
891         if self.before_note:
892             printer.dump (self.before_note)
893    # print something after the note to which an event is attached, e.g. resetting
894     def print_after_note (self, printer):
895         if self.after_note:
896             printer.dump (self.after_note)
897     pass
898
899 class SpanEvent (Event):
900     def __init__ (self):
901         Event.__init__ (self)
902         self.span_direction = 0 # start/stop
903         self.line_type = 'solid'
904         self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
905         self.size = 0 # size of e.g. ocrave shift
906     def wait_for_note (self):
907         return True
908     def get_properties(self):
909         return "'span-direction  %d" % self.span_direction
910     def set_span_type (self, type):
911         self.span_type = type
912
913 class SlurEvent (SpanEvent):
914     def print_before_note (self, printer):
915         command = {'dotted': '\\slurDotted', 
916                   'dashed' : '\\slurDashed'}.get (self.line_type, '')
917         if command and self.span_direction == -1:
918             printer.dump (command)
919     def print_after_note (self, printer):
920         # reset non-solid slur types!
921         command = {'dotted': '\\slurSolid', 
922                   'dashed' : '\\slurSolid'}.get (self.line_type, '')
923         if command and self.span_direction == -1:
924             printer.dump (command)
925     def ly_expression (self):
926         return {-1: '(', 1:')'}.get (self.span_direction, '')
927
928 class BeamEvent (SpanEvent):
929     def ly_expression (self):
930         return {-1: '[', 1:']'}.get (self.span_direction, '')
931
932 class PedalEvent (SpanEvent):
933     def ly_expression (self):
934         return {-1: '\\sustainOn',
935             0:'\\sustainOff\\sustainOn',
936             1:'\\sustainOff'}.get (self.span_direction, '')
937
938 class TextSpannerEvent (SpanEvent):
939     def ly_expression (self):
940         return {-1: '\\startTextSpan',
941             1:'\\stopTextSpan'}.get (self.span_direction, '')
942
943 class BracketSpannerEvent (SpanEvent):
944     # Ligature brackets use prefix-notation!!!
945     def print_before_note (self, printer):
946         if self.span_direction == -1:
947             printer.dump ('\[')
948     # the the bracket after the last note
949     def print_after_note (self, printer):
950         if self.span_direction == 1:
951             printer.dump ('\]')
952     # we're printing everything in print_(before|after)_note...
953     def ly_expression (self):
954         return '';
955
956
957 class OctaveShiftEvent (SpanEvent):
958     def wait_for_note (self):
959         return False
960     def set_span_type (self, type):
961         self.span_type = {'up': 1, 'down': -1}.get (type, 0)
962     def ly_octave_shift_indicator (self):
963         # convert 8/15 to lilypond indicators (+-1/+-2)
964         value = {8: 1, 15: 2}.get (self.size, 0)
965         # negative values go up!
966         value *= -1*self.span_type
967         return value
968     def ly_expression (self):
969         dir = self.ly_octave_shift_indicator ()
970         value = ''
971         if dir:
972             value = '\ottava #%s' % dir
973         return { 
974             -1: value,
975             1: '\ottava #0'}.get (self.span_direction, '')
976
977 class TrillSpanEvent (SpanEvent):
978     def ly_expression (self):
979         return {-1: '\\startTrillSpan',
980             0: '', # no need to write out anything for type='continue'
981             1:'\\stopTrillSpan'}.get (self.span_direction, '')
982
983 class GlissandoEvent (SpanEvent):
984     def print_before_note (self, printer):
985         if self.span_direction == -1:
986             style= {
987                 "dashed" : "dashed-line",
988                 "dotted" : "dotted-line",
989                 "wavy"   : "zigzag" 
990             }. get (self.line_type, None)
991             if style:
992                 printer.dump ("\once \override Glissando #'style = #'%s" % style)
993     def ly_expression (self):
994         return {-1: '\\glissando',
995             1:''}.get (self.span_direction, '')
996
997 class ArpeggioEvent(Event):
998     def __init__ (self):
999         Event.__init__ (self)
1000         self.direction = 0
1001         self.non_arpeggiate = False
1002     def wait_for_note (self):
1003         return True
1004     def print_before_note (self, printer):
1005         if self.non_arpeggiate:
1006             printer.dump ("\\arpeggioBracket")
1007         else:
1008           dir = { -1: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.get (self.direction, '')
1009           if dir:
1010               printer.dump (dir)
1011     def print_after_note (self, printer):
1012         if self.non_arpeggiate or self.direction:
1013             printer.dump ("\\arpeggioNormal")
1014     def ly_expression (self):
1015         return ('\\arpeggio')
1016
1017
1018 class TieEvent(Event):
1019     def ly_expression (self):
1020         return '~'
1021
1022
1023 class HairpinEvent (SpanEvent):
1024     def set_span_type (self, type):
1025         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
1026     def hairpin_to_ly (self):
1027         if self.span_direction == 1:
1028             return '\!'
1029         else:
1030             return {1: '\<', -1: '\>'}.get (self.span_type, '')
1031     
1032     def ly_expression (self):
1033         return self.hairpin_to_ly ()
1034     
1035     def print_ly (self, printer):
1036         val = self.hairpin_to_ly ()
1037         if val:
1038             printer.dump (val)
1039
1040
1041
1042 class DynamicsEvent (Event):
1043     def __init__ (self):
1044         Event.__init__ (self)
1045         self.type = None
1046     def wait_for_note (self):
1047         return True
1048     def ly_expression (self):
1049         if self.type:
1050             return '\%s' % self.type
1051         else:
1052             return
1053
1054     def print_ly (self, printer):
1055         if self.type:
1056             printer.dump ("\\%s" % self.type)
1057
1058 class MarkEvent (Event):
1059     def __init__ (self, text="\\default"):
1060         Event.__init__ (self)
1061         self.mark = text
1062     def wait_for_note (self):
1063         return False
1064     def ly_contents (self):
1065         if self.mark:
1066             return '%s' % self.mark
1067         else:
1068             return "\"ERROR\""
1069     def ly_expression (self):
1070         return '\\mark %s' % self.ly_contents ()
1071
1072 class MusicGlyphMarkEvent (MarkEvent):
1073     def ly_contents (self):
1074         if self.mark:
1075             return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
1076         else:
1077             return ''
1078
1079
1080 class TextEvent (Event):
1081     def __init__ (self):
1082         Event.__init__ (self)
1083         self.Text = None
1084         self.force_direction = None
1085         self.markup = ''
1086     def wait_for_note (self):
1087         return True
1088
1089     def direction_mod (self):
1090         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1091
1092     def ly_expression (self):
1093         base_string = '%s\"%s\"'
1094         if self.markup:
1095             base_string = '%s\markup{ ' + self.markup + ' {%s} }'
1096         return base_string % (self.direction_mod (), self.text)
1097
1098 class ArticulationEvent (Event):
1099     def __init__ (self):
1100         Event.__init__ (self)
1101         self.type = None
1102         self.force_direction = None
1103     def wait_for_note (self):
1104         return True
1105
1106     def direction_mod (self):
1107         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
1108
1109     def ly_expression (self):
1110         return '%s\\%s' % (self.direction_mod (), self.type)
1111
1112 class ShortArticulationEvent (ArticulationEvent):
1113     def direction_mod (self):
1114         # default is -
1115         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1116     def ly_expression (self):
1117         if self.type:
1118             return '%s%s' % (self.direction_mod (), self.type)
1119         else:
1120             return ''
1121
1122 class NoDirectionArticulationEvent (ArticulationEvent):
1123     def ly_expression (self):
1124         if self.type:
1125             return '\\%s' % self.type
1126         else:
1127             return ''
1128
1129 class MarkupEvent (ShortArticulationEvent):
1130     def __init__ (self):
1131         ArticulationEvent.__init__ (self)
1132         self.contents = None
1133     def ly_expression (self):
1134         if self.contents:
1135             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
1136         else:
1137             return ''
1138
1139 class FretEvent (MarkupEvent):
1140     def __init__ (self):
1141         MarkupEvent.__init__ (self)
1142         self.force_direction = 1
1143         self.strings = 6
1144         self.frets = 4
1145         self.barre = None
1146         self.elements = []
1147     def ly_expression (self):
1148         val = ""
1149         if self.strings <> 6:
1150             val += "w:%s;" % self.strings
1151         if self.frets <> 4:
1152             val += "h:%s;" % self.frets
1153         if self.barre and len (self.barre) >= 3:
1154             val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
1155         have_fingering = False
1156         for i in self.elements:
1157             if len (i) > 1:
1158                 val += "%s-%s" % (i[0], i[1])
1159             if len (i) > 2:
1160                 have_fingering = True
1161                 val += "-%s" % i[2]
1162             val += ";"
1163         if have_fingering:
1164             val = "f:1;" + val
1165         if val:
1166             return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
1167         else:
1168             return ''
1169
1170 class ChordPitch:
1171     def __init__ (self):
1172         self.alteration = 0
1173         self.step = 0
1174     def __repr__(self):
1175         return self.ly_expression()
1176     def ly_expression (self): 
1177         return pitch_generating_function (self)
1178
1179 class ChordModification:
1180     def __init__ (self):
1181         self.alteration = 0
1182         self.step = 0
1183         self.type = 0
1184     def ly_expression (self):
1185         if self.type:
1186             val = {1: ".", -1: "^" }.get (self.type, "")
1187             val += "%s" % self.step
1188             val += {1: "+", -1: "-"}.get (self.alteration, "")
1189             return val
1190         else:
1191             return ''
1192
1193 class ChordNameEvent (Event):
1194     def __init__ (self):
1195         Event.__init__ (self)
1196         self.root = None
1197         self.kind = None
1198         self.duration = None
1199         self.modifications = []
1200         self.bass = None
1201     def add_modification (self, mod):
1202         self.modifications.append (mod)
1203     def ly_expression (self):
1204         if not self.root:
1205             return ''
1206         value = self.root.ly_expression ()
1207         if self.duration:
1208             value += self.duration.ly_expression ()
1209         if self.kind:
1210             value += ":"
1211             value += self.kind
1212         # First print all additions/changes, and only afterwards all subtractions
1213         for m in self.modifications:
1214             if m.type == 1:
1215               value += m.ly_expression ()
1216         for m in self.modifications:
1217             if m.type == -1:
1218               value += m.ly_expression ()
1219         if self.bass:
1220             value += "/+%s" % self.bass.ly_expression ()
1221         return value
1222
1223
1224 class TremoloEvent (ArticulationEvent):
1225     def __init__ (self):
1226         Event.__init__ (self)
1227         self.bars = 0
1228
1229     def ly_expression (self):
1230         str=''
1231         if self.bars and self.bars > 0:
1232             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
1233         return str
1234
1235 class BendEvent (ArticulationEvent):
1236     def __init__ (self):
1237         Event.__init__ (self)
1238         self.alter = 0
1239     def ly_expression (self):
1240         if self.alter:
1241             return "-\\bendAfter #%s" % self.alter
1242         else:
1243             return ''
1244
1245 class RhythmicEvent(Event):
1246     def __init__ (self):
1247         Event.__init__ (self)
1248         self.duration = Duration()
1249         
1250     def get_length (self):
1251         return self.duration.get_length()
1252         
1253     def get_properties (self):
1254         return ("'duration %s"
1255                 % self.duration.lisp_expression ())
1256     
1257 class RestEvent (RhythmicEvent):
1258     def __init__ (self):
1259         RhythmicEvent.__init__ (self)
1260         self.pitch = None
1261     def ly_expression (self):
1262         if self.pitch:
1263             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1264         else:
1265             return 'r%s' % self.duration.ly_expression ()
1266     
1267     def print_ly (self, printer):
1268         if self.pitch:
1269             self.pitch.print_ly (printer)
1270             self.duration.print_ly (printer)
1271             printer ('\\rest')
1272         else:
1273             printer('r')
1274             self.duration.print_ly (printer)
1275
1276 class SkipEvent (RhythmicEvent):
1277     def ly_expression (self):
1278         return 's%s' % self.duration.ly_expression () 
1279
1280 class NoteEvent(RhythmicEvent):
1281     def  __init__ (self):
1282         RhythmicEvent.__init__ (self)
1283         self.pitch = None
1284         self.drum_type = None
1285         self.cautionary = False
1286         self.forced_accidental = False
1287         
1288     def get_properties (self):
1289         str = RhythmicEvent.get_properties (self)
1290         
1291         if self.pitch:
1292             str += self.pitch.lisp_expression ()
1293         elif self.drum_type:
1294             str += "'drum-type '%s" % self.drum_type
1295
1296         return str
1297     
1298     def pitch_mods (self):
1299         excl_question = ''
1300         if self.cautionary:
1301             excl_question += '?'
1302         if self.forced_accidental:
1303             excl_question += '!'
1304
1305         return excl_question
1306     
1307     def ly_expression (self):
1308         if self.pitch:
1309             return '%s%s%s' % (self.pitch.ly_expression (),
1310                                self.pitch_mods(),
1311                                self.duration.ly_expression ())
1312         elif self.drum_type:
1313             return '%s%s' (self.drum_type,
1314                            self.duration.ly_expression ())
1315
1316     def print_ly (self, printer):
1317         if self.pitch:
1318             self.pitch.print_ly (printer)
1319             printer (self.pitch_mods ())
1320         else:
1321             printer (self.drum_type)
1322
1323         self.duration.print_ly (printer)
1324
1325 class KeySignatureChange (Music):
1326     def __init__ (self):
1327         Music.__init__ (self)
1328         self.scale = []
1329         self.tonic = Pitch()
1330         self.mode = 'major'
1331         
1332     def ly_expression (self):
1333         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1334                      self.mode)
1335     
1336     def lisp_expression (self):
1337         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
1338         scale_str = ("'(%s)" % string.join (pairs))
1339
1340         return """ (make-music 'KeyChangeEvent
1341      'pitch-alist %s) """ % scale_str
1342
1343 class TimeSignatureChange (Music):
1344     def __init__ (self):
1345         Music.__init__ (self)
1346         self.fraction = (4,4)
1347     def ly_expression (self):
1348         return '\\time %d/%d ' % self.fraction
1349     
1350 class ClefChange (Music):
1351     def __init__ (self):
1352         Music.__init__ (self)
1353         self.type = 'G'
1354         self.position = 2
1355         self.octave = 0
1356
1357     def octave_modifier (self):
1358         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1359     def clef_name (self):
1360         return {('G', 2): "treble",
1361                 ('G', 1): "french",
1362                 ('C', 1): "soprano",
1363                 ('C', 2): "mezzosoprano",
1364                 ('C', 3): "alto",
1365                 ('C', 4): "tenor",
1366                 ('C', 5): "baritone",
1367                 ('F', 3): "varbaritone",
1368                 ('F', 4): "bass",
1369                 ('F', 5): "subbass",
1370                 ("percussion", 2): "percussion",
1371                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1372     def ly_expression (self):
1373         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1374
1375     clef_dict = {
1376         "G": ("clefs.G", -2, -6),
1377         "C": ("clefs.C", 0, 0),
1378         "F": ("clefs.F", 2, 6),
1379         }
1380     
1381     def lisp_expression (self):
1382         try:
1383             (glyph, pos, c0) = self.clef_dict[self.type]
1384         except KeyError:
1385             return ""
1386         clefsetting = """
1387         (make-music 'SequentialMusic
1388         'elements (list
1389    (context-spec-music
1390    (make-property-set 'clefGlyph "%s") 'Staff)
1391    (context-spec-music
1392    (make-property-set 'clefPosition %d) 'Staff)
1393    (context-spec-music
1394    (make-property-set 'middleCPosition %d) 'Staff)))
1395 """ % (glyph, pos, c0)
1396         return clefsetting
1397
1398 class Transposition (Music):
1399     def __init__ (self):
1400         Music.__init__ (self)
1401         self.pitch = None
1402     def ly_expression (self):
1403         self.pitch._force_absolute_pitch = True
1404         return '\\transposition %s' % self.pitch.ly_expression ()
1405
1406 class StaffChange (Music):
1407     def __init__ (self, staff):
1408         Music.__init__ (self)
1409         self.staff = staff
1410     def ly_expression (self):
1411         if self.staff:
1412             return "\\change Staff=\"%s\"" % self.staff
1413         else:
1414             return ''
1415
1416
1417 class TempoMark (Music):
1418     def __init__ (self):
1419         Music.__init__ (self)
1420         self.baseduration = None
1421         self.newduration = None
1422         self.beats = None
1423         self.parentheses = False
1424     def set_base_duration (self, dur):
1425         self.baseduration = dur
1426     def set_new_duration (self, dur):
1427         self.newduration = dur
1428     def set_beats_per_minute (self, beats):
1429         self.beats = beats
1430     def set_parentheses (self, parentheses):
1431         self.parentheses = parentheses
1432     def wait_for_note (self):
1433         return False
1434     def duration_to_markup (self, dur):
1435         if dur:
1436             # Generate the markup to print the note, use scheme mode for 
1437             # ly_expression to get longa and not \longa (which causes an error)
1438             return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
1439         else:
1440             return ''
1441     def tempo_markup_template (self):
1442         return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
1443     def ly_expression (self):
1444         res = ''
1445         if not self.baseduration:
1446             return res
1447         if self.beats:
1448             if self.parentheses:
1449                 res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
1450             else:
1451                 res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
1452         elif self.newduration:
1453             dm = self.duration_to_markup (self.baseduration)
1454             ndm = self.duration_to_markup (self.newduration)
1455             if self.parentheses:
1456                 contents = "\"(\" %s = %s \")\"" % (dm, ndm)
1457             else:
1458                 contents = " %s = %s " % (dm, ndm)
1459             res += self.tempo_markup_template() % contents
1460         else:
1461             return ''
1462         return res
1463
1464 class FiguredBassNote (Music):
1465     def __init__ (self):
1466         Music.__init__ (self)
1467         self.number = ''
1468         self.prefix = ''
1469         self.suffix = ''
1470     def set_prefix (self, prefix):
1471         self.prefix = prefix
1472     def set_suffix (self, suffix):
1473         self.prefix = suffix
1474     def set_number (self, number):
1475         self.number = number
1476     def ly_expression (self):
1477         res = ''
1478         if self.number:
1479             res += self.number
1480         else:
1481             res += '_'
1482         if self.prefix:
1483             res += self.prefix
1484         if self.suffix:
1485             res += self.suffix
1486         return res
1487
1488
1489 class FiguredBassEvent (NestedMusic):
1490     def __init__ (self):
1491         NestedMusic.__init__ (self)
1492         self.duration = None
1493         self.real_duration = 0
1494         self.parentheses = False
1495         return
1496     def set_duration (self, dur):
1497         self.duration = dur
1498     def set_parentheses (self, par):
1499         self.parentheses = par
1500     def set_real_duration (self, dur):
1501         self.real_duration = dur
1502
1503     def print_ly (self, printer):
1504         figured_bass_events = [e for e in self.elements if
1505                isinstance (e, FiguredBassNote)]
1506         if figured_bass_events:
1507           notes = []
1508           for x in figured_bass_events:
1509               notes.append (x.ly_expression ())
1510           contents = string.join (notes)
1511           if self.parentheses:
1512               contents = '[%s]' % contents
1513           printer ('<%s>' % contents)
1514           self.duration.print_ly (printer)
1515
1516
1517 class MultiMeasureRest(Music):
1518
1519     def lisp_expression (self):
1520         return """
1521 (make-music
1522   'MultiMeasureRestMusicGroup
1523   'elements
1524   (list (make-music (quote BarCheck))
1525         (make-music
1526           'ChordEvent
1527           'elements
1528           (list (make-music
1529                   'MultiMeasureRestEvent
1530                   'duration
1531                   %s)))
1532         (make-music (quote BarCheck))))
1533 """ % self.duration.lisp_expression ()
1534
1535     def ly_expression (self):
1536         return 'R%s' % self.duration.ly_expression ()
1537
1538
1539 class StaffGroup:
1540     def __init__ (self, command = "StaffGroup"):
1541         self.stafftype = command
1542         self.id = None
1543         self.instrument_name = None
1544         self.short_instrument_name = None
1545         self.symbol = None
1546         self.spanbar = None
1547         self.children = []
1548         self.is_group = True
1549         # part_information is a list with entries of the form
1550         #     [staffid, voicelist]
1551         # where voicelist is a list with entries of the form
1552         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1553         self.part_information = None
1554
1555     def append_staff (self, staff):
1556         self.children.append (staff)
1557
1558     def set_part_information (self, part_name, staves_info):
1559         if part_name == self.id:
1560             self.part_information = staves_info
1561         else:
1562             for c in self.children:
1563                 c.set_part_information (part_name, staves_info)
1564
1565     def print_ly_contents (self, printer):
1566         for c in self.children:
1567             if c:
1568                 c.print_ly (printer)
1569     def print_ly_overrides (self, printer):
1570         needs_with = False
1571         needs_with |= self.spanbar == "no"
1572         needs_with |= self.instrument_name != None
1573         needs_with |= self.short_instrument_name != None
1574         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1575         if needs_with:
1576             printer.dump ("\\with {")
1577             if self.instrument_name or self.short_instrument_name:
1578                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1579             if self.spanbar == "no":
1580                 printer.dump ("\\override SpanBar #'transparent = ##t")
1581             brack = {"brace": "SystemStartBrace",
1582                      "none": "f",
1583                      "line": "SystemStartSquare"}.get (self.symbol, None)
1584             if brack:
1585                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1586             printer.dump ("}")
1587
1588     def print_ly (self, printer):
1589         if self.stafftype:
1590             printer.dump ("\\new %s" % self.stafftype)
1591         self.print_ly_overrides (printer)
1592         printer.dump ("<<")
1593         printer.newline ()
1594         if self.stafftype and self.instrument_name:
1595             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1596                     escape_instrument_string (self.instrument_name)))
1597             printer.newline ()
1598         if self.stafftype and self.short_instrument_name:
1599             printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1600                     escape_instrument_string (self.short_instrument_name)))
1601             printer.newline ()
1602         self.print_ly_contents (printer)
1603         printer.newline ()
1604         printer.dump (">>")
1605         printer.newline ()
1606
1607
1608 class Staff (StaffGroup):
1609     def __init__ (self, command = "Staff"):
1610         StaffGroup.__init__ (self, command)
1611         self.is_group = False
1612         self.part = None
1613         self.voice_command = "Voice"
1614         self.substafftype = None
1615
1616     def print_ly_overrides (self, printer):
1617         pass
1618
1619     def print_ly_contents (self, printer):
1620         if not self.id or not self.part_information:
1621             return
1622         sub_staff_type = self.substafftype
1623         if not sub_staff_type:
1624             sub_staff_type = self.stafftype
1625
1626         for [staff_id, voices] in self.part_information:
1627             # Chord names need to come before the staff itself!
1628             for [v, lyrics, figuredbass, chordnames] in voices:
1629                 if chordnames:
1630                     printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
1631
1632             # now comes the real staff definition:
1633             if staff_id:
1634                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1635             else:
1636                 printer ('\\context %s << ' % sub_staff_type)
1637             printer.newline ()
1638             n = 0
1639             nr_voices = len (voices)
1640             for [v, lyrics, figuredbass, chordnames] in voices:
1641                 n += 1
1642                 voice_count_text = ''
1643                 if nr_voices > 1:
1644                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1645                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1646                 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1647                 printer.newline ()
1648
1649                 for l in lyrics:
1650                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1651                     printer.newline()
1652                 if figuredbass:
1653                     printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass))
1654             printer ('>>')
1655
1656     def print_ly (self, printer):
1657         if self.part_information and len (self.part_information) > 1:
1658             self.stafftype = "PianoStaff"
1659             self.substafftype = "Staff"
1660         StaffGroup.print_ly (self, printer)
1661
1662 class TabStaff (Staff):
1663     def __init__ (self, command = "TabStaff"):
1664         Staff.__init__ (self, command)
1665         self.string_tunings = []
1666         self.tablature_format = None
1667         self.voice_command = "TabVoice"
1668     def print_ly_overrides (self, printer):
1669         if self.string_tunings or self.tablature_format:
1670             printer.dump ("\\with {")
1671             if self.string_tunings:
1672                 printer.dump ("stringTunings = #'(")
1673                 for i in self.string_tunings:
1674                     printer.dump ("%s" % i.semitones ())
1675                 printer.dump (")")
1676             if self.tablature_format:
1677                 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1678             printer.dump ("}")
1679
1680
1681 class DrumStaff (Staff):
1682     def __init__ (self, command = "DrumStaff"):
1683         Staff.__init__ (self, command)
1684         self.drum_style_table = None
1685         self.voice_command = "DrumVoice"
1686     def print_ly_overrides (self, printer):
1687         if self.drum_style_table:
1688             printer.dump ("\with {")
1689             printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1690             printer.dump ("}")
1691
1692 class RhythmicStaff (Staff):
1693     def __init__ (self, command = "RhythmicStaff"):
1694         Staff.__init__ (self, command)
1695         
1696 class Score:
1697     def __init__ (self):
1698         self.contents = None
1699         self.create_midi = False
1700
1701     def set_contents (self, contents):
1702         self.contents = contents
1703     
1704     def set_part_information (self, part_id, staves_info):
1705         if self.contents:
1706           self.contents.set_part_information (part_id, staves_info)
1707
1708     def print_ly (self, printer):
1709         printer.dump ("\\score {");
1710         printer.newline ()
1711         if self.contents:
1712             self.contents.print_ly (printer);
1713         printer.dump ("\\layout {}");
1714         printer.newline ()
1715         if not self.create_midi:
1716             printer.dump ("% To create MIDI output, uncomment the following line:");
1717             printer.newline ();
1718             printer.dump ("% ");
1719         printer.dump ("\\midi {}");
1720         printer.newline ()
1721         printer.dump ("}");
1722         printer.newline ()
1723
1724
1725 def test_pitch ():
1726     bflat = Pitch()
1727     bflat.alteration = -1
1728     bflat.step =  6
1729     bflat.octave = -1
1730     fifth = Pitch()
1731     fifth.step = 4
1732     down = Pitch ()
1733     down.step = -4
1734     down.normalize ()
1735     
1736     
1737     print bflat.semitones()
1738     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1739     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1740
1741     print bflat.semitones(), 'down'
1742     print bflat.transposed (down)
1743     print bflat.transposed (down).transposed (down)
1744     print bflat.transposed (down).transposed (down).transposed (down)
1745
1746
1747
1748 def test_printer ():
1749     def make_note ():
1750         evc = ChordEvent()
1751         n = NoteEvent()
1752         evc.append (n)
1753         return n
1754
1755     def make_tup ():
1756         m = SequentialMusic()
1757         m.append (make_note ())
1758         m.append (make_note ())
1759         m.append (make_note ())
1760
1761         
1762         t = TimeScaledMusic ()
1763         t.numerator = 2
1764         t.denominator = 3
1765         t.element = m
1766         return t
1767
1768     m = SequentialMusic ()
1769     m.append (make_tup ())
1770     m.append (make_tup ())
1771     m.append (make_tup ())
1772     
1773     printer = Output_printer()
1774     m.print_ly (printer)
1775     printer.newline ()
1776     
1777 def test_expr ():
1778     m = SequentialMusic()
1779     l = 2  
1780     evc = ChordEvent()
1781     n = NoteEvent()
1782     n.duration.duration_log = l
1783     n.pitch.step = 1
1784     evc.insert_around (None, n, 0)
1785     m.insert_around (None, evc, 0)
1786
1787     evc = ChordEvent()
1788     n = NoteEvent()
1789     n.duration.duration_log = l
1790     n.pitch.step = 3
1791     evc.insert_around (None, n, 0)
1792     m.insert_around (None, evc, 0)
1793
1794     evc = ChordEvent()
1795     n = NoteEvent()
1796     n.duration.duration_log = l
1797     n.pitch.step = 2 
1798     evc.insert_around (None, n, 0)
1799     m.insert_around (None, evc, 0)
1800
1801     evc = ClefChange()
1802     evc.type = 'treble'
1803     m.insert_around (None, evc, 0)
1804
1805     evc = ChordEvent()
1806     tonic = Pitch ()
1807     tonic.step = 2
1808     tonic.alteration = -2
1809     n = KeySignatureChange()
1810     n.tonic=tonic.copy()
1811     n.scale = [0, 0, -2, 0, 0,-2,-2]
1812     
1813     evc.insert_around (None, n, 0)
1814     m.insert_around (None, evc, 0)
1815
1816     return m
1817
1818
1819 if __name__ == '__main__':
1820     test_printer ()
1821     raise 'bla'
1822     test_pitch()
1823     
1824     expr = test_expr()
1825     expr.set_start (Rational (0))
1826     print expr.ly_expression()
1827     start = Rational (0,4)
1828     stop = Rational (4,2)
1829     def sub(x, start=start, stop=stop):
1830         ok = x.start >= start and x.start +x.get_length() <= stop
1831         return ok
1832     
1833     print expr.lisp_sub_expression(sub)
1834