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