Fix #1194.
[lilypond/mpolesky.git] / python / musicexp.py
blob1c9f3effb18ac9fb0b9d67754560e1064420c62a
1 # -*- coding: utf-8 -*-
2 import inspect
3 import sys
4 import string
5 import re
6 import lilylib as ly
8 _ = ly._
10 from rational import Rational
12 # Store previously converted pitch for \relative conversion as a global state variable
13 previous_pitch = None
14 relative_pitches = False
16 def warning (str):
17 ly.stderr_write ((_ ("warning: %s") % str) + "\n")
20 def escape_instrument_string (input_string):
21 retstring = string.replace (input_string, "\"", "\\\"")
22 if re.match ('.*[\r\n]+.*', retstring):
23 rx = re.compile (r'[\n\r]+')
24 strings = rx.split (retstring)
25 retstring = "\\markup { \\column { "
26 for s in strings:
27 retstring += "\\line {\"" + s + "\"} "
28 retstring += "} }"
29 else:
30 retstring = "\"" + retstring + "\""
31 return retstring
33 class Output_stack_element:
34 def __init__ (self):
35 self.factor = Rational (1)
36 def copy (self):
37 o = Output_stack_element()
38 o.factor = self.factor
39 return o
41 class Output_printer:
43 """A class that takes care of formatting (eg.: indenting) a
44 Music expression as a .ly file.
46 """
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
58 def set_file (self, file):
59 self._file = file
61 def dump_version (self):
62 self.newline ()
63 self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
64 self.newline ()
66 def get_indent (self):
67 return self._nesting * self._indent
69 def override (self):
70 last = self._output_state_stack[-1]
71 self._output_state_stack.append (last.copy())
73 def add_factor (self, factor):
74 self.override()
75 self._output_state_stack[-1].factor *= factor
77 def revert (self):
78 del self._output_state_stack[-1]
79 if not self._output_state_stack:
80 raise 'empty'
82 def duration_factor (self):
83 return self._output_state_stack[-1].factor
85 def print_verbatim (self, str):
86 self._line += str
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)
99 def print_duration_string (self, str):
100 if self._last_duration == str:
101 return
103 self.unformatted_output (str)
105 def add_word (self, str):
106 if (len (str) + 1 + len (self._line) > self._line_len):
107 self.newline()
108 self._skipspace = True
110 if not self._skipspace:
111 self._line += ' '
112 self.unformatted_output (str)
113 self._skipspace = False
115 def newline (self):
116 self._file.write (self._line + '\n')
117 self._line = ' ' * self._indent * self._nesting
118 self._skipspace = True
120 def skipspace (self):
121 self._skipspace = True
123 def __call__(self, arg):
124 self.dump (arg)
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)
136 def close (self):
137 self.newline ()
138 self._file.close ()
139 self._file = None
142 class Duration:
143 def __init__ (self):
144 self.duration_log = 0
145 self.dots = 0
146 self.factor = Rational (1)
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 ())
155 def ly_expression (self, factor = None, scheme_mode = False):
156 if not factor:
157 factor = self.factor
159 if self.duration_log < 0:
160 if scheme_mode:
161 longer_dict = {-1: "breve", -2: "longa"}
162 else:
163 longer_dict = {-1: "\\breve", -2: "\\longa"}
164 str = longer_dict.get (self.duration_log, "1")
165 else:
166 str = '%d' % (1 << self.duration_log)
167 str += '.'*self.dots
169 if factor <> Rational (1,1):
170 if factor.denominator () <> 1:
171 str += '*%d/%d' % (factor.numerator (), factor.denominator ())
172 else:
173 str += '*%d' % factor.numerator ()
175 return str
177 def print_ly (self, outputter):
178 str = self.ly_expression (self.factor / outputter.duration_factor ())
179 outputter.print_duration_string (str)
181 def __repr__(self):
182 return self.ly_expression()
184 def copy (self):
185 d = Duration ()
186 d.dots = self.dots
187 d.duration_log = self.duration_log
188 d.factor = self.factor
189 return d
191 def get_length (self):
192 dot_fact = Rational( (1 << (1 + self.dots))-1,
193 1 << self.dots)
195 log = abs (self.duration_log)
196 dur = 1 << log
197 if self.duration_log < 0:
198 base = Rational (dur)
199 else:
200 base = Rational (1, dur)
202 return base * dot_fact * self.factor
205 # Implement the different note names for the various languages
206 def pitch_generic (pitch, notenames, accidentals):
207 str = notenames[pitch.step]
208 halftones = int (pitch.alteration)
209 if halftones < 0:
210 str += accidentals[0] * (-halftones)
211 elif pitch.alteration > 0:
212 str += accidentals[3] * (halftones)
213 # Handle remaining fraction to pitch.alteration (for microtones)
214 if (halftones != pitch.alteration):
215 if None in accidentals[1:3]:
216 warning (_ ("Language does not support microtones contained in the piece"))
217 else:
218 try:
219 str += {-0.5: accidentals[1], 0.5: accidentals[2]}[pitch.alteration-halftones]
220 except KeyError:
221 warning (_ ("Language does not support microtones contained in the piece"))
222 return str
224 def pitch_general (pitch):
225 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['es', 'eh', 'ih', 'is'])
226 return str.replace ('aes', 'as').replace ('ees', 'es')
228 def pitch_nederlands (pitch):
229 return pitch_general (pitch)
231 def pitch_english (pitch):
232 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['f', 'qf', 'qs', 's'])
233 return str.replace ('aes', 'as').replace ('ees', 'es')
235 def pitch_deutsch (pitch):
236 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['es', 'eh', 'ih', 'is'])
237 return str.replace ('hes', 'b').replace ('aes', 'as').replace ('ees', 'es')
239 def pitch_norsk (pitch):
240 return pitch_deutsch (pitch)
242 def pitch_svenska (pitch):
243 str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', None, None, 'iss'])
244 return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es')
246 def pitch_italiano (pitch):
247 str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd'])
248 return str
250 def pitch_catalan (pitch):
251 return pitch_italiano (pitch)
253 def pitch_espanol (pitch):
254 str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 's'])
255 return str
257 def pitch_vlaams (pitch):
258 str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', None, None, 'k'])
259 return str
261 def set_pitch_language (language):
262 global pitch_generating_function
263 function_dict = {
264 "nederlands": pitch_nederlands,
265 "english": pitch_english,
266 "deutsch": pitch_deutsch,
267 "norsk": pitch_norsk,
268 "svenska": pitch_svenska,
269 "italiano": pitch_italiano,
270 "catalan": pitch_catalan,
271 "espanol": pitch_espanol,
272 "vlaams": pitch_vlaams}
273 pitch_generating_function = function_dict.get (language, pitch_general)
275 # global variable to hold the formatting function.
276 pitch_generating_function = pitch_general
279 class Pitch:
280 def __init__ (self):
281 self.alteration = 0
282 self.step = 0
283 self.octave = 0
284 self._force_absolute_pitch = False
286 def __repr__(self):
287 return self.ly_expression()
289 def transposed (self, interval):
290 c = self.copy ()
291 c.alteration += interval.alteration
292 c.step += interval.step
293 c.octave += interval.octave
294 c.normalize ()
296 target_st = self.semitones() + interval.semitones()
297 c.alteration += target_st - c.semitones()
298 return c
300 def normalize (c):
301 while c.step < 0:
302 c.step += 7
303 c.octave -= 1
304 c.octave += c.step / 7
305 c.step = c.step % 7
307 def lisp_expression (self):
308 return '(ly:make-pitch %d %d %d)' % (self.octave,
309 self.step,
310 self.alteration)
312 def copy (self):
313 p = Pitch ()
314 p.alteration = self.alteration
315 p.step = self.step
316 p.octave = self.octave
317 return p
319 def steps (self):
320 return self.step + self.octave *7
322 def semitones (self):
323 return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
325 def ly_step_expression (self):
326 return pitch_generating_function (self)
328 def absolute_pitch (self):
329 if self.octave >= 0:
330 return "'" * (self.octave + 1)
331 elif self.octave < -1:
332 return "," * (-self.octave - 1)
333 else:
334 return ''
336 def relative_pitch (self):
337 global previous_pitch
338 if not previous_pitch:
339 previous_pitch = self
340 return self.absolute_pitch ()
341 previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step
342 this_pitch_steps = self.octave * 7 + self.step
343 pitch_diff = (this_pitch_steps - previous_pitch_steps)
344 previous_pitch = self
345 if pitch_diff > 3:
346 return "'" * ((pitch_diff + 3) / 7)
347 elif pitch_diff < -3:
348 return "," * ((-pitch_diff + 3) / 7)
349 else:
350 return ""
352 def ly_expression (self):
353 str = self.ly_step_expression ()
354 if relative_pitches and not self._force_absolute_pitch:
355 str += self.relative_pitch ()
356 else:
357 str += self.absolute_pitch ()
359 return str
361 def print_ly (self, outputter):
362 outputter (self.ly_expression())
364 class Music:
365 def __init__ (self):
366 self.parent = None
367 self.start = Rational (0)
368 self.comment = ''
369 self.identifier = None
371 def get_length(self):
372 return Rational (0)
374 def get_properties (self):
375 return ''
377 def has_children (self):
378 return False
380 def get_index (self):
381 if self.parent:
382 return self.parent.elements.index (self)
383 else:
384 return None
385 def name (self):
386 return self.__class__.__name__
388 def lisp_expression (self):
389 name = self.name()
391 props = self.get_properties ()
393 return "(make-music '%s %s)" % (name, props)
395 def set_start (self, start):
396 self.start = start
398 def find_first (self, predicate):
399 if predicate (self):
400 return self
401 return None
403 def print_comment (self, printer, text = None):
404 if not text:
405 text = self.comment
407 if not text:
408 return
410 if text == '\n':
411 printer.newline ()
412 return
414 lines = string.split (text, '\n')
415 for l in lines:
416 if l:
417 printer.unformatted_output ('% ' + l)
418 printer.newline ()
421 def print_with_identifier (self, printer):
422 if self.identifier:
423 printer ("\\%s" % self.identifier)
424 else:
425 self.print_ly (printer)
427 def print_ly (self, printer):
428 printer (self.ly_expression ())
430 class MusicWrapper (Music):
431 def __init__ (self):
432 Music.__init__(self)
433 self.element = None
434 def print_ly (self, func):
435 self.element.print_ly (func)
437 class ModeChangingMusicWrapper (MusicWrapper):
438 def __init__ (self):
439 MusicWrapper.__init__ (self)
440 self.mode = 'notemode'
442 def print_ly (self, func):
443 func ('\\%s' % self.mode)
444 MusicWrapper.print_ly (self, func)
446 class RelativeMusic (MusicWrapper):
447 def __init__ (self):
448 MusicWrapper.__init__ (self)
449 self.basepitch = None
451 def print_ly (self, func):
452 global previous_pitch
453 global relative_pitches
454 prev_relative_pitches = relative_pitches
455 relative_pitches = True
456 previous_pitch = self.basepitch
457 if not previous_pitch:
458 previous_pitch = Pitch ()
459 func ('\\relative %s%s' % (pitch_generating_function (previous_pitch),
460 previous_pitch.absolute_pitch ()))
461 MusicWrapper.print_ly (self, func)
462 relative_pitches = prev_relative_pitches
464 class TimeScaledMusic (MusicWrapper):
465 def __init__ (self):
466 MusicWrapper.__init__ (self)
467 self.numerator = 1
468 self.denominator = 1
469 self.display_number = "actual" # valid values "actual" | "both" | None
470 # Display the basic note length for the tuplet:
471 self.display_type = None # value values "actual" | "both" | None
472 self.display_bracket = "bracket" # valid values "bracket" | "curved" | None
473 self.actual_type = None # The actually played unit of the scaling
474 self.normal_type = None # The basic unit of the scaling
475 self.display_numerator = None
476 self.display_denominator = None
478 def print_ly (self, func):
479 if self.display_bracket == None:
480 func ("\\once \\override TupletBracket #'stencil = ##f")
481 func.newline ()
482 elif self.display_bracket == "curved":
483 warning (_ ("Tuplet brackets of curved shape are not correctly implemented"))
484 func ("\\once \\override TupletBracket #'stencil = #ly:slur::print")
485 func.newline ()
487 base_number_function = {None: "#f",
488 "actual": "tuplet-number::calc-denominator-text",
489 "both": "tuplet-number::calc-fraction-text"}.get (self.display_number, None)
490 # If we have non-standard numerator/denominator, use our custom function
491 if self.display_number == "actual" and self.display_denominator:
492 base_number_function = "(tuplet-number::non-default-tuplet-denominator-text %s)" % self.display_denominator
493 elif self.display_number == "both" and (self.display_denominator or self.display_numerator):
494 if self.display_numerator:
495 num = self.display_numerator
496 else:
497 num = "#f"
498 if self.display_denominator:
499 den = self.display_denominator
500 else:
501 den = "#f"
502 base_number_function = "(tuplet-number::non-default-tuplet-fraction-text %s %s)" % (den, num)
505 if self.display_type == "actual" and self.normal_type:
506 # Obtain the note duration in scheme-mode, i.e. \longa as \\longa
507 base_duration = self.normal_type.ly_expression (None, True)
508 func ("\\once \\override TupletNumber #'text = #(tuplet-number::append-note-wrapper %s \"%s\")" %
509 (base_number_function, base_duration))
510 func.newline ()
511 elif self.display_type == "both": # TODO: Implement this using actual_type and normal_type!
512 if self.display_number == None:
513 func ("\\once \\override TupletNumber #'stencil = ##f")
514 func.newline ()
515 elif self.display_number == "both":
516 den_duration = self.normal_type.ly_expression (None, True)
517 # If we don't have an actual type set, use the normal duration!
518 if self.actual_type:
519 num_duration = self.actual_type.ly_expression (None, True)
520 else:
521 num_duration = den_duration
522 if (self.display_denominator or self.display_numerator):
523 func ("\\once \\override TupletNumber #'text = #(tuplet-number::non-default-fraction-with-notes %s \"%s\" %s \"%s\")" %
524 (self.display_denominator, den_duration,
525 self.display_numerator, num_duration))
526 func.newline ()
527 else:
528 func ("\\once \\override TupletNumber #'text = #(tuplet-number::fraction-with-notes \"%s\" \"%s\")" %
529 (den_duration, num_duration))
530 func.newline ()
531 else:
532 if self.display_number == None:
533 func ("\\once \\override TupletNumber #'stencil = ##f")
534 func.newline ()
535 elif self.display_number == "both":
536 func ("\\once \\override TupletNumber #'text = #%s" % base_number_function)
537 func.newline ()
539 func ('\\times %d/%d ' %
540 (self.numerator, self.denominator))
541 func.add_factor (Rational (self.numerator, self.denominator))
542 MusicWrapper.print_ly (self, func)
543 func.revert ()
545 class NestedMusic(Music):
546 def __init__ (self):
547 Music.__init__ (self)
548 self.elements = []
550 def append (self, what):
551 if what:
552 self.elements.append (what)
554 def has_children (self):
555 return self.elements
557 def insert_around (self, succ, elt, dir):
558 assert elt.parent == None
559 assert succ == None or succ in self.elements
562 idx = 0
563 if succ:
564 idx = self.elements.index (succ)
565 if dir > 0:
566 idx += 1
567 else:
568 if dir < 0:
569 idx = 0
570 elif dir > 0:
571 idx = len (self.elements)
573 self.elements.insert (idx, elt)
574 elt.parent = self
576 def get_properties (self):
577 return ("'elements (list %s)"
578 % string.join (map (lambda x: x.lisp_expression(),
579 self.elements)))
581 def get_subset_properties (self, predicate):
582 return ("'elements (list %s)"
583 % string.join (map (lambda x: x.lisp_expression(),
584 filter ( predicate, self.elements))))
585 def get_neighbor (self, music, dir):
586 assert music.parent == self
587 idx = self.elements.index (music)
588 idx += dir
589 idx = min (idx, len (self.elements) -1)
590 idx = max (idx, 0)
592 return self.elements[idx]
594 def delete_element (self, element):
595 assert element in self.elements
597 self.elements.remove (element)
598 element.parent = None
600 def set_start (self, start):
601 self.start = start
602 for e in self.elements:
603 e.set_start (start)
605 def find_first (self, predicate):
606 r = Music.find_first (self, predicate)
607 if r:
608 return r
610 for e in self.elements:
611 r = e.find_first (predicate)
612 if r:
613 return r
614 return None
616 class SequentialMusic (NestedMusic):
617 def get_last_event_chord (self):
618 value = None
619 at = len( self.elements ) - 1
620 while (at >= 0 and
621 not isinstance (self.elements[at], ChordEvent) and
622 not isinstance (self.elements[at], BarLine)):
623 at -= 1
625 if (at >= 0 and isinstance (self.elements[at], ChordEvent)):
626 value = self.elements[at]
627 return value
629 def print_ly (self, printer, newline = True):
630 printer ('{')
631 if self.comment:
632 self.print_comment (printer)
634 if newline:
635 printer.newline()
636 for e in self.elements:
637 e.print_ly (printer)
639 printer ('}')
640 if newline:
641 printer.newline()
643 def lisp_sub_expression (self, pred):
644 name = self.name()
647 props = self.get_subset_properties (pred)
649 return "(make-music '%s %s)" % (name, props)
651 def set_start (self, start):
652 for e in self.elements:
653 e.set_start (start)
654 start += e.get_length()
656 class RepeatedMusic:
657 def __init__ (self):
658 self.repeat_type = "volta"
659 self.repeat_count = 2
660 self.endings = []
661 self.music = None
662 def set_music (self, music):
663 if isinstance (music, Music):
664 self.music = music
665 elif isinstance (music, list):
666 self.music = SequentialMusic ()
667 self.music.elements = music
668 else:
669 warning (_ ("unable to set the music %(music)s for the repeat %(repeat)s") % \
670 {'music':music, 'repeat':self})
671 def add_ending (self, music):
672 self.endings.append (music)
673 def print_ly (self, printer):
674 printer.dump ('\\repeat %s %s' % (self.repeat_type, self.repeat_count))
675 if self.music:
676 self.music.print_ly (printer)
677 else:
678 warning (_ ("encountered repeat without body"))
679 printer.dump ('{}')
680 if self.endings:
681 printer.dump ('\\alternative {')
682 for e in self.endings:
683 e.print_ly (printer)
684 printer.dump ('}')
687 class Lyrics:
688 def __init__ (self):
689 self.lyrics_syllables = []
691 def print_ly (self, printer):
692 printer.dump ("\lyricmode {")
693 for l in self.lyrics_syllables:
694 printer.dump ( "%s " % l )
695 printer.dump ("}")
697 def ly_expression (self):
698 lstr = "\lyricmode {\n "
699 for l in self.lyrics_syllables:
700 lstr += l + " "
701 lstr += "\n}"
702 return lstr
705 class Header:
706 def __init__ (self):
707 self.header_fields = {}
708 def set_field (self, field, value):
709 self.header_fields[field] = value
711 def print_ly (self, printer):
712 printer.dump ("\header {")
713 printer.newline ()
714 for (k,v) in self.header_fields.items ():
715 if v:
716 printer.dump ('%s = %s' % (k,v))
717 printer.newline ()
718 printer.dump ("}")
719 printer.newline ()
720 printer.newline ()
723 class Paper:
724 def __init__ (self):
725 self.global_staff_size = -1
726 # page size
727 self.page_width = -1
728 self.page_height = -1
729 # page margins
730 self.top_margin = -1
731 self.bottom_margin = -1
732 self.left_margin = -1
733 self.right_margin = -1
734 self.system_left_margin = -1
735 self.system_right_margin = -1
736 self.system_distance = -1
737 self.top_system_distance = -1
739 def print_length_field (self, printer, field, value):
740 if value >= 0:
741 printer.dump ("%s = %s\\cm" % (field, value))
742 printer.newline ()
743 def print_ly (self, printer):
744 if self.global_staff_size > 0:
745 printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size)
746 printer.newline ()
747 printer.dump ('\\paper {')
748 printer.newline ()
749 self.print_length_field (printer, "paper-width", self.page_width)
750 self.print_length_field (printer, "paper-height", self.page_height)
751 self.print_length_field (printer, "top-margin", self.top_margin)
752 self.print_length_field (printer, "botton-margin", self.bottom_margin)
753 self.print_length_field (printer, "left-margin", self.left_margin)
754 # TODO: maybe set line-width instead of right-margin?
755 self.print_length_field (printer, "right-margin", self.right_margin)
756 # TODO: What's the corresponding setting for system_left_margin and
757 # system_right_margin in LilyPond?
758 self.print_length_field (printer, "between-system-space", self.system_distance)
759 self.print_length_field (printer, "page-top-space", self.top_system_distance)
761 printer.dump ('}')
762 printer.newline ()
764 class Layout:
765 def __init__ (self):
766 self.context_dict = {}
767 def add_context (self, context):
768 if not self.context_dict.has_key (context):
769 self.context_dict[context] = []
770 def set_context_item (self, context, item):
771 self.add_context (context)
772 if not item in self.context_dict[context]:
773 self.context_dict[context].append (item)
774 def print_ly (self, printer):
775 if self.context_dict.items ():
776 printer.dump ('\\layout {')
777 printer.newline ()
778 for (context, defs) in self.context_dict.items ():
779 printer.dump ('\\context { \\%s' % context)
780 printer.newline ()
781 for d in defs:
782 printer.dump (d)
783 printer.newline ()
784 printer.dump ('}')
785 printer.newline ()
786 printer.dump ('}')
787 printer.newline ()
790 class ChordEvent (NestedMusic):
791 def __init__ (self):
792 NestedMusic.__init__ (self)
793 self.after_grace_elements = None
794 self.grace_elements = None
795 self.grace_type = None
796 def append_grace (self, element):
797 if element:
798 if not self.grace_elements:
799 self.grace_elements = SequentialMusic ()
800 self.grace_elements.append (element)
801 def append_after_grace (self, element):
802 if element:
803 if not self.after_grace_elements:
804 self.after_grace_elements = SequentialMusic ()
805 self.after_grace_elements.append (element)
807 def has_elements (self):
808 return [e for e in self.elements if
809 isinstance (e, NoteEvent) or isinstance (e, RestEvent)] != []
812 def get_length (self):
813 l = Rational (0)
814 for e in self.elements:
815 l = max(l, e.get_length())
816 return l
818 def get_duration (self):
819 note_events = [e for e in self.elements if
820 isinstance (e, NoteEvent) or isinstance (e, RestEvent)]
821 if note_events:
822 return note_events[0].duration
823 else:
824 return None
826 def print_ly (self, printer):
827 note_events = [e for e in self.elements if
828 isinstance (e, NoteEvent)]
830 rest_events = [e for e in self.elements if
831 isinstance (e, RhythmicEvent)
832 and not isinstance (e, NoteEvent)]
834 other_events = [e for e in self.elements if
835 not isinstance (e, RhythmicEvent)]
837 if self.after_grace_elements:
838 printer ('\\afterGrace {')
840 if self.grace_elements and self.elements:
841 if self.grace_type:
842 printer ('\\%s' % self.grace_type)
843 else:
844 printer ('\\grace')
845 # don't print newlines after the { and } braces
846 self.grace_elements.print_ly (printer, False)
847 elif self.grace_elements: # no self.elements!
848 warning (_ ("Grace note with no following music: %s") % self.grace_elements)
849 if self.grace_type:
850 printer ('\\%s' % self.grace_type)
851 else:
852 printer ('\\grace')
853 self.grace_elements.print_ly (printer, False)
854 printer ('{}')
856 # Print all overrides and other settings needed by the
857 # articulations/ornaments before the note
858 for e in other_events:
859 e.print_before_note (printer)
861 if rest_events:
862 rest_events[0].print_ly (printer)
863 elif len (note_events) == 1:
864 note_events[0].print_ly (printer)
865 elif note_events:
866 global previous_pitch
867 pitches = []
868 basepitch = None
869 for x in note_events:
870 pitches.append (x.chord_element_ly ())
871 if not basepitch:
872 basepitch = previous_pitch
873 printer ('<%s>' % string.join (pitches))
874 previous_pitch = basepitch
875 duration = self.get_duration ()
876 if duration:
877 duration.print_ly (printer)
878 else:
879 pass
881 for e in other_events:
882 e.print_ly (printer)
884 for e in other_events:
885 e.print_after_note (printer)
887 if self.after_grace_elements:
888 printer ('}')
889 self.after_grace_elements.print_ly (printer, False)
891 self.print_comment (printer)
893 class Partial (Music):
894 def __init__ (self):
895 Music.__init__ (self)
896 self.partial = None
897 def print_ly (self, printer):
898 if self.partial:
899 printer.dump ("\\partial %s" % self.partial.ly_expression ())
901 class BarLine (Music):
902 def __init__ (self):
903 Music.__init__ (self)
904 self.bar_number = 0
905 self.type = None
907 def print_ly (self, printer):
908 bar_symbol = { 'regular': "|", 'dotted': ":", 'dashed': "dashed",
909 'heavy': "|", 'light-light': "||", 'light-heavy': "|.",
910 'heavy-light': ".|", 'heavy-heavy': ".|.", 'tick': "'",
911 'short': "'", 'none': "" }.get (self.type, None)
912 if bar_symbol <> None:
913 printer.dump ('\\bar "%s"' % bar_symbol)
914 else:
915 printer.dump ("|")
917 if self.bar_number > 0 and (self.bar_number % 10) == 0:
918 printer.dump ("\\barNumberCheck #%d " % self.bar_number)
919 elif self.bar_number > 0:
920 printer.print_verbatim (' %% %d' % self.bar_number)
921 printer.newline ()
923 def ly_expression (self):
924 return " | "
926 class Event(Music):
927 def __init__ (self):
928 # strings to print before the note to which an event is attached.
929 # Ignored for notes etc.
930 self.before_note = None
931 self.after_note = None
932 # print something before the note to which an event is attached, e.g. overrides
933 def print_before_note (self, printer):
934 if self.before_note:
935 printer.dump (self.before_note)
936 # print something after the note to which an event is attached, e.g. resetting
937 def print_after_note (self, printer):
938 if self.after_note:
939 printer.dump (self.after_note)
940 pass
942 class SpanEvent (Event):
943 def __init__ (self):
944 Event.__init__ (self)
945 self.span_direction = 0 # start/stop
946 self.line_type = 'solid'
947 self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
948 self.size = 0 # size of e.g. ocrave shift
949 def wait_for_note (self):
950 return True
951 def get_properties(self):
952 return "'span-direction %d" % self.span_direction
953 def set_span_type (self, type):
954 self.span_type = type
956 class SlurEvent (SpanEvent):
957 def print_before_note (self, printer):
958 command = {'dotted': '\\slurDotted',
959 'dashed' : '\\slurDashed'}.get (self.line_type, '')
960 if command and self.span_direction == -1:
961 printer.dump (command)
962 def print_after_note (self, printer):
963 # reset non-solid slur types!
964 command = {'dotted': '\\slurSolid',
965 'dashed' : '\\slurSolid'}.get (self.line_type, '')
966 if command and self.span_direction == -1:
967 printer.dump (command)
968 def ly_expression (self):
969 return {-1: '(', 1:')'}.get (self.span_direction, '')
971 class BeamEvent (SpanEvent):
972 def ly_expression (self):
973 return {-1: '[', 1:']'}.get (self.span_direction, '')
975 class PedalEvent (SpanEvent):
976 def ly_expression (self):
977 return {-1: '\\sustainOn',
978 0:'\\sustainOff\\sustainOn',
979 1:'\\sustainOff'}.get (self.span_direction, '')
981 class TextSpannerEvent (SpanEvent):
982 def ly_expression (self):
983 return {-1: '\\startTextSpan',
984 1:'\\stopTextSpan'}.get (self.span_direction, '')
986 class BracketSpannerEvent (SpanEvent):
987 # Ligature brackets use prefix-notation!!!
988 def print_before_note (self, printer):
989 if self.span_direction == -1:
990 printer.dump ('\[')
991 # the bracket after the last note
992 def print_after_note (self, printer):
993 if self.span_direction == 1:
994 printer.dump ('\]')
995 # we're printing everything in print_(before|after)_note...
996 def ly_expression (self):
997 return '';
1000 class OctaveShiftEvent (SpanEvent):
1001 def wait_for_note (self):
1002 return False
1003 def set_span_type (self, type):
1004 self.span_type = {'up': 1, 'down': -1}.get (type, 0)
1005 def ly_octave_shift_indicator (self):
1006 # convert 8/15 to lilypond indicators (+-1/+-2)
1007 try:
1008 value = {8: 1, 15: 2}[self.size]
1009 except KeyError:
1010 warning (_ ("Invalid octave shift size found: %s. Using no shift.") % self.size)
1011 value = 0
1012 # negative values go up!
1013 value *= -1*self.span_type
1014 return value
1015 def ly_expression (self):
1016 dir = self.ly_octave_shift_indicator ()
1017 value = ''
1018 if dir:
1019 value = '\ottava #%s' % dir
1020 return {
1021 -1: value,
1022 1: '\ottava #0'}.get (self.span_direction, '')
1024 class TrillSpanEvent (SpanEvent):
1025 def ly_expression (self):
1026 return {-1: '\\startTrillSpan',
1027 0: '', # no need to write out anything for type='continue'
1028 1:'\\stopTrillSpan'}.get (self.span_direction, '')
1030 class GlissandoEvent (SpanEvent):
1031 def print_before_note (self, printer):
1032 if self.span_direction == -1:
1033 style= {
1034 "dashed" : "dashed-line",
1035 "dotted" : "dotted-line",
1036 "wavy" : "zigzag"
1037 }. get (self.line_type, None)
1038 if style:
1039 printer.dump ("\\once \\override Glissando #'style = #'%s" % style)
1040 def ly_expression (self):
1041 return {-1: '\\glissando',
1042 1:''}.get (self.span_direction, '')
1044 class ArpeggioEvent(Event):
1045 def __init__ (self):
1046 Event.__init__ (self)
1047 self.direction = 0
1048 self.non_arpeggiate = False
1049 def wait_for_note (self):
1050 return True
1051 def print_before_note (self, printer):
1052 if self.non_arpeggiate:
1053 printer.dump ("\\arpeggioBracket")
1054 else:
1055 dir = { -1: "\\arpeggioArrowDown", 1: "\\arpeggioArrowUp" }.get (self.direction, '')
1056 if dir:
1057 printer.dump (dir)
1058 def print_after_note (self, printer):
1059 if self.non_arpeggiate or self.direction:
1060 printer.dump ("\\arpeggioNormal")
1061 def ly_expression (self):
1062 return ('\\arpeggio')
1065 class TieEvent(Event):
1066 def ly_expression (self):
1067 return '~'
1070 class HairpinEvent (SpanEvent):
1071 def set_span_type (self, type):
1072 self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
1073 def hairpin_to_ly (self):
1074 if self.span_direction == 1:
1075 return '\!'
1076 else:
1077 return {1: '\<', -1: '\>'}.get (self.span_type, '')
1079 def ly_expression (self):
1080 return self.hairpin_to_ly ()
1082 def print_ly (self, printer):
1083 val = self.hairpin_to_ly ()
1084 if val:
1085 printer.dump (val)
1089 class DynamicsEvent (Event):
1090 def __init__ (self):
1091 Event.__init__ (self)
1092 self.type = None
1093 def wait_for_note (self):
1094 return True
1095 def ly_expression (self):
1096 if self.type:
1097 return '\%s' % self.type
1098 else:
1099 return
1101 def print_ly (self, printer):
1102 if self.type:
1103 printer.dump ("\\%s" % self.type)
1105 class MarkEvent (Event):
1106 def __init__ (self, text="\\default"):
1107 Event.__init__ (self)
1108 self.mark = text
1109 def wait_for_note (self):
1110 return False
1111 def ly_contents (self):
1112 if self.mark:
1113 return '%s' % self.mark
1114 else:
1115 return "\"ERROR\""
1116 def ly_expression (self):
1117 return '\\mark %s' % self.ly_contents ()
1119 class MusicGlyphMarkEvent (MarkEvent):
1120 def ly_contents (self):
1121 if self.mark:
1122 return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
1123 else:
1124 return ''
1127 class TextEvent (Event):
1128 def __init__ (self):
1129 Event.__init__ (self)
1130 self.Text = None
1131 self.force_direction = None
1132 self.markup = ''
1133 def wait_for_note (self):
1134 return True
1136 def direction_mod (self):
1137 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1139 def ly_expression (self):
1140 base_string = '%s\"%s\"'
1141 if self.markup:
1142 base_string = '%s\markup{ ' + self.markup + ' {%s} }'
1143 return base_string % (self.direction_mod (), self.text)
1145 class ArticulationEvent (Event):
1146 def __init__ (self):
1147 Event.__init__ (self)
1148 self.type = None
1149 self.force_direction = None
1150 def wait_for_note (self):
1151 return True
1153 def direction_mod (self):
1154 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
1156 def ly_expression (self):
1157 return '%s\\%s' % (self.direction_mod (), self.type)
1159 class ShortArticulationEvent (ArticulationEvent):
1160 def direction_mod (self):
1161 # default is -
1162 return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
1163 def ly_expression (self):
1164 if self.type:
1165 return '%s%s' % (self.direction_mod (), self.type)
1166 else:
1167 return ''
1169 class NoDirectionArticulationEvent (ArticulationEvent):
1170 def ly_expression (self):
1171 if self.type:
1172 return '\\%s' % self.type
1173 else:
1174 return ''
1176 class MarkupEvent (ShortArticulationEvent):
1177 def __init__ (self):
1178 ArticulationEvent.__init__ (self)
1179 self.contents = None
1180 def ly_expression (self):
1181 if self.contents:
1182 return "%s\\markup { %s }" % (self.direction_mod (), self.contents)
1183 else:
1184 return ''
1186 class FretEvent (MarkupEvent):
1187 def __init__ (self):
1188 MarkupEvent.__init__ (self)
1189 self.force_direction = 1
1190 self.strings = 6
1191 self.frets = 4
1192 self.barre = None
1193 self.elements = []
1194 def ly_expression (self):
1195 val = ""
1196 if self.strings <> 6:
1197 val += "w:%s;" % self.strings
1198 if self.frets <> 4:
1199 val += "h:%s;" % self.frets
1200 if self.barre and len (self.barre) >= 3:
1201 val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2])
1202 have_fingering = False
1203 for i in self.elements:
1204 if len (i) > 1:
1205 val += "%s-%s" % (i[0], i[1])
1206 if len (i) > 2:
1207 have_fingering = True
1208 val += "-%s" % i[2]
1209 val += ";"
1210 if have_fingering:
1211 val = "f:1;" + val
1212 if val:
1213 return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val)
1214 else:
1215 return ''
1218 class FunctionWrapperEvent (Event):
1219 def __init__ (self, function_name = None):
1220 Event.__init__ (self)
1221 self.function_name = function_name
1222 def pre_note_ly (self, is_chord_element):
1223 if self.function_name:
1224 return "\\%s" % self.function_name
1225 else:
1226 return ''
1227 def pre_chord_ly (self):
1228 return ''
1229 def ly_expression (self):
1230 if self.function_name:
1231 return "\\%s" % self.function_name
1232 else:
1233 return ''
1235 class ParenthesizeEvent (FunctionWrapperEvent):
1236 def __init__ (self):
1237 FunctionWrapperEvent.__init__ (self, "parenthesize")
1239 class NotestyleEvent (Event):
1240 def __init__ (self):
1241 Event.__init__ (self)
1242 self.style = None
1243 self.filled = None
1244 def pre_chord_ly (self):
1245 if self.style:
1246 return "\\once \\override NoteHead #'style = #%s" % self.style
1247 else:
1248 return ''
1249 def pre_note_ly (self, is_chord_element):
1250 if self.style and is_chord_element:
1251 return "\\tweak #'style #%s" % self.style
1252 else:
1253 return ''
1254 def ly_expression (self):
1255 return self.pre_chord_ly ()
1258 class ChordPitch:
1259 def __init__ (self):
1260 self.alteration = 0
1261 self.step = 0
1262 def __repr__(self):
1263 return self.ly_expression()
1264 def ly_expression (self):
1265 return pitch_generating_function (self)
1267 class ChordModification:
1268 def __init__ (self):
1269 self.alteration = 0
1270 self.step = 0
1271 self.type = 0
1272 def ly_expression (self):
1273 if self.type:
1274 val = {1: ".", -1: "^" }.get (self.type, "")
1275 val += "%s" % self.step
1276 val += {1: "+", -1: "-"}.get (self.alteration, "")
1277 return val
1278 else:
1279 return ''
1281 class ChordNameEvent (Event):
1282 def __init__ (self):
1283 Event.__init__ (self)
1284 self.root = None
1285 self.kind = None
1286 self.duration = None
1287 self.modifications = []
1288 self.bass = None
1289 def add_modification (self, mod):
1290 self.modifications.append (mod)
1291 def ly_expression (self):
1292 if not self.root:
1293 return ''
1294 value = self.root.ly_expression ()
1295 if self.duration:
1296 value += self.duration.ly_expression ()
1297 if self.kind:
1298 value += ":"
1299 value += self.kind
1300 # First print all additions/changes, and only afterwards all subtractions
1301 for m in self.modifications:
1302 if m.type == 1:
1303 value += m.ly_expression ()
1304 for m in self.modifications:
1305 if m.type == -1:
1306 value += m.ly_expression ()
1307 if self.bass:
1308 value += "/+%s" % self.bass.ly_expression ()
1309 return value
1312 class TremoloEvent (ArticulationEvent):
1313 def __init__ (self):
1314 Event.__init__ (self)
1315 self.bars = 0
1317 def ly_expression (self):
1318 str=''
1319 if self.bars and self.bars > 0:
1320 str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
1321 return str
1323 class BendEvent (ArticulationEvent):
1324 def __init__ (self):
1325 Event.__init__ (self)
1326 self.alter = None
1327 def ly_expression (self):
1328 if self.alter != None:
1329 return "-\\bendAfter #%s" % self.alter
1330 else:
1331 return ''
1333 class RhythmicEvent(Event):
1334 def __init__ (self):
1335 Event.__init__ (self)
1336 self.duration = Duration()
1337 self.associated_events = []
1339 def add_associated_event (self, ev):
1340 if ev:
1341 self.associated_events.append (ev)
1343 def pre_chord_ly (self):
1344 return [ev.pre_chord_ly () for ev in self.associated_events]
1346 def pre_note_ly (self, is_chord_element):
1347 return [ev.pre_note_ly (is_chord_element) for ev in self.associated_events]
1349 def ly_expression_pre_note (self, is_chord_element):
1350 res = string.join (self.pre_note_ly (is_chord_element), ' ')
1351 if res != '':
1352 res = res + ' '
1353 return res
1355 def get_length (self):
1356 return self.duration.get_length()
1358 def get_properties (self):
1359 return ("'duration %s"
1360 % self.duration.lisp_expression ())
1362 class RestEvent (RhythmicEvent):
1363 def __init__ (self):
1364 RhythmicEvent.__init__ (self)
1365 self.pitch = None
1367 def ly_expression (self):
1368 res = self.ly_expression_pre_note (False)
1369 if self.pitch:
1370 return res + "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
1371 else:
1372 return 'r%s' % self.duration.ly_expression ()
1374 def print_ly (self, printer):
1375 for ev in self.associated_events:
1376 ev.print_ly (printer)
1377 if self.pitch:
1378 self.pitch.print_ly (printer)
1379 self.duration.print_ly (printer)
1380 printer ('\\rest')
1381 else:
1382 printer('r')
1383 self.duration.print_ly (printer)
1385 class SkipEvent (RhythmicEvent):
1386 def ly_expression (self):
1387 return 's%s' % self.duration.ly_expression ()
1389 class NoteEvent(RhythmicEvent):
1390 def __init__ (self):
1391 RhythmicEvent.__init__ (self)
1392 self.pitch = None
1393 self.drum_type = None
1394 self.cautionary = False
1395 self.forced_accidental = False
1397 def get_properties (self):
1398 str = RhythmicEvent.get_properties (self)
1400 if self.pitch:
1401 str += self.pitch.lisp_expression ()
1402 elif self.drum_type:
1403 str += "'drum-type '%s" % self.drum_type
1405 return str
1407 def pitch_mods (self):
1408 excl_question = ''
1409 if self.cautionary:
1410 excl_question += '?'
1411 if self.forced_accidental:
1412 excl_question += '!'
1414 return excl_question
1416 def ly_expression (self):
1417 # obtain all stuff that needs to be printed before the note:
1418 res = self.ly_expression_pre_note (True)
1419 if self.pitch:
1420 return res + '%s%s%s' % (self.pitch.ly_expression (),
1421 self.pitch_mods(),
1422 self.duration.ly_expression ())
1423 elif self.drum_type:
1424 return res + '%s%s' (self.drum_type,
1425 self.duration.ly_expression ())
1427 def chord_element_ly (self):
1428 # obtain all stuff that needs to be printed before the note:
1429 res = self.ly_expression_pre_note (True)
1430 if self.pitch:
1431 return res + '%s%s' % (self.pitch.ly_expression (),
1432 self.pitch_mods())
1433 elif self.drum_type:
1434 return res + '%s%s' (self.drum_type)
1437 def print_ly (self, printer):
1438 for ev in self.associated_events:
1439 ev.print_ly (printer)
1440 if self.pitch:
1441 self.pitch.print_ly (printer)
1442 printer (self.pitch_mods ())
1443 else:
1444 printer (self.drum_type)
1446 self.duration.print_ly (printer)
1448 class KeySignatureChange (Music):
1449 def __init__ (self):
1450 Music.__init__ (self)
1451 self.tonic = None
1452 self.mode = 'major'
1453 self.non_standard_alterations = None
1455 def format_non_standard_alteration (self, a):
1456 alter_dict = { -2: ",DOUBLE-FLAT",
1457 -1.5: ",THREE-Q-FLAT",
1458 -1: ",FLAT",
1459 -0.5: ",SEMI-FLAT",
1460 0: ",NATURAL",
1461 0.5: ",SEMI-SHARP",
1462 1: ",SHARP",
1463 1.5: ",THREE-Q-SHARP",
1464 2: ",DOUBLE-SHARP"}
1465 try:
1466 accidental = alter_dict[a[1]]
1467 except KeyError:
1468 warning (_ ("Unable to convert alteration %s to a lilypond expression") % a[1])
1469 return ''
1470 if len (a) == 2:
1471 return "( %s . %s )" % (a[0], accidental)
1472 elif len (a) == 3:
1473 return "(( %s . %s ) . %s )" % (a[2], a[0], accidental)
1474 else:
1475 return ''
1477 def ly_expression (self):
1478 if self.tonic:
1479 return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
1480 self.mode)
1481 elif self.non_standard_alterations:
1482 alterations = [self.format_non_standard_alteration (a) for
1483 a in self.non_standard_alterations]
1484 return "\\set Staff.keySignature = #`(%s)" % string.join (alterations, " ")
1485 else:
1486 return ''
1488 class TimeSignatureChange (Music):
1489 def __init__ (self):
1490 Music.__init__ (self)
1491 self.fractions = [4,4]
1492 self.style = None
1493 def format_fraction (self, frac):
1494 if isinstance (frac, list):
1495 l = [self.format_fraction (f) for f in frac]
1496 return "(" + string.join (l, " ") + ")"
1497 else:
1498 return "%s" % frac
1500 def ly_expression (self):
1501 st = ''
1502 # Print out the style if we have ome, but the '() should only be
1503 # forced for 2/2 or 4/4, since in all other cases we'll get numeric
1504 # signatures anyway despite the default 'C signature style!
1505 is_common_signature = self.fractions in ([2,2], [4,4], [4,2])
1506 if self.style:
1507 if self.style == "common":
1508 st = "\\defaultTimeSignature"
1509 elif (self.style != "'()"):
1510 st = "\\once \\override Staff.TimeSignature #'style = #%s " % self.style
1511 elif (self.style != "'()") or is_common_signature:
1512 st = "\\numericTimeSignature"
1514 # Easy case: self.fractions = [n,d] => normal \time n/d call:
1515 if len (self.fractions) == 2 and isinstance (self.fractions[0], int):
1516 return st + '\\time %d/%d ' % tuple (self.fractions)
1517 elif self.fractions:
1518 return st + "\\compoundMeter #'%s" % self.format_fraction (self.fractions)
1519 else:
1520 return st + ''
1522 class ClefChange (Music):
1523 def __init__ (self):
1524 Music.__init__ (self)
1525 self.type = 'G'
1526 self.position = 2
1527 self.octave = 0
1529 def octave_modifier (self):
1530 return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
1531 def clef_name (self):
1532 return {('G', 2): "treble",
1533 ('G', 1): "french",
1534 ('C', 1): "soprano",
1535 ('C', 2): "mezzosoprano",
1536 ('C', 3): "alto",
1537 ('C', 4): "tenor",
1538 ('C', 5): "baritone",
1539 ('F', 3): "varbaritone",
1540 ('F', 4): "bass",
1541 ('F', 5): "subbass",
1542 ("percussion", 2): "percussion",
1543 # Workaround: MuseScore uses PERC instead of percussion
1544 ("PERC", 2): "percussion",
1545 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
1546 def ly_expression (self):
1547 return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
1549 clef_dict = {
1550 "G": ("clefs.G", -2, -6),
1551 "C": ("clefs.C", 0, 0),
1552 "F": ("clefs.F", 2, 6),
1555 def lisp_expression (self):
1556 try:
1557 (glyph, pos, c0) = self.clef_dict[self.type]
1558 except KeyError:
1559 return ""
1560 clefsetting = """
1561 (make-music 'SequentialMusic
1562 'elements (list
1563 (context-spec-music
1564 (make-property-set 'clefGlyph "%s") 'Staff)
1565 (context-spec-music
1566 (make-property-set 'clefPosition %d) 'Staff)
1567 (context-spec-music
1568 (make-property-set 'middleCPosition %d) 'Staff)))
1569 """ % (glyph, pos, c0)
1570 return clefsetting
1572 class Transposition (Music):
1573 def __init__ (self):
1574 Music.__init__ (self)
1575 self.pitch = None
1576 def ly_expression (self):
1577 self.pitch._force_absolute_pitch = True
1578 return '\\transposition %s' % self.pitch.ly_expression ()
1580 class StaffChange (Music):
1581 def __init__ (self, staff):
1582 Music.__init__ (self)
1583 self.staff = staff
1584 def ly_expression (self):
1585 if self.staff:
1586 return "\\change Staff=\"%s\"" % self.staff
1587 else:
1588 return ''
1590 class SetEvent (Music):
1591 def __init__ (self, contextprop, value):
1592 Music.__init__ (self)
1593 self.context_prop = contextprop
1594 self.value = value
1595 def ly_expression (self):
1596 if self.value:
1597 return "\\set %s = %s" % (self.context_prop, self.value)
1598 else:
1599 return ''
1601 class StaffLinesEvent (Music):
1602 def __init__ (self, lines):
1603 Music.__init__ (self)
1604 self.lines = lines
1605 def ly_expression (self):
1606 if (self.lines > 0):
1607 return "\\stopStaff \\override Staff.StaffSymbol #'line-count = #%s \\startStaff" % self.lines
1608 else:
1609 return "\\stopStaff \\revert Staff.StaffSymbol #'line-count \\startStaff"
1611 class TempoMark (Music):
1612 def __init__ (self):
1613 Music.__init__ (self)
1614 self.baseduration = None
1615 self.newduration = None
1616 self.beats = None
1617 self.parentheses = False
1618 def set_base_duration (self, dur):
1619 self.baseduration = dur
1620 def set_new_duration (self, dur):
1621 self.newduration = dur
1622 def set_beats_per_minute (self, beats):
1623 self.beats = beats
1624 def set_parentheses (self, parentheses):
1625 self.parentheses = parentheses
1626 def wait_for_note (self):
1627 return False
1628 def duration_to_markup (self, dur):
1629 if dur:
1630 # Generate the markup to print the note, use scheme mode for
1631 # ly_expression to get longa and not \longa (which causes an error)
1632 return "\\general-align #Y #DOWN \\smaller \\note #\"%s\" #UP" % dur.ly_expression(None, True)
1633 else:
1634 return ''
1635 def tempo_markup_template (self):
1636 return "\\mark\\markup { \\fontsize #-2 \\line { %s } }"
1637 def ly_expression (self):
1638 res = ''
1639 if not self.baseduration:
1640 return res
1641 if self.beats:
1642 if self.parentheses:
1643 res += "\\tempo \"\" %s=%s" % (self.baseduration.ly_expression(), self.beats)
1644 else:
1645 res += "\\tempo %s=%s" % (self.baseduration.ly_expression(), self.beats)
1646 elif self.newduration:
1647 dm = self.duration_to_markup (self.baseduration)
1648 ndm = self.duration_to_markup (self.newduration)
1649 if self.parentheses:
1650 contents = "\"(\" %s = %s \")\"" % (dm, ndm)
1651 else:
1652 contents = " %s = %s " % (dm, ndm)
1653 res += self.tempo_markup_template() % contents
1654 else:
1655 return ''
1656 return res
1658 class FiguredBassNote (Music):
1659 def __init__ (self):
1660 Music.__init__ (self)
1661 self.number = ''
1662 self.prefix = ''
1663 self.suffix = ''
1664 def set_prefix (self, prefix):
1665 self.prefix = prefix
1666 def set_suffix (self, suffix):
1667 self.prefix = suffix
1668 def set_number (self, number):
1669 self.number = number
1670 def ly_expression (self):
1671 res = ''
1672 if self.number:
1673 res += self.number
1674 else:
1675 res += '_'
1676 if self.prefix:
1677 res += self.prefix
1678 if self.suffix:
1679 res += self.suffix
1680 return res
1683 class FiguredBassEvent (NestedMusic):
1684 def __init__ (self):
1685 NestedMusic.__init__ (self)
1686 self.duration = None
1687 self.real_duration = 0
1688 self.parentheses = False
1689 return
1690 def set_duration (self, dur):
1691 self.duration = dur
1692 def set_parentheses (self, par):
1693 self.parentheses = par
1694 def set_real_duration (self, dur):
1695 self.real_duration = dur
1697 def print_ly (self, printer):
1698 figured_bass_events = [e for e in self.elements if
1699 isinstance (e, FiguredBassNote)]
1700 if figured_bass_events:
1701 notes = []
1702 for x in figured_bass_events:
1703 notes.append (x.ly_expression ())
1704 contents = string.join (notes)
1705 if self.parentheses:
1706 contents = '[%s]' % contents
1707 printer ('<%s>' % contents)
1708 self.duration.print_ly (printer)
1711 class MultiMeasureRest(Music):
1713 def lisp_expression (self):
1714 return """
1715 (make-music
1716 'MultiMeasureRestMusicGroup
1717 'elements
1718 (list (make-music (quote BarCheck))
1719 (make-music
1720 'ChordEvent
1721 'elements
1722 (list (make-music
1723 'MultiMeasureRestEvent
1724 'duration
1725 %s)))
1726 (make-music (quote BarCheck))))
1727 """ % self.duration.lisp_expression ()
1729 def ly_expression (self):
1730 return 'R%s' % self.duration.ly_expression ()
1733 class Break (Music):
1734 def __init__ (self, tp="break"):
1735 Music.__init__ (self)
1736 self.type = tp
1737 def print_ly (self, printer):
1738 if self.type:
1739 printer.dump ("\\%s" % self.type)
1741 class StaffGroup:
1742 def __init__ (self, command = "StaffGroup"):
1743 self.stafftype = command
1744 self.id = None
1745 self.instrument_name = None
1746 self.short_instrument_name = None
1747 self.symbol = None
1748 self.spanbar = None
1749 self.children = []
1750 self.is_group = True
1751 self.context_modifications = []
1752 # part_information is a list with entries of the form
1753 # [staffid, voicelist]
1754 # where voicelist is a list with entries of the form
1755 # [voiceid1, [lyricsid11, lyricsid12,...] ]
1756 self.part_information = None
1758 def append_staff (self, staff):
1759 self.children.append (staff)
1761 def set_part_information (self, part_name, staves_info):
1762 if part_name == self.id:
1763 self.part_information = staves_info
1764 else:
1765 for c in self.children:
1766 c.set_part_information (part_name, staves_info)
1768 def add_context_modification (self, modification):
1769 self.context_modifications.append (modification)
1771 def print_ly_contents (self, printer):
1772 for c in self.children:
1773 if c:
1774 c.print_ly (printer)
1775 def needs_with (self):
1776 needs_with = False
1777 needs_with |= self.spanbar == "no"
1778 needs_with |= self.instrument_name != None
1779 needs_with |= self.short_instrument_name != None
1780 needs_with |= (self.symbol != None) and (self.symbol != "bracket")
1781 return needs_with
1782 def print_ly_context_mods (self, printer):
1783 if self.instrument_name or self.short_instrument_name:
1784 printer.dump ("\\consists \"Instrument_name_engraver\"")
1785 if self.spanbar == "no":
1786 printer.dump ("\\override SpanBar #'transparent = ##t")
1787 brack = {"brace": "SystemStartBrace",
1788 "none": "f",
1789 "line": "SystemStartSquare"}.get (self.symbol, None)
1790 if brack:
1791 printer.dump ("systemStartDelimiter = #'%s" % brack)
1793 def print_ly_overrides (self, printer):
1794 needs_with = self.needs_with () | (len (self.context_modifications) > 0);
1795 if needs_with:
1796 printer.dump ("\\with {")
1797 self.print_ly_context_mods (printer)
1798 for m in self.context_modifications:
1799 printer.dump (m)
1800 printer.dump ("}")
1802 def print_ly (self, printer):
1803 if self.stafftype:
1804 printer.dump ("\\new %s" % self.stafftype)
1805 self.print_ly_overrides (printer)
1806 printer.dump ("<<")
1807 printer.newline ()
1808 if self.stafftype and self.instrument_name:
1809 printer.dump ("\\set %s.instrumentName = %s" % (self.stafftype,
1810 escape_instrument_string (self.instrument_name)))
1811 printer.newline ()
1812 if self.stafftype and self.short_instrument_name:
1813 printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
1814 escape_instrument_string (self.short_instrument_name)))
1815 printer.newline ()
1816 self.print_ly_contents (printer)
1817 printer.newline ()
1818 printer.dump (">>")
1819 printer.newline ()
1822 class Staff (StaffGroup):
1823 def __init__ (self, command = "Staff"):
1824 StaffGroup.__init__ (self, command)
1825 self.is_group = False
1826 self.part = None
1827 self.voice_command = "Voice"
1828 self.substafftype = None
1830 def needs_with (self):
1831 return False
1832 def print_ly_context_mods (self, printer):
1833 pass
1835 def print_ly_contents (self, printer):
1836 if not self.id or not self.part_information:
1837 return
1838 sub_staff_type = self.substafftype
1839 if not sub_staff_type:
1840 sub_staff_type = self.stafftype
1842 for [staff_id, voices] in self.part_information:
1843 # Chord names need to come before the staff itself!
1844 for [v, lyrics, figuredbass, chordnames] in voices:
1845 if chordnames:
1846 printer ('\context ChordNames = "%s" \\%s' % (chordnames, chordnames))
1848 # now comes the real staff definition:
1849 if staff_id:
1850 printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
1851 else:
1852 printer ('\\context %s << ' % sub_staff_type)
1853 printer.newline ()
1854 n = 0
1855 nr_voices = len (voices)
1856 for [v, lyrics, figuredbass, chordnames] in voices:
1857 n += 1
1858 voice_count_text = ''
1859 if nr_voices > 1:
1860 voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
1861 3: ' \\voiceThree'}.get (n, ' \\voiceFour')
1862 printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
1863 printer.newline ()
1865 for l in lyrics:
1866 printer ('\\new Lyrics \\lyricsto "%s" \\%s' % (v,l))
1867 printer.newline()
1868 if figuredbass:
1869 printer ('\context FiguredBass = "%s" \\%s' % (figuredbass, figuredbass))
1870 printer ('>>')
1872 def print_ly (self, printer):
1873 if self.part_information and len (self.part_information) > 1:
1874 self.stafftype = "PianoStaff"
1875 self.substafftype = "Staff"
1876 StaffGroup.print_ly (self, printer)
1878 class TabStaff (Staff):
1879 def __init__ (self, command = "TabStaff"):
1880 Staff.__init__ (self, command)
1881 self.string_tunings = []
1882 self.tablature_format = None
1883 self.voice_command = "TabVoice"
1884 def print_ly_overrides (self, printer):
1885 if self.string_tunings or self.tablature_format:
1886 printer.dump ("\\with {")
1887 if self.string_tunings:
1888 printer.dump ("stringTunings = #'(")
1889 for i in self.string_tunings:
1890 printer.dump ("%s" % i.semitones ())
1891 printer.dump (")")
1892 if self.tablature_format:
1893 printer.dump ("tablatureFormat = #%s" % self.tablature_format)
1894 printer.dump ("}")
1897 class DrumStaff (Staff):
1898 def __init__ (self, command = "DrumStaff"):
1899 Staff.__init__ (self, command)
1900 self.drum_style_table = None
1901 self.voice_command = "DrumVoice"
1902 def print_ly_overrides (self, printer):
1903 if self.drum_style_table:
1904 printer.dump ("\with {")
1905 printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
1906 printer.dump ("}")
1908 class RhythmicStaff (Staff):
1909 def __init__ (self, command = "RhythmicStaff"):
1910 Staff.__init__ (self, command)
1912 class Score:
1913 def __init__ (self):
1914 self.contents = None
1915 self.create_midi = False
1917 def set_contents (self, contents):
1918 self.contents = contents
1920 def set_part_information (self, part_id, staves_info):
1921 if self.contents:
1922 self.contents.set_part_information (part_id, staves_info)
1924 def print_ly (self, printer):
1925 printer.dump ("\\score {");
1926 printer.newline ()
1927 if self.contents:
1928 self.contents.print_ly (printer);
1929 printer.dump ("\\layout {}");
1930 printer.newline ()
1931 if not self.create_midi:
1932 printer.dump ("% To create MIDI output, uncomment the following line:");
1933 printer.newline ();
1934 printer.dump ("% ");
1935 printer.dump ("\\midi {}");
1936 printer.newline ()
1937 printer.dump ("}");
1938 printer.newline ()
1941 def test_pitch ():
1942 bflat = Pitch()
1943 bflat.alteration = -1
1944 bflat.step = 6
1945 bflat.octave = -1
1946 fifth = Pitch()
1947 fifth.step = 4
1948 down = Pitch ()
1949 down.step = -4
1950 down.normalize ()
1953 print bflat.semitones()
1954 print bflat.transposed (fifth), bflat.transposed (fifth).transposed (fifth)
1955 print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
1957 print bflat.semitones(), 'down'
1958 print bflat.transposed (down)
1959 print bflat.transposed (down).transposed (down)
1960 print bflat.transposed (down).transposed (down).transposed (down)
1964 def test_printer ():
1965 def make_note ():
1966 evc = ChordEvent()
1967 n = NoteEvent()
1968 evc.append (n)
1969 return n
1971 def make_tup ():
1972 m = SequentialMusic()
1973 m.append (make_note ())
1974 m.append (make_note ())
1975 m.append (make_note ())
1978 t = TimeScaledMusic ()
1979 t.numerator = 2
1980 t.denominator = 3
1981 t.element = m
1982 return t
1984 m = SequentialMusic ()
1985 m.append (make_tup ())
1986 m.append (make_tup ())
1987 m.append (make_tup ())
1989 printer = Output_printer()
1990 m.print_ly (printer)
1991 printer.newline ()
1993 def test_expr ():
1994 m = SequentialMusic()
1995 l = 2
1996 evc = ChordEvent()
1997 n = NoteEvent()
1998 n.duration.duration_log = l
1999 n.pitch.step = 1
2000 evc.insert_around (None, n, 0)
2001 m.insert_around (None, evc, 0)
2003 evc = ChordEvent()
2004 n = NoteEvent()
2005 n.duration.duration_log = l
2006 n.pitch.step = 3
2007 evc.insert_around (None, n, 0)
2008 m.insert_around (None, evc, 0)
2010 evc = ChordEvent()
2011 n = NoteEvent()
2012 n.duration.duration_log = l
2013 n.pitch.step = 2
2014 evc.insert_around (None, n, 0)
2015 m.insert_around (None, evc, 0)
2017 evc = ClefChange()
2018 evc.type = 'treble'
2019 m.insert_around (None, evc, 0)
2021 evc = ChordEvent()
2022 tonic = Pitch ()
2023 tonic.step = 2
2024 tonic.alteration = -2
2025 n = KeySignatureChange()
2026 n.tonic=tonic.copy()
2027 n.scale = [0, 0, -2, 0, 0,-2,-2]
2029 evc.insert_around (None, n, 0)
2030 m.insert_around (None, evc, 0)
2032 return m
2035 if __name__ == '__main__':
2036 test_printer ()
2037 raise 'bla'
2038 test_pitch()
2040 expr = test_expr()
2041 expr.set_start (Rational (0))
2042 print expr.ly_expression()
2043 start = Rational (0,4)
2044 stop = Rational (4,2)
2045 def sub(x, start=start, stop=stop):
2046 ok = x.start >= start and x.start +x.get_length() <= stop
2047 return ok
2049 print expr.lisp_sub_expression(sub)