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