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