]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Only print out the bar number if we really have a bar number
[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) or isinstance (e, RestEvent)]
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         elif self.bar_number > 0:
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: '\\sustainOn',
867             0:'\\sustainOff\\sustainOn',
868             1:'\\sustainOff'}.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 = '\ottava #%s' % dir
905         return { 
906             -1: value,
907             1: '\ottava #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: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.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 ("\\arpeggioNormal")
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 ChordPitch:
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 ChordModification:
1112     def __init__ (self):
1113         self.alteration = 0
1114         self.step = 0
1115         self.type = 0
1116     def ly_expression (self):
1117         if self.type:
1118             val = {1: ".", -1: "^" }.get (self.type, "")
1119             val += "%s" % self.step
1120             val += {1: "+", -1: "-"}.get (self.alteration, "")
1121             return val
1122         else:
1123             return ''
1124
1125 class ChordNameEvent (Event):
1126     def __init__ (self):
1127         Event.__init__ (self)
1128         self.root = None
1129         self.kind = None
1130         self.duration = None
1131         self.modifications = []
1132         self.bass = None
1133     def add_modification (self, mod):
1134         self.modifications.append (mod)
1135     def ly_expression (self):
1136         if not self.root:
1137             return ''
1138         value = self.root.ly_expression ()
1139         if self.duration:
1140             value += self.duration.ly_expression ()
1141         if self.kind:
1142             value += ":"
1143             value += self.kind
1144         # First print all additions/changes, and only afterwards all subtractions
1145         for m in self.modifications:
1146             if m.type == 1:
1147               value += m.ly_expression ()
1148         for m in self.modifications:
1149             if m.type == -1:
1150               value += m.ly_expression ()
1151         if self.bass:
1152             value += "/+%s" % self.bass.ly_expression ()
1153         return value
1154
1155
1156 class TremoloEvent (ArticulationEvent):
1157     def __init__ (self):
1158         Event.__init__ (self)
1159         self.bars = 0
1160
1161     def ly_expression (self):
1162         str=''
1163         if self.bars and self.bars > 0:
1164             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
1165         return str
1166
1167 class BendEvent (ArticulationEvent):
1168     def __init__ (self):
1169         Event.__init__ (self)
1170         self.alter = 0
1171     def ly_expression (self):
1172         if self.alter:
1173             return "-\\bendAfter #%s" % self.alter
1174         else:
1175             return ''
1176
1177 class RhythmicEvent(Event):
1178     def __init__ (self):
1179         Event.__init__ (self)
1180         self.duration = Duration()
1181         
1182     def get_length (self):
1183         return self.duration.get_length()
1184         
1185     def get_properties (self):
1186         return ("'duration %s"
1187                 % self.duration.lisp_expression ())
1188     
1189 class RestEvent (RhythmicEvent):
1190     def __init__ (self):
1191         RhythmicEvent.__init__ (self)
1192         self.pitch = None
1193     def ly_expression (self):
1194         if self.pitch:
1195             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1196         else:
1197             return 'r%s' % self.duration.ly_expression ()
1198     
1199     def print_ly (self, printer):
1200         if self.pitch:
1201             self.pitch.print_ly (printer)
1202             self.duration.print_ly (printer)
1203             printer ('\\rest')
1204         else:
1205             printer('r')
1206             self.duration.print_ly (printer)
1207
1208 class SkipEvent (RhythmicEvent):
1209     def ly_expression (self):
1210         return 's%s' % self.duration.ly_expression () 
1211
1212 class NoteEvent(RhythmicEvent):
1213     def  __init__ (self):
1214         RhythmicEvent.__init__ (self)
1215         self.pitch = None
1216         self.drum_type = None
1217         self.cautionary = False
1218         self.forced_accidental = False
1219         
1220     def get_properties (self):
1221         str = RhythmicEvent.get_properties (self)
1222         
1223         if self.pitch:
1224             str += self.pitch.lisp_expression ()
1225         elif self.drum_type:
1226             str += "'drum-type '%s" % self.drum_type
1227
1228         return str
1229     
1230     def pitch_mods (self):
1231         excl_question = ''
1232         if self.cautionary:
1233             excl_question += '?'
1234         if self.forced_accidental:
1235             excl_question += '!'
1236
1237         return excl_question
1238     
1239     def ly_expression (self):
1240         if self.pitch:
1241             return '%s%s%s' % (self.pitch.ly_expression (),
1242                                self.pitch_mods(),
1243                                self.duration.ly_expression ())
1244         elif self.drum_type:
1245             return '%s%s' (self.drum_type,
1246                            self.duration.ly_expression ())
1247
1248     def print_ly (self, printer):
1249         if self.pitch:
1250             self.pitch.print_ly (printer)
1251             printer (self.pitch_mods ())
1252         else:
1253             printer (self.drum_type)
1254
1255         self.duration.print_ly (printer)
1256
1257 class KeySignatureChange (Music):
1258     def __init__ (self):
1259         Music.__init__ (self)
1260         self.scale = []
1261         self.tonic = Pitch()
1262         self.mode = 'major'
1263         
1264     def ly_expression (self):
1265         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1266                      self.mode)
1267     
1268     def lisp_expression (self):
1269         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
1270         scale_str = ("'(%s)" % string.join (pairs))
1271
1272         return """ (make-music 'KeyChangeEvent
1273      'pitch-alist %s) """ % scale_str
1274
1275 class TimeSignatureChange (Music):
1276     def __init__ (self):
1277         Music.__init__ (self)
1278         self.fraction = (4,4)
1279     def ly_expression (self):
1280         return '\\time %d/%d ' % self.fraction
1281     
1282 class ClefChange (Music):
1283     def __init__ (self):
1284         Music.__init__ (self)
1285         self.type = 'G'
1286         self.position = 2
1287         self.octave = 0
1288
1289     def octave_modifier (self):
1290         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1291     def clef_name (self):
1292         return {('G', 2): "treble",
1293                 ('G', 1): "french",
1294                 ('C', 1): "soprano",
1295                 ('C', 2): "mezzosoprano",
1296                 ('C', 3): "alto",
1297                 ('C', 4): "tenor",
1298                 ('C', 5): "baritone",
1299                 ('F', 3): "varbaritone",
1300                 ('F', 4): "bass",
1301                 ('F', 5): "subbass",
1302                 ("percussion", 2): "percussion",
1303                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1304     def ly_expression (self):
1305         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1306
1307     clef_dict = {
1308         "G": ("clefs.G", -2, -6),
1309         "C": ("clefs.C", 0, 0),
1310         "F": ("clefs.F", 2, 6),
1311         }
1312     
1313     def lisp_expression (self):
1314         try:
1315             (glyph, pos, c0) = self.clef_dict[self.type]
1316         except KeyError:
1317             return ""
1318         clefsetting = """
1319         (make-music 'SequentialMusic
1320         'elements (list
1321    (context-spec-music
1322    (make-property-set 'clefGlyph "%s") 'Staff)
1323    (context-spec-music
1324    (make-property-set 'clefPosition %d) 'Staff)
1325    (context-spec-music
1326    (make-property-set 'middleCPosition %d) 'Staff)))
1327 """ % (glyph, pos, c0)
1328         return clefsetting
1329
1330
1331 class StaffChange (Music):
1332     def __init__ (self, staff):
1333         Music.__init__ (self)
1334         self.staff = staff
1335     def ly_expression (self):
1336         if self.staff:
1337             return "\\change Staff=\"%s\"" % self.staff
1338         else:
1339             return ''
1340
1341
1342 class TempoMark (Music):
1343     def __init__ (self):
1344         Music.__init__ (self)
1345         self.baseduration = None
1346         self.newduration = None
1347         self.beats = None
1348         self.parentheses = False
1349     def set_base_duration (self, dur):
1350         self.baseduration = dur
1351     def set_new_duration (self, dur):
1352         self.newduration = dur
1353     def set_beats_per_minute (self, beats):
1354         self.beats = beats
1355     def set_parentheses (self, parentheses):
1356         self.parentheses = parentheses
1357     def wait_for_note (self):
1358         return False
1359     def duration_to_markup (self, dur):
1360         if dur:
1361             # Generate the markup to print the note, use scheme mode for 
1362             # ly_expression to get longa and not \longa (which causes an error)
1363             return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
1364         else:
1365             return ''
1366     def tempo_markup_template (self):
1367         return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
1368     def ly_expression (self):
1369         res = ''
1370         if not self.baseduration:
1371             return res
1372         if self.beats:
1373             if self.parentheses:
1374                 res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
1375             else:
1376                 res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
1377         elif self.newduration:
1378             dm = self.duration_to_markup (self.baseduration)
1379             ndm = self.duration_to_markup (self.newduration)
1380             if self.parentheses:
1381                 contents = "\"(\" %s = %s \")\"" % (dm, ndm)
1382             else:
1383                 contents = " %s = %s " % (dm, ndm)
1384             res += self.tempo_markup_template() % contents
1385         else:
1386             return ''
1387         return res
1388
1389 class FiguredBassNote (Music):
1390     def __init__ (self):
1391         Music.__init__ (self)
1392         self.number = ''
1393         self.prefix = ''
1394         self.suffix = ''
1395     def set_prefix (self, prefix):
1396         self.prefix = prefix
1397     def set_suffix (self, suffix):
1398         self.prefix = suffix
1399     def set_number (self, number):
1400         self.number = number
1401     def ly_expression (self):
1402         res = ''
1403         if self.number:
1404             res += self.number
1405         else:
1406             res += '_'
1407         if self.prefix:
1408             res += self.prefix
1409         if self.suffix:
1410             res += self.suffix
1411         return res
1412
1413
1414 class FiguredBassEvent (NestedMusic):
1415     def __init__ (self):
1416         NestedMusic.__init__ (self)
1417         self.duration = None
1418         self.real_duration = 0
1419         self.parentheses = False
1420         return
1421     def set_duration (self, dur):
1422         self.duration = dur
1423     def set_parentheses (self, par):
1424         self.parentheses = par
1425     def set_real_duration (self, dur):
1426         self.real_duration = dur
1427
1428     def print_ly (self, printer):
1429         figured_bass_events = [e for e in self.elements if
1430                isinstance (e, FiguredBassNote)]
1431         if figured_bass_events:
1432           notes = []
1433           for x in figured_bass_events:
1434               notes.append (x.ly_expression ())
1435           contents = string.join (notes)
1436           if self.parentheses:
1437               contents = '[%s]' % contents
1438           printer ('<%s>' % contents)
1439           self.duration.print_ly (printer)
1440
1441
1442 class MultiMeasureRest(Music):
1443
1444     def lisp_expression (self):
1445         return """
1446 (make-music
1447   'MultiMeasureRestMusicGroup
1448   'elements
1449   (list (make-music (quote BarCheck))
1450         (make-music
1451           'ChordEvent
1452           'elements
1453           (list (make-music
1454                   'MultiMeasureRestEvent
1455                   'duration
1456                   %s)))
1457         (make-music (quote BarCheck))))
1458 """ % self.duration.lisp_expression ()
1459
1460     def ly_expression (self):
1461         return 'R%s' % self.duration.ly_expression ()
1462
1463
1464 class StaffGroup:
1465     def __init__ (self, command = "StaffGroup"):
1466         self.stafftype = command
1467         self.id = None
1468         self.instrument_name = None
1469         self.short_instrument_name = None
1470         self.symbol = None
1471         self.spanbar = None
1472         self.children = []
1473         self.is_group = True
1474         # part_information is a list with entries of the form
1475         #     [staffid, voicelist]
1476         # where voicelist is a list with entries of the form
1477         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1478         self.part_information = None
1479
1480     def append_staff (self, staff):
1481         self.children.append (staff)
1482
1483     def set_part_information (self, part_name, staves_info):
1484         if part_name == self.id:
1485             self.part_information = staves_info
1486         else:
1487             for c in self.children:
1488                 c.set_part_information (part_name, staves_info)
1489
1490     def print_ly_contents (self, printer):
1491         for c in self.children:
1492             if c:
1493                 c.print_ly (printer)
1494     def print_ly_overrides (self, printer):
1495         needs_with = False
1496         needs_with |= self.spanbar == "no"
1497         needs_with |= self.instrument_name != None
1498         needs_with |= self.short_instrument_name != None
1499         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1500         if needs_with:
1501             printer.dump ("\\with {")
1502             if self.instrument_name or self.short_instrument_name:
1503                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1504             if self.spanbar == "no":
1505                 printer.dump ("\\override SpanBar #'transparent = ##t")
1506             brack = {"brace": "SystemStartBrace",
1507                      "none": "f",
1508                      "line": "SystemStartSquare"}.get (self.symbol, None)
1509             if brack:
1510                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1511             printer.dump ("}")
1512
1513     def print_ly (self, printer):
1514         if self.stafftype:
1515             printer.dump ("\\new %s" % self.stafftype)
1516         self.print_ly_overrides (printer)
1517         printer.dump ("<<")
1518         printer.newline ()
1519         if self.stafftype and self.instrument_name:
1520             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1521                     escape_instrument_string (self.instrument_name)))
1522             printer.newline ()
1523         if self.stafftype and self.short_instrument_name:
1524             printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1525                     escape_instrument_string (self.short_instrument_name)))
1526             printer.newline ()
1527         self.print_ly_contents (printer)
1528         printer.newline ()
1529         printer.dump (">>")
1530         printer.newline ()
1531
1532
1533 class Staff (StaffGroup):
1534     def __init__ (self, command = "Staff"):
1535         StaffGroup.__init__ (self, command)
1536         self.is_group = False
1537         self.part = None
1538         self.voice_command = "Voice"
1539         self.substafftype = None
1540
1541     def print_ly_overrides (self, printer):
1542         pass
1543
1544     def print_ly_contents (self, printer):
1545         if not self.id or not self.part_information:
1546             return
1547         sub_staff_type = self.substafftype
1548         if not sub_staff_type:
1549             sub_staff_type = self.stafftype
1550
1551         for [staff_id, voices] in self.part_information:
1552             # Chord names need to come before the staff itself!
1553             for [v, lyrics, figuredbass, chordnames] in voices:
1554                 if chordnames:
1555                     printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
1556
1557             # now comes the real staff definition:
1558             if staff_id:
1559                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1560             else:
1561                 printer ('\\context %s << ' % sub_staff_type)
1562             printer.newline ()
1563             n = 0
1564             nr_voices = len (voices)
1565             for [v, lyrics, figuredbass, chordnames] in voices:
1566                 n += 1
1567                 voice_count_text = ''
1568                 if nr_voices > 1:
1569                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1570                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1571                 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1572                 printer.newline ()
1573
1574                 for l in lyrics:
1575                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1576                     printer.newline()
1577                 if figuredbass:
1578                     printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass))
1579             printer ('>>')
1580
1581     def print_ly (self, printer):
1582         if self.part_information and len (self.part_information) > 1:
1583             self.stafftype = "PianoStaff"
1584             self.substafftype = "Staff"
1585         StaffGroup.print_ly (self, printer)
1586
1587 class TabStaff (Staff):
1588     def __init__ (self, command = "TabStaff"):
1589         Staff.__init__ (self, command)
1590         self.string_tunings = []
1591         self.tablature_format = None
1592         self.voice_command = "TabVoice"
1593     def print_ly_overrides (self, printer):
1594         if self.string_tunings or self.tablature_format:
1595             printer.dump ("\\with {")
1596             if self.string_tunings:
1597                 printer.dump ("stringTunings = #'(")
1598                 for i in self.string_tunings:
1599                     printer.dump ("%s" % i.semitones ())
1600                 printer.dump (")")
1601             if self.tablature_format:
1602                 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1603             printer.dump ("}")
1604
1605
1606 class DrumStaff (Staff):
1607     def __init__ (self, command = "DrumStaff"):
1608         Staff.__init__ (self, command)
1609         self.drum_style_table = None
1610         self.voice_command = "DrumVoice"
1611     def print_ly_overrides (self, printer):
1612         if self.drum_style_table:
1613             printer.dump ("\with {")
1614             printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1615             printer.dump ("}")
1616
1617 class RhythmicStaff (Staff):
1618     def __init__ (self, command = "RhythmicStaff"):
1619         Staff.__init__ (self, command)
1620         
1621 class Score:
1622     def __init__ (self):
1623         self.contents = None
1624         self.create_midi = False
1625
1626     def set_contents (self, contents):
1627         self.contents = contents
1628     
1629     def set_part_information (self, part_id, staves_info):
1630         if self.contents:
1631           self.contents.set_part_information (part_id, staves_info)
1632
1633     def print_ly (self, printer):
1634         printer.dump ("\\score {");
1635         printer.newline ()
1636         if self.contents:
1637             self.contents.print_ly (printer);
1638         printer.dump ("\\layout {}");
1639         printer.newline ()
1640         if not self.create_midi:
1641             printer.dump ("% To create MIDI output, uncomment the following line:");
1642             printer.newline ();
1643             printer.dump ("% ");
1644         printer.dump ("\\midi {}");
1645         printer.newline ()
1646         printer.dump ("}");
1647         printer.newline ()
1648
1649
1650 def test_pitch ():
1651     bflat = Pitch()
1652     bflat.alteration = -1
1653     bflat.step =  6
1654     bflat.octave = -1
1655     fifth = Pitch()
1656     fifth.step = 4
1657     down = Pitch ()
1658     down.step = -4
1659     down.normalize ()
1660     
1661     
1662     print bflat.semitones()
1663     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1664     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1665
1666     print bflat.semitones(), 'down'
1667     print bflat.transposed (down)
1668     print bflat.transposed (down).transposed (down)
1669     print bflat.transposed (down).transposed (down).transposed (down)
1670
1671
1672
1673 def test_printer ():
1674     def make_note ():
1675         evc = ChordEvent()
1676         n = NoteEvent()
1677         evc.append (n)
1678         return n
1679
1680     def make_tup ():
1681         m = SequentialMusic()
1682         m.append (make_note ())
1683         m.append (make_note ())
1684         m.append (make_note ())
1685
1686         
1687         t = TimeScaledMusic ()
1688         t.numerator = 2
1689         t.denominator = 3
1690         t.element = m
1691         return t
1692
1693     m = SequentialMusic ()
1694     m.append (make_tup ())
1695     m.append (make_tup ())
1696     m.append (make_tup ())
1697     
1698     printer = Output_printer()
1699     m.print_ly (printer)
1700     printer.newline ()
1701     
1702 def test_expr ():
1703     m = SequentialMusic()
1704     l = 2  
1705     evc = ChordEvent()
1706     n = NoteEvent()
1707     n.duration.duration_log = l
1708     n.pitch.step = 1
1709     evc.insert_around (None, n, 0)
1710     m.insert_around (None, evc, 0)
1711
1712     evc = ChordEvent()
1713     n = NoteEvent()
1714     n.duration.duration_log = l
1715     n.pitch.step = 3
1716     evc.insert_around (None, n, 0)
1717     m.insert_around (None, evc, 0)
1718
1719     evc = ChordEvent()
1720     n = NoteEvent()
1721     n.duration.duration_log = l
1722     n.pitch.step = 2 
1723     evc.insert_around (None, n, 0)
1724     m.insert_around (None, evc, 0)
1725
1726     evc = ClefChange()
1727     evc.type = 'treble'
1728     m.insert_around (None, evc, 0)
1729
1730     evc = ChordEvent()
1731     tonic = Pitch ()
1732     tonic.step = 2
1733     tonic.alteration = -2
1734     n = KeySignatureChange()
1735     n.tonic=tonic.copy()
1736     n.scale = [0, 0, -2, 0, 0,-2,-2]
1737     
1738     evc.insert_around (None, n, 0)
1739     m.insert_around (None, evc, 0)
1740
1741     return m
1742
1743
1744 if __name__ == '__main__':
1745     test_printer ()
1746     raise 'bla'
1747     test_pitch()
1748     
1749     expr = test_expr()
1750     expr.set_start (Rational (0))
1751     print expr.ly_expression()
1752     start = Rational (0,4)
1753     stop = Rational (4,2)
1754     def sub(x, start=start, stop=stop):
1755         ok = x.start >= start and x.start +x.get_length() <= stop
1756         return ok
1757     
1758     print expr.lisp_sub_expression(sub)
1759