from rational import Rational
+# Store previously converted pitch for \relative conversion as a global state variable
+previous_pitch = None
+relative_pitches = False
def escape_instrument_string (input_string):
retstring = string.replace (input_string, "\"", "\\\"")
- if re.match ('.*\n.*', retstring):
- strings = retstring.split ('\r\n')
+ if re.match ('.*[\r\n]+.*', retstring):
+ rx = re.compile (r'[\n\r]+')
+ strings = rx.split (retstring)
retstring = "\\markup { \\column { "
for s in strings:
retstring += "\\line {\"" + s + "\"} "
self.newline ()
self._file.close ()
self._file = None
-
-
+
+
class Duration:
def __init__ (self):
self.duration_log = 0
return base * dot_fact * self.factor
-
+
+# Implement the different note names for the various languages
+def pitch_generic (pitch, notenames, accidentals):
+ str = notenames[pitch.step]
+ if pitch.alteration < 0:
+ str += accidentals[0] * (-pitch.alteration)
+ elif pitch.alteration > 0:
+ str += accidentals[3] * (pitch.alteration)
+ return str
+
+def pitch_general (pitch):
+ str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['es', 'eh', 'ih', 'is'])
+ return str.replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_nederlands (pitch):
+ return pitch_general (pitch)
+
+def pitch_english (pitch):
+ str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'b'], ['f', 'qf', 'qs', 's'])
+ return str.replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_deutsch (pitch):
+ str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['es', 'eh', 'ih', 'is'])
+ return str.replace ('hes', 'b').replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_norsk (pitch):
+ return pitch_deutsch (pitch)
+
+def pitch_svenska (pitch):
+ str = pitch_generic (pitch, ['c', 'd', 'e', 'f', 'g', 'a', 'h'], ['ess', '', '', 'iss'])
+ return str.replace ('hess', 'b').replace ('aes', 'as').replace ('ees', 'es')
+
+def pitch_italiano (pitch):
+ str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', 'sb', 'sd', 'd'])
+ return str
+
+def pitch_catalan (pitch):
+ return pitch_italiano (pitch)
+
+def pitch_espanol (pitch):
+ str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 's'])
+ return str
+
+def pitch_vlaams (pitch):
+ str = pitch_generic (pitch, ['do', 're', 'mi', 'fa', 'sol', 'la', 'si'], ['b', '', '', 'k'])
+ return str
+
+def set_pitch_language (language):
+ global pitch_generating_function
+ function_dict = {
+ "nederlands": pitch_nederlands,
+ "english": pitch_english,
+ "deutsch": pitch_deutsch,
+ "norsk": pitch_norsk,
+ "svenska": pitch_svenska,
+ "italiano": pitch_italiano,
+ "catalan": pitch_catalan,
+ "espanol": pitch_espanol,
+ "vlaams": pitch_vlaams}
+ pitch_generating_function = function_dict.get (language, pitch_general)
+
+# global variable to hold the formatting function.
+pitch_generating_function = pitch_general
+
+
class Pitch:
def __init__ (self):
self.alteration = 0
return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
def ly_step_expression (self):
- str = 'cdefgab'[self.step]
- if self.alteration > 0:
- str += 'is'* (self.alteration)
- elif self.alteration < 0:
- str += 'es'* (-self.alteration)
+ return pitch_generating_function (self)
- return str.replace ('aes', 'as').replace ('ees', 'es')
-
- 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 ' %
printer.newline ()
+class Paper:
+ def __init__ (self):
+ self.global_staff_size = -1
+ # page size
+ self.page_width = -1
+ self.page_height = -1
+ # page margins
+ self.top_margin = -1
+ self.bottom_margin = -1
+ self.left_margin = -1
+ self.right_margin = -1
+ self.system_left_margin = -1
+ self.system_right_margin = -1
+ self.system_distance = -1
+ self.top_system_distance = -1
+
+ def print_length_field (self, printer, field, value):
+ if value >= 0:
+ printer.dump ("%s = %s\\cm" % (field, value))
+ printer.newline ()
+ def print_ly (self, printer):
+ if self.global_staff_size > 0:
+ printer.dump ('#(set-global-staff-size %s)' % self.global_staff_size)
+ printer.newline ()
+ printer.dump ('\\paper {')
+ printer.newline ()
+ self.print_length_field (printer, "paper-width", self.page_width)
+ self.print_length_field (printer, "paper-height", self.page_height)
+ self.print_length_field (printer, "top-margin", self.top_margin)
+ self.print_length_field (printer, "botton-margin", self.bottom_margin)
+ self.print_length_field (printer, "left-margin", self.left_margin)
+ # TODO: maybe set line-width instead of right-margin?
+ self.print_length_field (printer, "right-margin", self.right_margin)
+ # TODO: What's the corresponding setting for system_left_margin and
+ # system_right_margin in Lilypond?
+ self.print_length_field (printer, "between-system-space", self.system_distance)
+ self.print_length_field (printer, "page-top-space", self.top_system_distance)
+
+ printer.dump ('}')
+ printer.newline ()
class EventChord (NestedMusic):
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
1:''}.get (self.span_direction, '')
class ArpeggioEvent(Event):
+ def __init__ (self):
+ Event.__init__ (self)
+ self.direction = 0
def wait_for_note (self):
return True;
def ly_expression (self):
+ # TODO: Use self.direction for up/down arpeggios
return ('\\arpeggio')
# default is -
return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
def ly_expression (self):
- return '%s%s' % (self.direction_mod (), self.type)
+ if self.type:
+ return '%s%s' % (self.direction_mod (), self.type)
+ else:
+ return ''
class NoDirectionArticulationEvent (ArticulationEvent):
def ly_expression (self):
- return '\\%s' % self.type
+ if self.type:
+ return '\\%s' % self.type
+ else:
+ return ''
class MarkupEvent (ShortArticulationEvent):
def __init__ (self):
self.symbol = None
self.spanbar = None
self.children = []
+ self.is_group = True
# part_information is a list with entries of the form
# [staffid, voicelist]
# where voicelist is a list with entries of the form
# [voiceid1, [lyricsid11, lyricsid12,...] ]
self.part_information = None
- def appendStaff (self, staff):
+ def append_staff (self, staff):
self.children.append (staff)
- def setPartInformation (self, part_name, staves_info):
+ def set_part_information (self, part_name, staves_info):
if part_name == self.id:
self.part_information = staves_info
else:
for c in self.children:
- c.setPartInformation (part_name, staves_info)
+ c.set_part_information (part_name, staves_info)
def print_ly_contents (self, printer):
for c in self.children:
printer.dump ("\\override SpanBar #'transparent = ##t")
brack = {"brace": "SystemStartBrace",
"none": "f",
- "line": "SystemStartBar"}.get (self.symbol, None)
+ "line": "SystemStartSquare"}.get (self.symbol, None)
if brack:
printer.dump ("systemStartDelimiter = #'%s" % brack)
printer.dump ("}")
escape_instrument_string (self.instrument_name)))
printer.newline ()
if self.stafftype and self.short_instrument_name:
- printer.dump ("\\set %s.shortInstrumentName = %s\n" % (self.stafftype,
+ printer.dump ("\\set %s.shortInstrumentName = %s" % (self.stafftype,
escape_instrument_string (self.short_instrument_name)))
printer.newline ()
self.print_ly_contents (printer)
class Staff (StaffGroup):
- def __init__ (self):
- StaffGroup.__init__ (self, "Staff")
+ def __init__ (self, command = "Staff"):
+ StaffGroup.__init__ (self, command)
+ self.is_group = False
self.part = None
+ self.voice_command = "Voice"
+ self.substafftype = None
def print_ly_overrides (self, printer):
pass
def print_ly_contents (self, printer):
if not self.id or not self.part_information:
return
+ sub_staff_type = self.substafftype
+ if not sub_staff_type:
+ sub_staff_type = self.stafftype
for [staff_id, voices] in self.part_information:
if staff_id:
- printer ('\\context Staff = "%s" << ' % staff_id)
+ printer ('\\context %s = "%s" << ' % (sub_staff_type, staff_id))
else:
- printer ('\\context Staff << ')
+ printer ('\\context %s << ' % sub_staff_type)
printer.newline ()
n = 0
nr_voices = len (voices)
if nr_voices > 1:
voice_count_text = {1: ' \\voiceOne', 2: ' \\voiceTwo',
3: ' \\voiceThree'}.get (n, ' \\voiceFour')
- printer ('\\context Voice = "%s" {%s \\%s }' % (v,voice_count_text,v))
+ printer ('\\context %s = "%s" {%s \\%s }' % (self.voice_command, v, voice_count_text, v))
printer.newline ()
for l in lyrics:
def print_ly (self, printer):
if self.part_information and len (self.part_information) > 1:
self.stafftype = "PianoStaff"
+ self.substafftype = "Staff"
StaffGroup.print_ly (self, printer)
+class TabStaff (Staff):
+ def __init__ (self, command = "TabStaff"):
+ Staff.__init__ (self, command)
+ self.string_tunings = []
+ self.tablature_format = None
+ self.voice_command = "TabVoice"
+ def print_ly_overrides (self, printer):
+ if self.string_tunings or self.tablature_format:
+ printer.dump ("\\with {")
+ if self.string_tunings:
+ printer.dump ("stringTunings = #'(")
+ for i in self.string_tunings:
+ printer.dump ("%s" % i.semitones ())
+ printer.dump (")")
+ if self.tablature_format:
+ printer.dump ("tablatureFormat = #%s" % self.tablature_format)
+ printer.dump ("}")
+
+
+class DrumStaff (Staff):
+ def __init__ (self, command = "DrumStaff"):
+ Staff.__init__ (self, command)
+ self.drum_style_table = None
+ self.voice_command = "DrumVoice"
+ def print_ly_overrides (self, printer):
+ if self.drum_style_table:
+ printer.dump ("\with {")
+ printer.dump ("drumStyleTable = #%s" % self.drum_style_table)
+ printer.dump ("}")
+
+class RhythmicStaff (Staff):
+ def __init__ (self, command = "RhythmicStaff"):
+ Staff.__init__ (self, command)
+
def test_pitch ():
bflat = Pitch()