]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
Merge branch 'master' of ssh+git://hanwen@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
673 class ChordEvent (NestedMusic):
674     def __init__ (self):
675         NestedMusic.__init__ (self)
676         self.grace_elements = None
677         self.grace_type = None
678     def append_grace (self, element):
679         if element:
680             if not self.grace_elements:
681                 self.grace_elements = SequentialMusic ()
682             self.grace_elements.append (element)
683
684     def get_length (self):
685         l = Rational (0)
686         for e in self.elements:
687             l = max(l, e.get_length())
688         return l
689     
690     def print_ly (self, printer):
691         note_events = [e for e in self.elements if
692                isinstance (e, NoteEvent)]
693
694         rest_events = [e for e in self.elements if
695                isinstance (e, RhythmicEvent)
696                and not isinstance (e, NoteEvent)]
697         
698         other_events = [e for e in self.elements if
699                 not isinstance (e, RhythmicEvent)]
700
701         if self.grace_elements and self.elements:
702             if self.grace_type:
703                 printer ('\\%s' % self.grace_type)
704             else:
705                 printer ('\\grace')
706             # don't print newlines after the { and } braces
707             self.grace_elements.print_ly (printer, False)
708         # Print all overrides and other settings needed by the 
709         # articulations/ornaments before the note
710         for e in other_events:
711             e.print_before_note (printer)
712
713         if rest_events:
714             rest_events[0].print_ly (printer)
715         elif len (note_events) == 1:
716             note_events[0].print_ly (printer)
717         elif note_events:
718             global previous_pitch
719             pitches = []
720             basepitch = None
721             for x in note_events:
722                 pitches.append (x.pitch.ly_expression ())
723                 if not basepitch:
724                     basepitch = previous_pitch
725             printer ('<%s>' % string.join (pitches))
726             previous_pitch = basepitch
727             note_events[0].duration.print_ly (printer)
728         else:
729             pass
730         
731         for e in other_events:
732             e.print_ly (printer)
733
734         for e in other_events:
735             e.print_after_note (printer)
736
737         self.print_comment (printer)
738             
739 class Partial (Music):
740     def __init__ (self):
741         Music.__init__ (self)
742         self.partial = None
743     def print_ly (self, printer):
744         if self.partial:
745             printer.dump ("\\partial %s" % self.partial.ly_expression ())
746
747 class BarLine (Music):
748     def __init__ (self):
749         Music.__init__ (self)
750         self.bar_number = 0
751         self.type = None
752         
753     def print_ly (self, printer):
754         bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': ":",
755                        'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
756                        'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
757                        'short': "'", 'none': "" }.get (self.type, None)
758         if bar_symbol <> None:
759             printer.dump ('\\bar "%s"' % bar_symbol)
760         else:
761             printer.dump ("|")
762
763         if self.bar_number > 0 and (self.bar_number % 10) == 0:
764             printer.dump ("\\barNumberCheck #%d " % self.bar_number)
765         else:
766             printer.print_verbatim (' %% %d' % self.bar_number)
767         printer.newline ()
768
769     def ly_expression (self):
770         return " | "
771
772 class Event(Music):
773     def __init__ (self):
774         # strings to print before the note to which an event is attached.
775         # Ignored for notes etc.
776         self.before_note = None
777         self.after_note = None
778    # print something before the note to which an event is attached, e.g. overrides
779     def print_before_note (self, printer):
780         if self.before_note:
781             printer.dump (self.before_note)
782    # print something after the note to which an event is attached, e.g. resetting
783     def print_after_note (self, printer):
784         if self.after_note:
785             printer.dump (self.after_note)
786     pass
787
788 class SpanEvent (Event):
789     def __init__ (self):
790         Event.__init__ (self)
791         self.span_direction = 0 # start/stop
792         self.line_type = 'solid'
793         self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
794         self.size = 0 # size of e.g. ocrave shift
795     def wait_for_note (self):
796         return True
797     def get_properties(self):
798         return "'span-direction  %d" % self.span_direction
799     def set_span_type (self, type):
800         self.span_type = type
801
802 class SlurEvent (SpanEvent):
803     def print_before_note (self, printer):
804         command = {'dotted': '\\slurDotted', 
805                   'dashed' : '\\slurDashed'}.get (self.line_type, '')
806         if command and self.span_direction == -1:
807             printer.dump (command)
808     def print_after_note (self, printer):
809         # reset non-solid slur types!
810         command = {'dotted': '\\slurSolid', 
811                   'dashed' : '\\slurSolid'}.get (self.line_type, '')
812         if command and self.span_direction == -1:
813             printer.dump (command)
814     def ly_expression (self):
815         return {-1: '(', 1:')'}.get (self.span_direction, '')
816
817 class BeamEvent (SpanEvent):
818     def ly_expression (self):
819         return {-1: '[', 1:']'}.get (self.span_direction, '')
820
821 class PedalEvent (SpanEvent):
822     def ly_expression (self):
823         return {-1: '\\sustainDown',
824             0:'\\sustainUp\\sustainDown',
825             1:'\\sustainUp'}.get (self.span_direction, '')
826
827 class TextSpannerEvent (SpanEvent):
828     def ly_expression (self):
829         return {-1: '\\startTextSpan',
830             1:'\\stopTextSpan'}.get (self.span_direction, '')
831
832 class BracketSpannerEvent (SpanEvent):
833     def ly_expression (self):
834         return {-1: '\\startGroup',
835             1:'\\stopGroup'}.get (self.span_direction, '')
836
837
838 # type==-1 means octave up, type==-2 means octave down
839 class OctaveShiftEvent (SpanEvent):
840     def wait_for_note (self):
841         return False
842     def set_span_type (self, type):
843         self.span_type = {'up': 1, 'down': -1}.get (type, 0)
844     def ly_octave_shift_indicator (self):
845         # convert 8/15 to lilypond indicators (+-1/+-2)
846         value = {8: 1, 15: 2}.get (self.size, 0)
847         # negative values go up!
848         value *= -1*self.span_type
849         return value
850     def ly_expression (self):
851         dir = self.ly_octave_shift_indicator ()
852         value = ''
853         if dir:
854             value = '#(set-octavation %s)' % dir
855         return { 
856             -1: value,
857             1: '#(set-octavation 0)'}.get (self.span_direction, '')
858
859 class TrillSpanEvent (SpanEvent):
860     def ly_expression (self):
861         return {-1: '\\startTrillSpan',
862             0: '', # no need to write out anything for type='continue'
863             1:'\\stopTrillSpan'}.get (self.span_direction, '')
864
865 class GlissandoEvent (SpanEvent):
866     def print_before_note (self, printer):
867         if self.span_direction == -1:
868             style= {
869                 "dashed" : "dashed-line",
870                 "dotted" : "dotted-line",
871                 "wavy"   : "zigzag" 
872             }. get (self.line_type, None)
873             if style:
874                 printer.dump ("\once \override Glissando #'style = #'%s" % style)
875     def ly_expression (self):
876         return {-1: '\\glissando',
877             1:''}.get (self.span_direction, '')
878
879 class ArpeggioEvent(Event):
880     def __init__ (self):
881         Event.__init__ (self)
882         self.direction = 0
883         self.non_arpeggiate = False
884     def wait_for_note (self):
885         return True
886     def print_before_note (self, printer):
887         if self.non_arpeggiate:
888             printer.dump ("\\arpeggioBracket")
889         else:
890           dir = { -1: "\\arpeggioDown", 1: "\\arpeggioUp" }.get (self.direction, '')
891           if dir:
892               printer.dump (dir)
893     def print_after_note (self, printer):
894         if self.non_arpeggiate or self.direction:
895             printer.dump ("\\arpeggioNeutral")
896     def ly_expression (self):
897         return ('\\arpeggio')
898
899
900 class TieEvent(Event):
901     def ly_expression (self):
902         return '~'
903
904
905 class HairpinEvent (SpanEvent):
906     def set_span_type (self, type):
907         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
908     def hairpin_to_ly (self):
909         if self.span_direction == 1:
910             return '\!'
911         else:
912             return {1: '\<', -1: '\>'}.get (self.span_type, '')
913     
914     def ly_expression (self):
915         return self.hairpin_to_ly ()
916     
917     def print_ly (self, printer):
918         val = self.hairpin_to_ly ()
919         if val:
920             printer.dump (val)
921
922
923
924 class DynamicsEvent (Event):
925     def __init__ (self):
926         Event.__init__ (self)
927         self.type = None
928     def wait_for_note (self):
929         return True
930     def ly_expression (self):
931         if self.type:
932             return '\%s' % self.type
933         else:
934             return
935
936     def print_ly (self, printer):
937         if self.type:
938             printer.dump ("\\%s" % self.type)
939
940 class MarkEvent (Event):
941     def __init__ (self, text="\\default"):
942         Event.__init__ (self)
943         self.mark = text
944     def wait_for_note (self):
945         return False
946     def ly_contents (self):
947         if self.mark:
948             return '%s' % self.mark
949         else:
950             return "\"ERROR\""
951     def ly_expression (self):
952         return '\\mark %s' % self.ly_contents ()
953
954 class MusicGlyphMarkEvent (MarkEvent):
955     def ly_contents (self):
956         if self.mark:
957             return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
958         else:
959             return ''
960
961
962 class TextEvent (Event):
963     def __init__ (self):
964         Event.__init__ (self)
965         self.Text = None
966         self.force_direction = None
967         self.markup = ''
968     def wait_for_note (self):
969         return True
970
971     def direction_mod (self):
972         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
973
974     def ly_expression (self):
975         base_string = '%s\"%s\"'
976         if self.markup:
977             base_string = '%s\markup{ ' + self.markup + ' {%s} }'
978         return base_string % (self.direction_mod (), self.text)
979
980 class ArticulationEvent (Event):
981     def __init__ (self):
982         Event.__init__ (self)
983         self.type = None
984         self.force_direction = None
985     def wait_for_note (self):
986         return True
987
988     def direction_mod (self):
989         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
990
991     def ly_expression (self):
992         return '%s\\%s' % (self.direction_mod (), self.type)
993
994 class ShortArticulationEvent (ArticulationEvent):
995     def direction_mod (self):
996         # default is -
997         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
998     def ly_expression (self):
999         if self.type:
1000             return '%s%s' % (self.direction_mod (), self.type)
1001         else:
1002             return ''
1003
1004 class NoDirectionArticulationEvent (ArticulationEvent):
1005     def ly_expression (self):
1006         if self.type:
1007             return '\\%s' % self.type
1008         else:
1009             return ''
1010
1011 class MarkupEvent (ShortArticulationEvent):
1012     def __init__ (self):
1013         ArticulationEvent.__init__ (self)
1014         self.contents = None
1015     def ly_expression (self):
1016         if self.contents:
1017             return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
1018         else:
1019             return ''
1020
1021 class FretEvent (MarkupEvent):
1022     def __init__ (self):
1023         MarkupEvent.__init__ (self)
1024         self.force_direction = 1
1025         self.strings = 6
1026         self.frets = 4
1027         self.barre = None
1028         self.elements = []
1029     def ly_expression (self):
1030         val = ""
1031         if self.strings <> 6:
1032             val += "w:%s;" % self.strings
1033         if self.frets <> 4:
1034             val += "h:%s;" % self.frets
1035         if self.barre and len (self.barre) >= 3:
1036             val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
1037         have_fingering = False
1038         for i in self.elements:
1039             if len (i) > 1:
1040                 val += "%s-%s" % (i[0], i[1])
1041             if len (i) > 2:
1042                 have_fingering = True
1043                 val += "-%s" % i[2]
1044             val += ";"
1045         if have_fingering:
1046             val = "f:1;" + val
1047         if val:
1048             return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
1049         else:
1050             return ''
1051
1052 class TremoloEvent (ArticulationEvent):
1053     def __init__ (self):
1054         Event.__init__ (self)
1055         self.bars = 0
1056
1057     def ly_expression (self):
1058         str=''
1059         if self.bars and self.bars > 0:
1060             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
1061         return str
1062
1063 class BendEvent (ArticulationEvent):
1064     def __init__ (self):
1065         Event.__init__ (self)
1066         self.alter = 0
1067     def ly_expression (self):
1068         if self.alter:
1069             return "-\\bendAfter #%s" % self.alter
1070         else:
1071             return ''
1072
1073 class RhythmicEvent(Event):
1074     def __init__ (self):
1075         Event.__init__ (self)
1076         self.duration = Duration()
1077         
1078     def get_length (self):
1079         return self.duration.get_length()
1080         
1081     def get_properties (self):
1082         return ("'duration %s"
1083                 % self.duration.lisp_expression ())
1084     
1085 class RestEvent (RhythmicEvent):
1086     def __init__ (self):
1087         RhythmicEvent.__init__ (self)
1088         self.pitch = None
1089     def ly_expression (self):
1090         if self.pitch:
1091             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1092         else:
1093             return 'r%s' % self.duration.ly_expression ()
1094     
1095     def print_ly (self, printer):
1096         if self.pitch:
1097             self.pitch.print_ly (printer)
1098             self.duration.print_ly (printer)
1099             printer ('\\rest')
1100         else:
1101             printer('r')
1102             self.duration.print_ly (printer)
1103
1104 class SkipEvent (RhythmicEvent):
1105     def ly_expression (self):
1106         return 's%s' % self.duration.ly_expression () 
1107
1108 class NoteEvent(RhythmicEvent):
1109     def  __init__ (self):
1110         RhythmicEvent.__init__ (self)
1111         self.pitch = None
1112         self.drum_type = None
1113         self.cautionary = False
1114         self.forced_accidental = False
1115         
1116     def get_properties (self):
1117         str = RhythmicEvent.get_properties (self)
1118         
1119         if self.pitch:
1120             str += self.pitch.lisp_expression ()
1121         elif self.drum_type:
1122             str += "'drum-type '%s" % self.drum_type
1123
1124         return str
1125     
1126     def pitch_mods (self):
1127         excl_question = ''
1128         if self.cautionary:
1129             excl_question += '?'
1130         if self.forced_accidental:
1131             excl_question += '!'
1132
1133         return excl_question
1134     
1135     def ly_expression (self):
1136         if self.pitch:
1137             return '%s%s%s' % (self.pitch.ly_expression (),
1138                                self.pitch_mods(),
1139                                self.duration.ly_expression ())
1140         elif self.drum_type:
1141             return '%s%s' (self.drum_type,
1142                            self.duration.ly_expression ())
1143
1144     def print_ly (self, printer):
1145         if self.pitch:
1146             self.pitch.print_ly (printer)
1147             printer (self.pitch_mods ())
1148         else:
1149             printer (self.drum_type)
1150
1151         self.duration.print_ly (printer)
1152
1153 class KeySignatureChange (Music):
1154     def __init__ (self):
1155         Music.__init__ (self)
1156         self.scale = []
1157         self.tonic = Pitch()
1158         self.mode = 'major'
1159         
1160     def ly_expression (self):
1161         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1162                      self.mode)
1163     
1164     def lisp_expression (self):
1165         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
1166         scale_str = ("'(%s)" % string.join (pairs))
1167
1168         return """ (make-music 'KeyChangeEvent
1169      'pitch-alist %s) """ % scale_str
1170
1171 class TimeSignatureChange (Music):
1172     def __init__ (self):
1173         Music.__init__ (self)
1174         self.fraction = (4,4)
1175     def ly_expression (self):
1176         return '\\time %d/%d ' % self.fraction
1177     
1178 class ClefChange (Music):
1179     def __init__ (self):
1180         Music.__init__ (self)
1181         self.type = 'G'
1182         self.position = 2
1183         self.octave = 0
1184
1185     def octave_modifier (self):
1186         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1187     def clef_name (self):
1188         return {('G', 2): "treble",
1189                 ('G', 1): "french",
1190                 ('C', 1): "soprano",
1191                 ('C', 2): "mezzosoprano",
1192                 ('C', 3): "alto",
1193                 ('C', 4): "tenor",
1194                 ('C', 5): "baritone",
1195                 ('F', 3): "varbaritone",
1196                 ('F', 4): "bass",
1197                 ('F', 5): "subbass",
1198                 ("percussion", 2): "percussion",
1199                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1200     def ly_expression (self):
1201         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1202
1203     clef_dict = {
1204         "G": ("clefs.G", -2, -6),
1205         "C": ("clefs.C", 0, 0),
1206         "F": ("clefs.F", 2, 6),
1207         }
1208     
1209     def lisp_expression (self):
1210         try:
1211             (glyph, pos, c0) = self.clef_dict[self.type]
1212         except KeyError:
1213             return ""
1214         clefsetting = """
1215         (make-music 'SequentialMusic
1216         'elements (list
1217    (context-spec-music
1218    (make-property-set 'clefGlyph "%s") 'Staff)
1219    (context-spec-music
1220    (make-property-set 'clefPosition %d) 'Staff)
1221    (context-spec-music
1222    (make-property-set 'middleCPosition %d) 'Staff)))
1223 """ % (glyph, pos, c0)
1224         return clefsetting
1225
1226
1227 class StaffChange (Music):
1228     def __init__ (self, staff):
1229         Music.__init__ (self)
1230         self.staff = staff
1231     def ly_expression (self):
1232         if self.staff:
1233             return "\\change Staff=\"%s\"" % self.staff
1234         else:
1235             return ''
1236
1237
1238 class MultiMeasureRest(Music):
1239
1240     def lisp_expression (self):
1241         return """
1242 (make-music
1243   'MultiMeasureRestMusicGroup
1244   'elements
1245   (list (make-music (quote BarCheck))
1246         (make-music
1247           'ChordEvent
1248           'elements
1249           (list (make-music
1250                   'MultiMeasureRestEvent
1251                   'duration
1252                   %s)))
1253         (make-music (quote BarCheck))))
1254 """ % self.duration.lisp_expression ()
1255
1256     def ly_expression (self):
1257         return 'R%s' % self.duration.ly_expression ()
1258
1259
1260 class StaffGroup:
1261     def __init__ (self, command = "StaffGroup"):
1262         self.stafftype = command
1263         self.id = None
1264         self.instrument_name = None
1265         self.short_instrument_name = None
1266         self.symbol = None
1267         self.spanbar = None
1268         self.children = []
1269         self.is_group = True
1270         # part_information is a list with entries of the form
1271         #     [staffid, voicelist]
1272         # where voicelist is a list with entries of the form
1273         #     [voiceid1, [lyricsid11, lyricsid12,...] ]
1274         self.part_information = None
1275
1276     def append_staff (self, staff):
1277         self.children.append (staff)
1278
1279     def set_part_information (self, part_name, staves_info):
1280         if part_name == self.id:
1281             self.part_information = staves_info
1282         else:
1283             for c in self.children:
1284                 c.set_part_information (part_name, staves_info)
1285
1286     def print_ly_contents (self, printer):
1287         for c in self.children:
1288             if c:
1289                 c.print_ly (printer)
1290     def print_ly_overrides (self, printer):
1291         needs_with = False
1292         needs_with |= self.spanbar == "no"
1293         needs_with |= self.instrument_name != None
1294         needs_with |= self.short_instrument_name != None
1295         needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1296         if needs_with:
1297             printer.dump ("\\with {")
1298             if self.instrument_name or self.short_instrument_name:
1299                 printer.dump ("\\consists \"Instrument_name_engraver\"")
1300             if self.spanbar == "no":
1301                 printer.dump ("\\override SpanBar #'transparent = ##t")
1302             brack = {"brace": "SystemStartBrace",
1303                      "none": "f",
1304                      "line": "SystemStartSquare"}.get (self.symbol, None)
1305             if brack:
1306                 printer.dump ("systemStartDelimiter = #'%s" % brack)
1307             printer.dump ("}")
1308
1309     def print_ly (self, printer):
1310         if self.stafftype:
1311             printer.dump ("\\new %s" % self.stafftype)
1312         self.print_ly_overrides (printer)
1313         printer.dump ("<<")
1314         printer.newline ()
1315         if self.stafftype and self.instrument_name:
1316             printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype, 
1317                     escape_instrument_string (self.instrument_name)))
1318             printer.newline ()
1319         if self.stafftype and self.short_instrument_name:
1320             printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1321                     escape_instrument_string (self.short_instrument_name)))
1322             printer.newline ()
1323         self.print_ly_contents (printer)
1324         printer.newline ()
1325         printer.dump (">>")
1326         printer.newline ()
1327
1328
1329 class Staff (StaffGroup):
1330     def __init__ (self, command = "Staff"):
1331         StaffGroup.__init__ (self, command)
1332         self.is_group = False
1333         self.part = None
1334         self.voice_command = "Voice"
1335         self.substafftype = None
1336
1337     def print_ly_overrides (self, printer):
1338         pass
1339
1340     def print_ly_contents (self, printer):
1341         if not self.id or not self.part_information:
1342             return
1343         sub_staff_type = self.substafftype
1344         if not sub_staff_type:
1345             sub_staff_type = self.stafftype
1346
1347         for [staff_id, voices] in self.part_information:
1348             if staff_id:
1349                 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1350             else:
1351                 printer ('\\context %s << ' % sub_staff_type)
1352             printer.newline ()
1353             n = 0
1354             nr_voices = len (voices)
1355             for [v, lyrics] in voices:
1356                 n += 1
1357                 voice_count_text = ''
1358                 if nr_voices > 1:
1359                     voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1360                                         3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1361                 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1362                 printer.newline ()
1363
1364                 for l in lyrics:
1365                     printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1366                     printer.newline()
1367             printer ('>>')
1368
1369     def print_ly (self, printer):
1370         if self.part_information and len (self.part_information) > 1:
1371             self.stafftype = "PianoStaff"
1372             self.substafftype = "Staff"
1373         StaffGroup.print_ly (self, printer)
1374
1375 class TabStaff (Staff):
1376     def __init__ (self, command = "TabStaff"):
1377         Staff.__init__ (self, command)
1378         self.string_tunings = []
1379         self.tablature_format = None
1380         self.voice_command = "TabVoice"
1381     def print_ly_overrides (self, printer):
1382         if self.string_tunings or self.tablature_format:
1383             printer.dump ("\\with {")
1384             if self.string_tunings:
1385                 printer.dump ("stringTunings = #'(")
1386                 for i in self.string_tunings:
1387                     printer.dump ("%s" % i.semitones ())
1388                 printer.dump (")")
1389             if self.tablature_format:
1390                 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1391             printer.dump ("}")
1392
1393
1394 class DrumStaff (Staff):
1395     def __init__ (self, command = "DrumStaff"):
1396         Staff.__init__ (self, command)
1397         self.drum_style_table = None
1398         self.voice_command = "DrumVoice"
1399     def print_ly_overrides (self, printer):
1400         if self.drum_style_table:
1401             printer.dump ("\with {")
1402             printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1403             printer.dump ("}")
1404
1405 class RhythmicStaff (Staff):
1406     def __init__ (self, command = "RhythmicStaff"):
1407         Staff.__init__ (self, command)
1408
1409
1410 def test_pitch ():
1411     bflat = Pitch()
1412     bflat.alteration = -1
1413     bflat.step =  6
1414     bflat.octave = -1
1415     fifth = Pitch()
1416     fifth.step = 4
1417     down = Pitch ()
1418     down.step = -4
1419     down.normalize ()
1420     
1421     
1422     print bflat.semitones()
1423     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
1424     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1425
1426     print bflat.semitones(), 'down'
1427     print bflat.transposed (down)
1428     print bflat.transposed (down).transposed (down)
1429     print bflat.transposed (down).transposed (down).transposed (down)
1430
1431
1432
1433 def test_printer ():
1434     def make_note ():
1435         evc = ChordEvent()
1436         n = NoteEvent()
1437         evc.append (n)
1438         return n
1439
1440     def make_tup ():
1441         m = SequentialMusic()
1442         m.append (make_note ())
1443         m.append (make_note ())
1444         m.append (make_note ())
1445
1446         
1447         t = TimeScaledMusic ()
1448         t.numerator = 2
1449         t.denominator = 3
1450         t.element = m
1451         return t
1452
1453     m = SequentialMusic ()
1454     m.append (make_tup ())
1455     m.append (make_tup ())
1456     m.append (make_tup ())
1457     
1458     printer = Output_printer()
1459     m.print_ly (printer)
1460     printer.newline ()
1461     
1462 def test_expr ():
1463     m = SequentialMusic()
1464     l = 2  
1465     evc = ChordEvent()
1466     n = NoteEvent()
1467     n.duration.duration_log = l
1468     n.pitch.step = 1
1469     evc.insert_around (None, n, 0)
1470     m.insert_around (None, evc, 0)
1471
1472     evc = ChordEvent()
1473     n = NoteEvent()
1474     n.duration.duration_log = l
1475     n.pitch.step = 3
1476     evc.insert_around (None, n, 0)
1477     m.insert_around (None, evc, 0)
1478
1479     evc = ChordEvent()
1480     n = NoteEvent()
1481     n.duration.duration_log = l
1482     n.pitch.step = 2 
1483     evc.insert_around (None, n, 0)
1484     m.insert_around (None, evc, 0)
1485
1486     evc = ClefChange()
1487     evc.type = 'treble'
1488     m.insert_around (None, evc, 0)
1489
1490     evc = ChordEvent()
1491     tonic = Pitch ()
1492     tonic.step = 2
1493     tonic.alteration = -2
1494     n = KeySignatureChange()
1495     n.tonic=tonic.copy()
1496     n.scale = [0, 0, -2, 0, 0,-2,-2]
1497     
1498     evc.insert_around (None, n, 0)
1499     m.insert_around (None, evc, 0)
1500
1501     return m
1502
1503
1504 if __name__ == '__main__':
1505     test_printer ()
1506     raise 'bla'
1507     test_pitch()
1508     
1509     expr = test_expr()
1510     expr.set_start (Rational (0))
1511     print expr.ly_expression()
1512     start = Rational (0,4)
1513     stop = Rational (4,2)
1514     def sub(x, start=start, stop=stop):
1515         ok = x.start >= start and x.start +x.get_length() <= stop
1516         return ok
1517     
1518     print expr.lisp_sub_expression(sub)
1519