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