import sys
import string
import re
+import lilylib as ly
+
+_ = ly._
from rational import Rational
+# Store previously converted pitch for \relative conversion as a global state variable
+previous_pitch = None
+relative_pitches = False
+
+def warning (str):
+ ly.stderr_write ((_ ("warning: %s") % str) + "\n")
+
def escape_instrument_string (input_string):
retstring = string.replace (input_string, "\"", "\\\"")
def ly_expression (self, factor = None):
if not factor:
factor = self.factor
-
- str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
+
+ if self.duration_log < 0:
+ str = {-1: "\\breve", -2: "\\longa"}.get (self.duration_log, "1")
+ else:
+ str = '%d' % (1 << self.duration_log)
+ str += '.'*self.dots
if factor <> Rational (1,1):
if factor.denominator () <> 1:
def ly_step_expression (self):
return pitch_generating_function (self)
-
- def ly_expression (self):
- str = self.ly_step_expression ()
+
+ def absolute_pitch (self):
if self.octave >= 0:
- str += "'" * (self.octave + 1)
+ return "'" * (self.octave + 1)
elif self.octave < -1:
- str += "," * (-self.octave - 1)
-
+ return "," * (-self.octave - 1)
+ else:
+ return ''
+
+ def relative_pitch (self):
+ global previous_pitch
+ if not previous_pitch:
+ previous_pitch = self
+ return self.absolute_pitch ()
+ previous_pitch_steps = previous_pitch.octave * 7 + previous_pitch.step
+ this_pitch_steps = self.octave * 7 + self.step
+ pitch_diff = (this_pitch_steps - previous_pitch_steps)
+ previous_pitch = self
+ if pitch_diff > 3:
+ return "'" * ((pitch_diff + 3) / 7)
+ elif pitch_diff < -3:
+ return "," * ((-pitch_diff + 3) / 7)
+ else:
+ return ""
+
+ def ly_expression (self):
+ str = self.ly_step_expression ()
+ if relative_pitches:
+ str += self.relative_pitch ()
+ else:
+ str += self.absolute_pitch ()
+
return str
def print_ly (self, outputter):
func ('\\%s' % self.mode)
MusicWrapper.print_ly (self, func)
+class RelativeMusic (MusicWrapper):
+ def __init__ (self):
+ MusicWrapper.__init__ (self)
+ self.basepitch = None
+
+ def print_ly (self, func):
+ global previous_pitch
+ global relative_pitches
+ prev_relative_pitches = relative_pitches
+ relative_pitches = True
+ previous_pitch = self.basepitch
+ if not previous_pitch:
+ previous_pitch = Pitch ()
+ func ('\\relative %s%s' % (pitch_generating_function (previous_pitch),
+ previous_pitch.absolute_pitch ()))
+ MusicWrapper.print_ly (self, func)
+ relative_pitches = prev_relative_pitches
+
class TimeScaledMusic (MusicWrapper):
def print_ly (self, func):
func ('\\times %d/%d ' %
value = None
at = len( self.elements ) - 1
while (at >= 0 and
- not isinstance (self.elements[at], EventChord) and
+ not isinstance (self.elements[at], ChordEvent) and
not isinstance (self.elements[at], BarLine)):
at -= 1
- if (at >= 0 and isinstance (self.elements[at], EventChord)):
+ if (at >= 0 and isinstance (self.elements[at], ChordEvent)):
value = self.elements[at]
return value
self.music = SequentialMusic ()
self.music.elements = music
else:
- sys.stderr.write ("WARNING: Unable to set the music %s for the repeat %s" % (music, self))
+ warning (_ ("unable to set the music %(music)s for the repeat %(repeat)s" % \
+ {'music':music, 'repeat':self}))
def add_ending (self, music):
self.endings.append (music)
def print_ly (self, printer):
if self.music:
self.music.print_ly (printer)
else:
- sys.stderr.write ("WARNING: Encountered repeat without body\n")
+ warning (_ ("encountered repeat without body"))
printer.dump ('{}')
if self.endings:
printer.dump ('\\alternative {')
printer.dump ('}')
printer.newline ()
+class Layout:
+ def __init__ (self):
+ self.context_dict = {}
+ def add_context (self, context):
+ if not self.context_dict.has_key (context):
+ self.context_dict[context] = []
+ def set_context_item (self, context, item):
+ self.add_context (context)
+ if not item in self.context_dict[context]:
+ self.context_dict[context].append (item)
+ def print_ly (self, printer):
+ if self.context_dict.items ():
+ printer.dump ('\\layout {')
+ printer.newline ()
+ for (context, defs) in self.context_dict.items ():
+ printer.dump ('\\context { \\%s' % context)
+ printer.newline ()
+ for d in defs:
+ printer.dump (d)
+ printer.newline ()
+ printer.dump ('}')
+ printer.newline ()
+ printer.dump ('}')
+ printer.newline ()
+
-class EventChord (NestedMusic):
+class ChordEvent (NestedMusic):
def __init__ (self):
NestedMusic.__init__ (self)
self.grace_elements = None
printer ('\\grace')
# don't print newlines after the { and } braces
self.grace_elements.print_ly (printer, False)
+ # Print all overrides and other settings needed by the
+ # articulations/ornaments before the note
+ for e in other_events:
+ e.print_before_note (printer)
if rest_events:
rest_events[0].print_ly (printer)
elif len (note_events) == 1:
note_events[0].print_ly (printer)
elif note_events:
- pitches = [x.pitch.ly_expression () for x in note_events]
+ global previous_pitch
+ pitches = []
+ basepitch = None
+ for x in note_events:
+ pitches.append (x.pitch.ly_expression ())
+ if not basepitch:
+ basepitch = previous_pitch
printer ('<%s>' % string.join (pitches))
+ previous_pitch = basepitch
note_events[0].duration.print_ly (printer)
else:
pass
for e in other_events:
e.print_ly (printer)
+ for e in other_events:
+ e.print_after_note (printer)
+
self.print_comment (printer)
class Partial (Music):
return " | "
class Event(Music):
+ def __init__ (self):
+ # strings to print before the note to which an event is attached.
+ # Ignored for notes etc.
+ self.before_note = None
+ self.after_note = None
+ # print something before the note to which an event is attached, e.g. overrides
+ def print_before_note (self, printer):
+ if self.before_note:
+ printer.dump (self.before_note)
+ # print something after the note to which an event is attached, e.g. resetting
+ def print_after_note (self, printer):
+ if self.after_note:
+ printer.dump (self.after_note)
pass
class SpanEvent (Event):
self.span_type = type
class SlurEvent (SpanEvent):
+ def print_before_note (self, printer):
+ command = {'dotted': '\\slurDotted',
+ 'dashed' : '\\slurDashed'}.get (self.line_type, '')
+ if command and self.span_direction == -1:
+ printer.dump (command)
+ def print_after_note (self, printer):
+ # reset non-solid slur types!
+ command = {'dotted': '\\slurSolid',
+ 'dashed' : '\\slurSolid'}.get (self.line_type, '')
+ if command and self.span_direction == -1:
+ printer.dump (command)
def ly_expression (self):
- before = ''
- after = ''
- # TODO: setting dashed/dotted line style does not work, because that
- # command needs to be written before the note, not when the
- # event is observed after the note!
- #before = {'dotted': '\\slurDotted',
- # 'dashed' : '\\slurDashed'}.get (self.line_type, '')
- #if before:
- #after = '\\slurSolid'
-
- return {-1: before + '(' + after,
- 1:')'}.get (self.span_direction, '')
+ return {-1: '(', 1:')'}.get (self.span_direction, '')
class BeamEvent (SpanEvent):
def ly_expression (self):
- return {-1: '[',
- 1:']'}.get (self.span_direction, '')
+ return {-1: '[', 1:']'}.get (self.span_direction, '')
class PedalEvent (SpanEvent):
def ly_expression (self):
return {-1: '\\sustainDown',
+ 0:'\\sustainUp\\sustainDown',
1:'\\sustainUp'}.get (self.span_direction, '')
-# type==-1 means octave up, type==-2 means octave down
+class TextSpannerEvent (SpanEvent):
+ def ly_expression (self):
+ return {-1: '\\startTextSpan',
+ 1:'\\stopTextSpan'}.get (self.span_direction, '')
+
+class BracketSpannerEvent (SpanEvent):
+ # Ligature brackets use prefix-notation!!!
+ def print_before_note (self, printer):
+ if self.span_direction == -1:
+ printer.dump ('\[')
+ # the the bracket after the last note
+ def print_after_note (self, printer):
+ if self.span_direction == 1:
+ printer.dump ('\]')
+ # we're printing everything in print_(before|after)_note...
+ def ly_expression (self):
+ return '';
+
+
class OctaveShiftEvent (SpanEvent):
def wait_for_note (self):
- return False;
+ return False
def set_span_type (self, type):
self.span_type = {'up': 1, 'down': -1}.get (type, 0)
def ly_octave_shift_indicator (self):
1:'\\stopTrillSpan'}.get (self.span_direction, '')
class GlissandoEvent (SpanEvent):
+ def print_before_note (self, printer):
+ if self.span_direction == -1:
+ style= {
+ "dashed" : "dashed-line",
+ "dotted" : "dotted-line",
+ "wavy" : "zigzag"
+ }. get (self.line_type, None)
+ if style:
+ printer.dump ("\once \override Glissando #'style = #'%s" % style)
def ly_expression (self):
- style = ''
- # TODO: wavy-line glissandos don't work, becasue the style has to be
- # set before the note, at the \glissando it's already too late!
- #if self.line_type == 'wavy':
- #style = "\once\override Glissando #'style = #'zigzag"
- # In lilypond, glissando is NOT a spanner, unlike MusicXML.
- return {-1: style + '\\glissando',
+ return {-1: '\\glissando',
1:''}.get (self.span_direction, '')
class ArpeggioEvent(Event):
def __init__ (self):
Event.__init__ (self)
self.direction = 0
+ self.non_arpeggiate = False
def wait_for_note (self):
- return True;
+ return True
+ def print_before_note (self, printer):
+ if self.non_arpeggiate:
+ printer.dump ("\\arpeggioBracket")
+ else:
+ dir = { -1: "\\arpeggioDown", 1: "\\arpeggioUp" }.get (self.direction, '')
+ if dir:
+ printer.dump (dir)
+ def print_after_note (self, printer):
+ if self.non_arpeggiate or self.direction:
+ printer.dump ("\\arpeggioNeutral")
def ly_expression (self):
- # TODO: Use self.direction for up/down arpeggios
return ('\\arpeggio')
class DynamicsEvent (Event):
def __init__ (self):
+ Event.__init__ (self)
self.type = None
- self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p",
- "mp", "mf",
- "f", "ff", "fff", "ffff",
- "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
def wait_for_note (self):
- return True;
+ return True
def ly_expression (self):
- if self.type == None:
- return;
- elif self.type in self.available_commands:
+ if self.type:
return '\%s' % self.type
else:
- return '-\markup{ \dynamic %s }' % self.type
-
- def print_ly (self, printer):
- if self.type == None:
return
- elif self.type in self.available_commands:
+
+ def print_ly (self, printer):
+ if self.type:
printer.dump ("\\%s" % self.type)
+
+class MarkEvent (Event):
+ def __init__ (self, text="\\default"):
+ Event.__init__ (self)
+ self.mark = text
+ def wait_for_note (self):
+ return False
+ def ly_contents (self):
+ if self.mark:
+ return '%s' % self.mark
+ else:
+ return "\"ERROR\""
+ def ly_expression (self):
+ return '\\mark %s' % self.ly_contents ()
+
+class MusicGlyphMarkEvent (MarkEvent):
+ def ly_contents (self):
+ if self.mark:
+ return '\\markup { \\musicglyph #"scripts.%s" }' % self.mark
else:
- printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
+ return ''
class TextEvent (Event):
def __init__ (self):
+ Event.__init__ (self)
self.Text = None
self.force_direction = None
self.markup = ''
class ArticulationEvent (Event):
def __init__ (self):
+ Event.__init__ (self)
self.type = None
self.force_direction = None
def wait_for_note (self):
- return True;
+ return True
def direction_mod (self):
return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
'elements
(list (make-music (quote BarCheck))
(make-music
- 'EventChord
+ 'ChordEvent
'elements
(list (make-music
'MultiMeasureRestEvent
def test_printer ():
def make_note ():
- evc = EventChord()
+ evc = ChordEvent()
n = NoteEvent()
evc.append (n)
return n
def test_expr ():
m = SequentialMusic()
l = 2
- evc = EventChord()
+ evc = ChordEvent()
n = NoteEvent()
n.duration.duration_log = l
n.pitch.step = 1
evc.insert_around (None, n, 0)
m.insert_around (None, evc, 0)
- evc = EventChord()
+ evc = ChordEvent()
n = NoteEvent()
n.duration.duration_log = l
n.pitch.step = 3
evc.insert_around (None, n, 0)
m.insert_around (None, evc, 0)
- evc = EventChord()
+ evc = ChordEvent()
n = NoteEvent()
n.duration.duration_log = l
n.pitch.step = 2
evc.type = 'treble'
m.insert_around (None, evc, 0)
- evc = EventChord()
+ evc = ChordEvent()
tonic = Pitch ()
tonic.step = 2
tonic.alteration = -2