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