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