X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=python%2Fmusicexp.py;h=862b5d8c4f70f5148c0a5b1899588b1859014c59;hb=024e51d1cb0e21d211baf91ad0774bc766dff687;hp=c4ef903a32e0a863616203ee89614da4f23854c8;hpb=074704b5874eaaaf80cbf7618d4b40c712089a61;p=lilypond.git diff --git a/python/musicexp.py b/python/musicexp.py index c4ef903a32..862b5d8c4f 100644 --- a/python/musicexp.py +++ b/python/musicexp.py @@ -2,14 +2,25 @@ import inspect 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, "\"", "\\\"") - 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 +137,8 @@ class Output_printer: self.newline () self._file.close () self._file = None - - + + class Duration: def __init__ (self): self.duration_log = 0 @@ -144,8 +155,12 @@ class Duration: 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: @@ -182,7 +197,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 +308,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 +428,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 ' % @@ -415,11 +530,11 @@ class SequentialMusic (NestedMusic): 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 @@ -463,7 +578,8 @@ class RepeatedMusic: 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): @@ -471,7 +587,7 @@ class RepeatedMusic: 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 {') @@ -516,9 +632,74 @@ 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 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 @@ -553,14 +734,25 @@ class EventChord (NestedMusic): 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 @@ -568,6 +760,9 @@ class EventChord (NestedMusic): 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): @@ -604,6 +799,19 @@ class BarLine (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): @@ -621,34 +829,44 @@ 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): + def ly_expression (self): + return {-1: '\\startGroup', + 1:'\\stopGroup'}.get (self.span_direction, '') + + 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): @@ -673,19 +891,36 @@ class TrillSpanEvent (SpanEvent): 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): return ('\\arpeggio') @@ -716,32 +951,45 @@ class HairpinEvent (SpanEvent): 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: - printer.dump ("-\\markup{ \\dynamic %s }" % self.type) + 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: + return '' class TextEvent (Event): def __init__ (self): + Event.__init__ (self) self.Text = None self.force_direction = None self.markup = '' @@ -759,8 +1007,11 @@ class TextEvent (Event): class ArticulationEvent (Event): def __init__ (self): + Event.__init__ (self) self.type = None self.force_direction = None + def wait_for_note (self): + return True def direction_mod (self): return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '') @@ -773,11 +1024,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): @@ -786,6 +1043,39 @@ class MarkupEvent (ShortArticulationEvent): def ly_expression (self): if self.contents: return "%s\\markup { %s }" % (self.direction_mod (), self.contents) + else: + return '' + +class FretEvent (MarkupEvent): + def __init__ (self): + MarkupEvent.__init__ (self) + self.force_direction = 1 + self.strings = 6 + self.frets = 4 + self.barre = None + self.elements = [] + def ly_expression (self): + val = "" + if self.strings <> 6: + val += "w:%s;" % self.strings + if self.frets <> 4: + val += "h:%s;" % self.frets + if self.barre and len (self.barre) >= 3: + val += "c:%s-%s-%s;" % (self.barre[0], self.barre[1], self.barre[2]) + have_fingering = False + for i in self.elements: + if len (i) > 1: + val += "%s-%s" % (i[0], i[1]) + if len (i) > 2: + have_fingering = True + val += "-%s" % i[2] + val += ";" + if have_fingering: + val = "f:1;" + val + if val: + return "%s\\markup { \\fret-diagram #\"%s\" }" % (self.direction_mod (), val) + else: + return '' class TremoloEvent (ArticulationEvent): def __init__ (self): @@ -962,6 +1252,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): @@ -971,7 +1272,7 @@ class MultiMeasureRest(Music): 'elements (list (make-music (quote BarCheck)) (make-music - 'EventChord + 'ChordEvent 'elements (list (make-music 'MultiMeasureRestEvent @@ -993,21 +1294,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: @@ -1027,7 +1329,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 ("}") @@ -1043,7 +1345,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) @@ -1053,9 +1355,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 @@ -1063,12 +1368,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) @@ -1078,7 +1386,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: @@ -1089,8 +1397,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() @@ -1117,7 +1460,7 @@ def test_pitch (): def test_printer (): def make_note (): - evc = EventChord() + evc = ChordEvent() n = NoteEvent() evc.append (n) return n @@ -1147,21 +1490,21 @@ def test_printer (): 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 @@ -1172,7 +1515,7 @@ def test_expr (): evc.type = 'treble' m.insert_around (None, evc, 0) - evc = EventChord() + evc = ChordEvent() tonic = Pitch () tonic.step = 2 tonic.alteration = -2