X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=python%2Fmusicexp.py;h=8fb4e5c31976c972e3544b182b15ccb14104b2a6;hb=34f50771ec53f5afbc49923833c32013f25fa1c2;hp=ef896f9e53752ec80ab325e8966225865dc6ae23;hpb=a6cf8162d8a5b61950137096a9f1193cfde21e87;p=lilypond.git diff --git a/python/musicexp.py b/python/musicexp.py index ef896f9e53..8fb4e5c319 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -5,11 +5,15 @@ import re 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 + "\"} " @@ -126,8 +130,8 @@ class Output_printer: self.newline () self._file.close () self._file = None - - + + class Duration: def __init__ (self): self.duration_log = 0 @@ -182,7 +186,71 @@ class Duration: 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 @@ -229,21 +297,39 @@ class Pitch: 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): @@ -331,6 +417,24 @@ class ModeChangingMusicWrapper (MusicWrapper): 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 ' % @@ -516,6 +620,46 @@ class Header: 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): @@ -559,8 +703,15 @@ 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 @@ -684,9 +835,13 @@ class GlissandoEvent (SpanEvent): 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') @@ -775,11 +930,17 @@ class ShortArticulationEvent (ArticulationEvent): # 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): @@ -997,6 +1158,17 @@ class ClefChange (Music): return clefsetting +class StaffChange (Music): + def __init__ (self, staff): + Music.__init__ (self) + self.staff = staff + def ly_expression (self): + if self.staff: + return "\\change Staff=\"%s\"" % self.staff + else: + return '' + + class MultiMeasureRest(Music): def lisp_expression (self): @@ -1028,21 +1200,22 @@ class StaffGroup: 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: @@ -1062,7 +1235,7 @@ class StaffGroup: 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 ("}") @@ -1078,7 +1251,7 @@ class StaffGroup: 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) @@ -1088,9 +1261,12 @@ class StaffGroup: 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 @@ -1098,12 +1274,15 @@ class Staff (StaffGroup): 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) @@ -1113,7 +1292,7 @@ class Staff (StaffGroup): 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: @@ -1124,8 +1303,43 @@ class Staff (StaffGroup): 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()