class Foo(XMLElement): _DTD = Sequence( Choice(Sequence(GroupReference(TestGroup), Element(Duration)), Sequence(GroupReference(TestGroup), ))) def __init__(self, *args, **kwargs): super().__init__(tag='foo', *args, **kwargs)
class ComplexTypeBarline(ComplexType, BarlineAttributes): """ If a barline is other than a normal single barline, it should be represented by a barline type that describes it. This includes information about repeats and multiple endings, as well as line style. Barline data is on the same level as the other musical data in a score - a child of a measure in a partwise score, or a part in a timewise score. This allows for barlines within measures, as in dotted barlines that subdivide measures in complex meters. The two fermata elements allow for fermatas on both sides of the barline (the lower one inverted). Barlines have a location attribute to make it easier to process barlines independently of the other musical data in a score. It is often easier to set up measures separately from entering notes. The location attribute must match where the barline element occurs within the rest of the musical data in the score. If location is left, it should be the first element in the measure, aside from the print, bookmark, and link elements. If location is right, it should be the last element, again with the possible exception of the print, bookmark, and link elements. If no location is specified, the right barline is the default. The segno, coda, and divisions attributes work the same way as in the sound element. They are used for playback when barline elements contain segno or coda child elements. """ _DTD = Sequence( Element(BarStyle, min_occurrence=0), GroupReference(Editorial), Element(WavyLine, min_occurrence=0), Element(Segno, min_occurrence=0), Element(Coda, min_occurrence=0), Element(Fermata, min_occurrence=0, max_occurrence=2), Element(Ending, min_occurrence=0), Element(Repeat, min_occurrence=0) ) def __init__(self, *args, **kwargs): super().__init__(tag='barline', *args, **kwargs)
class Score(XMLElement, DocumentAttributes): """The score-partwise element is the root element for a partwise MusicXML score. It includes a score-header group followed by a series of parts with measures inside. The document-attributes attribute group includes the version attribute. """ _DTD = Sequence(GroupReference(ScoreHeader), Element(Part, max_occurrence=None)) def __init__(self, *args, **kwargs): super().__init__(tag='score-partwise', *args, **kwargs) def write(self, path): xmlversion = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n' doctype = '<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML {} Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">\n'.format( self.version) try: if path[-4:] != '.xml': path += '.xml' except TypeError: pass output_file = open(path, 'w') output_file.write(xmlversion) output_file.write(doctype) output_file.write(self.to_string()) output_file.close()
class ComplexTypeNotations(ComplexType, PrintObject, OptionalUniqueId): """ Notations refer to musical notations, not XML notations. Multiple notations are allowed in order to represent multiple editorial levels. The print-object attribute, added in Version 3.0, allows notations to represent details of performance technique, such as fingerings, without having them appear in the score. """ _DTD = Sequence( GroupReference(Editorial), Choice(Element(Tied), Element(Slur), Element(Tuplet), Element(Glissando), Element(Slide), Element(Ornaments), Element(Technical), Element(Articulations), Element(Dynamics), Element(Fermata), Element(Arpeggiate), Element(NonArpeggiate), Element(AccidentalMark), Element(OtherNotation), min_occurrence=0, max_occurrence=None)) def __init__(self, *args, **kwargs): super().__init__(tag='notations', *args, **kwargs)
class TypeKey(ComplexType, KeyNumberAttribute, PrintStyle, PrintObject, OptionalUniqueId): """ The key type represents a key signature. Both traditional and non-traditional key signatures are supported. The optional number attribute refers to staff numbers. If absent, the key signature applies to all staves in the part. Key signatures appear at the start of each system unless the print-object attribute has been set to "no". """ _DTD = (Sequence( Choice( GroupReference(TraditionalKey), GroupReference(NonTraditionalKey, min_occurrence=0, max_occurrence=None)), Element(KeyOctave, min_occurrence=0, max_occurrence=None))) def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs)
class ComplexTypeSystemMargins(ComplexType): """ System margins are relative to the page margins. Positive values indent and negative values reduce the margin size. """ _DTD = Sequence(GroupReference(LeftRightMargins)) def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs)
class ComplexTypeDirection(ComplexType, Placement, Directive, OptionalUniqueId): """A direction is a musical indication that is not necessarily attached to a specific note. Two or more may be combined to indicate starts and stops of wedges, dashes, etc. For applications where a specific direction is indeed attached to a specific note, the direction element can be associated with the note element that follows it in score order that is not in a different voice. By default, a series of direction-type elements and a series of child elements of a direction-type within a single direction element follow one another in sequence visually. For a series of direction-type children, non-positional formatting attributes are carried over from the previous element by default.""" _DTD = Sequence(Element(DirectionType, max_occurrence=None), Element(Offset, min_occurrence=0), GroupReference(EditorialVoiceDirection), GroupReference(Staff, min_occurrence=0), Element(Sound, min_occurrence=0)) def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs)
class Note(XMLElement, XPosition, Font, Color, Printout, OptionalUniqueId): """ Notes are the most common type of MusicXML data. The MusicXML format keeps the MuseData distinction between elements used for sound information and elements used for notation information (e.g., tie is used for sound, tied for notation). Thus grace notes do not have a duration element. Cue notes have a duration element, as do forward elements, but no tie elements. Having these two types of information available can make interchange considerably easier, as some programs handle one type of information much more readily than the other. The print-leger attribute is used to indicate whether leger lines are printed. Notes without leger lines are used to indicate indeterminate high and low notes. By default, it is set to yes. If print-object is set to no, print-leger is interpreted to also be set to no if not present. This attribute is ignored for rests. The dynamics and end-dynamics attributes correspond to MIDI 1.0's Note On and Note Off velocities, respectively. They are expressed in terms of percentages of the default forte value (90 for MIDI 1.0). The attack and release attributes are used to alter the starting and stopping time of the note from when it would otherwise occur based on the flow of durations - information that is specific to a performance. They are expressed in terms of divisions, either positive or negative. A note that starts a tie should not have a release attribute, and a note that stops a tie should not have an attack attribute. The attack and release attributes are independent of each other. The attack attribute only changes the starting time of a note, and the release attribute only changes the stopping time of a note. If a note is played only particular times through a repeat, the time-only attribute shows which times to play the note. The pizzicato attribute is used when just this note is sounded pizzicato, vs. the pizzicato element which changes overall playback between pizzicato and arco. """ _DTD = Sequence( Choice( Sequence(GroupReference(FullNote), GroupReference(DurationGroup), Element(Tie, 0, 2)), Sequence( Element(Grace), Choice(Sequence(GroupReference(FullNote)), Sequence(GroupReference(FullNote), Element(Tie, 0, 2)), Sequence(Element(Cue), GroupReference(FullNote)))), Sequence(Element(Cue), GroupReference(FullNote), GroupReference(DurationGroup)), ), Element(Instrument, 0), GroupReference(EditorialVoice, 0), Element(Type, 0), Element(Dot, 0, None), Element(Accidental, 0), Element(TimeModification, 0, None), Element(Stem, 0), Element(Notehead, 0), Element(NoteheadText, 0), GroupReference(Staff, 0), Element(Beam, 0, 8), Element(Notations, 0, None), Element(Lyric, 0, None), Element(Play, 0)) def __init__(self, *args, **kwargs): super().__init__(tag='note', *args, **kwargs)
class ComplexTypeBackup(ComplexType): """ The backup and forward elements are required to coordinate multiple voices in one part, including music on multiple staves. The backup type is generally used to move between voices and staves. Thus the backup element does not include voice or staff elements. Duration values should always be positive, and should not cross measure boundaries or mid-measure changes in the divisions value. """ _DTD = Sequence(Element(Duration), GroupReference(Editorial)) def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs)
class Rest(Event, Measure): """ The rest element indicates notated rests or silences. Rest elements are usually empty, but placement on the staff can be specified using display-step and display-octave elements. If the measure attribute is set to yes, this indicates this is a complete measure rest. """ _DTD = Sequence(GroupReference(DisplayStepOctave, min_occurrence=0)) def __init__(self, measure=None, *args, **kwargs): super().__init__(tag='rest', measure=measure, *args, **kwargs)
class ComplexTypeDefaults(ComplexType): """ The defaults type specifies score-wide defaults for scaling, layout, and appearance. """ def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs) _DTD = Sequence(Element(Scaling, min_occurrence=0), GroupReference(Layout), Element(Appearance, 0), Element(MusicFont, 0), Element(WordFont, 0), Element(LyricFont, 0, None), Element(LyricLanguage, 0, None))
class ComplexTypePartGroup(ComplexType): """ The part-group element indicates groupings of parts in the score, usually indicated by braces and brackets. Braces that are used for multi-staff parts should be defined in the attributes element for that part. The part-group start element appears before the first score-part in the group. The part-group stop element appears after the last score-part in the group. The number attribute is used to distinguish overlapping and nested part-groups, not the sequence of groups. As with parts, groups can have a name and abbreviation. Values for the child elements are ignored at the stop of a group. A part-group element is not needed for a single multi-staff part. By default, multi-staff parts include a brace symbol and (if appropriate given the bar-style) common barlines. The symbol formatting for a multi-staff part can be more fully specified using the part-symbol element. """ _DTD = Sequence(Element(GroupName, min_occurrence=0), Element(GroupNameDisplay, min_occurrence=0), Element(GroupAbbreviation, min_occurrence=0), Element(GroupAbbreviationDisplay, min_occurrence=0), Element(GroupSymbol, min_occurrence=0), Element(GroupBarline, min_occurrence=0), Element(GroupTime, min_occurrence=0), GroupReference(Editorial)) _ATTRIBUTES = [] def __init__(self, type, number='1', *args, **kwargs): super().__init__(*args, **kwargs) self.type = type self.number = number @property def number(self): return self.get_attribute('number') @number.setter def number(self, value): if value is None: self.remove_attribute('number') else: Token(value) self._ATTRIBUTES.insert(0, 'number') self.set_attribute('number', value) @property def type(self): return self.get_attribute('type') @type.setter def type(self, value): if value is None: self.remove_attribute('type') else: TypeStartStop(value) self._ATTRIBUTES.insert(0, 'type') self.set_attribute('type', value)
class Time(XMLElement, PrintObject, OptionalUniqueId): """ Time signatures are represented by the beats element for the numerator and the beat-type element for the denominator. The symbol attribute is used indicate common and cut time symbols as well as a single number display. Multiple pairs of beat and beat-type elements are used for composite time signatures with multiple denominators, such as 2/4 + 3/8. A composite such as 3+2/8 requires only one beat/beat-type pair. The print-object attribute allows a time signature to be specified but not printed, as is the case for excerpts from the middle of a score. The value is "yes" if not present. The optional number attribute refers to staff numbers within the part. If absent, the time signature applies to all staves in the part. """ _DTD = Choice( Sequence(GroupReference(TimeSignature, max_occurrence=None), Element(Interchangeable, min_occurrence=0)), Element(SenzaMisura)) def __init__(self, *args, **kwargs): super().__init__(tag='time', *args, **kwargs)
class ComplexTypePrint(ComplexType, PrintAttributes, OptionalUniqueId): """ The print type contains general printing parameters, including the layout elements defined in the layout.mod file. The part-name-display and part-abbreviation-display elements used in the score.mod file may also be used here to change how a part name or abbreviation is displayed over the course of a piece. They take effect when the current measure or a succeeding measure starts a new system. Layout elements in a print statement only apply to the current page, system, staff, or measure. Music that follows continues to take the default values from the layout included in the defaults element. """ _DTD = Sequence(GroupReference(Layout), Element(MeasureLayout, min_occurrence=0), Element(MeasureNumbering, min_occurrence=0), Element(PartNameDisplay, min_occurrence=0), Element(PartAbbreviationDisplay, min_occurrence=0)) def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs)
class ComplexTypeAttributes(ComplexType): """ The attributes element contains musical information that typically changes on measure boundaries. This includes key and time signatures, clefs, transpositions, and staving. When attributes are changed mid-measure, it affects the music in score order, not in MusicXML document order """ _DTD = Sequence( GroupReference(Editorial), Element(Divisions, min_occurrence=0), Element(Key, min_occurrence=0, max_occurrence=None), Element(Time, min_occurrence=0, max_occurrence=None), Element(Staves, min_occurrence=0), Element(PartSymbol, min_occurrence=0), Element(Instruments, min_occurrence=0), Element(Clef, min_occurrence=0, max_occurrence=None), Element(StaffDetails, min_occurrence=0, max_occurrence=None), Element(Transpose, min_occurrence=0, max_occurrence=None), Element(Directive, min_occurrence=0, max_occurrence=None), Element(MeasureStyle, min_occurrence=0, max_occurrence=None)) def __init__(self, tag, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs)
class ComplexTypePageMargins(ComplexType): """Page margins are specified either for both even and odd pages, or via separate odd and even page number values. The type attribute is not needed when used as part of a print element. If omitted when the page-margins type is used in the defaults element, "both" is the default value.""" _DTD = Sequence(GroupReference(AllMargins)) def __init__(self, tag, type_=None, *args, **kwargs): super().__init__(tag=tag, *args, **kwargs) self.type = type_ @property def type(self): return self.get_attribute('type') @type.setter def type(self, value): if value is None: self.remove_attribute('type') else: TypeMarginType(value) self._ATTRIBUTES = ['type'] self.set_attribute('type', value)
class TreeChord(XMLTree): _DTD = Sequence( Choice( Sequence(GroupReference(FullNote), Element(Duration), Element(Tie, 0, 2)), Sequence(Element(Cue), GroupReference(FullNote), Element(Duration)), ), Element(Attributes, 0), Element(Direction, 0, None), Element(Instrument, 0), GroupReference(EditorialVoice, 0), Element(Type, 0), Element(Dot, 0, None), Element(TimeModification, 0, None), Element(Stem, 0), Element(Notehead, 0), Element(NoteheadText, 0), GroupReference(Staff, 0), Element(Beam, 0, 8), Element(Notations, 0, None), Element(Lyric, 0, None), Element(Play, 0)) def __init__(self, midis=71, quarter_duration=1, zero_mode='grace', **kwargs): super().__init__(**kwargs) self.parent_tree_part_voice = None self.parent_beat = None self._notes = None self._offset = None self._quarter_duration = None self._zero_mode = None self.zero_mode = zero_mode self.quarter_duration = quarter_duration self._midis = None self._pre_grace_chords = [] self._post_grace_chords = [] self.midis = midis self._tail = False self._head = False self._is_adjoinable = True self._flags = None self._manual_type = False self._manual_dots = False self._manual_staff_number = None self.manual_voice_number = None self.is_finger_tremolo = False self.relative_x = None self.tie_orientation = None # //private methods def _generate_notes(self): output = [] if self.zero_mode == 'remove' and self.quarter_duration == 0: return output for index, midi in enumerate(self.midis): note = TreeNote(parent_chord=self, event=midi.get_pitch_rest()) note.update_grace() note.accidental.set_force_show(midi.accidental.force_show) note.accidental.set_force_hide(midi.accidental.force_hide) note.is_finger_tremolo = self.is_finger_tremolo if self.relative_x is not None: note.relative_x = self.relative_x if midi.notehead: note.add_child(midi.notehead) for child in self.get_children(): if isinstance(child, Lyric) and index != 0: pass elif isinstance(child, Notations) and index != 0: grandchildren = child.get_children() for grandchild in grandchildren: if type(grandchild) not in (Ornaments, Technical, Articulations, Dynamics): note._add_notations(grandchild) elif isinstance(child, Direction): pass elif isinstance(child, Attributes): pass else: note.add_child(child) if index > 0: note.add_child(Chord()) output.append(note) return output def _get_staff_object(self): try: return self.get_children_by_type(StaffElement)[0] except IndexError: return None def _get_direction_types(self): output = [] for direction in self.get_children_by_type(Direction): for dt in direction.get_children_by_type(DirectionType): output.append(dt) return output def _remove_direction(self, direction): self.remove_child(direction) def _remove_direction_type(self, direction_type): parent = direction_type.up parent.remove_child(direction_type) if not parent.get_children(): self._remove_direction(parent) def _set_staff_number(self, val): if not isinstance(val, int): raise TypeError( 'staff_number.value must be of type int not{}'.format( type(val))) staff = self._get_staff_object() if staff is None: self.add_child(StaffElement(val)) else: staff.value = val # //public properties @property def end_position(self): return self.offset + self.quarter_duration @property def flags(self): if self._flags is None: self._flags = set() return self._flags @property def is_adjoinable(self): return self._is_adjoinable @is_adjoinable.setter def is_adjoinable(self, value): if not isinstance(value, bool): raise TypeError( 'is_adjoinable.value must be of type bool not{}'.format( type(value))) self._is_adjoinable = value @property def is_head(self): return self._head @property def is_rest(self): if self.midis[0].value == 0: return True return False @property def is_tail(self): return self._tail @property def is_tied_to_next(self): if 'start' in self.tie_types: return True return False @property def is_tied_to_previous(self): if 'stop' in self.tie_types: return True return False @property def manual_staff_number(self): return self._manual_staff_number @manual_staff_number.setter def manual_staff_number(self, val): if not isinstance(val, int): raise TypeError( 'manual_staff_number.value must be of type int not{}'.format( type(val))) self._manual_staff_number = val self._set_staff_number(val) @property def midis(self): return self._midis @midis.setter def midis(self, values): try: values = list(values) except TypeError: values = [values] output = [] for midi in values: if not isinstance(midi, Midi): output.append(Midi(midi)) else: output.append(midi) for midi in output: if midi.value == 0 and len(values) > 1: raise ValueError('midi with value 0 must be alone.') output = sorted(output, key=lambda midi: midi.value) self._midis = output @property def notes(self): if self._notes is None: self._notes = self._generate_notes() return self._notes @property def next_in_score(self): index = self.parent_tree_part_voice.chords.index(self) if index == len(self.parent_tree_part_voice.chords) - 1: current_part = self.parent_tree_part_voice.parent_tree_part_staff.parent_part next_measure = current_part.up.next if next_measure: next_part = next_measure.get_part_with_id(id=current_part.id) next_chord = next_part.get_staff( self.parent_tree_part_voice.parent_tree_part_staff.number ).get_voice(self.parent_tree_part_voice.number).chords[0] return next_chord else: return None else: return self.parent_tree_part_voice.chords[index + 1] @property def offset(self): if self._offset is None: self.update_offset() return self._offset @property def position_in_beat(self): index_in_beat = self.parent_beat.chords.index(self) if index_in_beat == 0: return 0 previous_in_beat = self.parent_beat.chords[index_in_beat - 1] return previous_in_beat.position_in_beat + previous_in_beat.quarter_duration @property def previous_in_part_voice(self): index = self.parent_tree_part_voice.chords.index(self) if index == 0: return None return self.parent_tree_part_voice.chords[index - 1] @property def previous_in_score_voice(self): if self.previous_in_part_voice is None: current_part = self.parent_tree_part_voice.parent_tree_part_staff.parent_part previous_measure = current_part.up.previous if previous_measure: previous_part = previous_measure.get_part_with_id( id=current_part.id) previous_chord = \ previous_part.get_staff(self.parent_tree_part_voice.parent_tree_part_staff.number).get_voice( self.parent_tree_part_voice.number).chords[-1] return previous_chord else: return None else: return self.previous_in_part_voice @property def quarter_duration(self): return self._quarter_duration @quarter_duration.setter def quarter_duration(self, value): if not isinstance(value, int) and not isinstance( value, float) and not isinstance(value, Fraction): raise TypeError( 'quarter_duration must be of type int, float or Fraction not {}' .format(type(value))) if value < 0: raise ValueError( 'quarter_duration {} must be zero or positive'.format(value)) if not isinstance(self.quarter_duration, Fraction): self._quarter_duration = Fraction(value) else: self._quarter_duration = value @property def staff_number(self): staff = self._get_staff_object() if staff: return staff.value else: return None @staff_number.setter def staff_number(self, val): if self.manual_staff_number is None: self._set_staff_number(val) @property def tie_types(self): return [tie.type for tie in self.get_children_by_type(Tie)] @property def tremoli(self): try: return self.get_children_by_type( Notations)[0].get_children_by_type( Ornaments)[0].get_children_by_type(Tremolo) except IndexError: return [] def update_offset(self): if self.previous_in_part_voice: output = self.previous_in_part_voice.offset + self.previous_in_part_voice.quarter_duration self._offset = output else: self._offset = 0 @property def zero_mode(self): return self._zero_mode @zero_mode.setter def zero_mode(self, val): permitted = ['remove', 'grace'] if val not in permitted: raise ValueError('zero_mode.value {} must be in {}'.format( val, permitted)) self._zero_mode = val @property def __name__(self): # return self.parent_tree_part_voice.__name__ + ' ' + 'ch:' + str(self.parent_tree_part_voice.chords.index(self) + 1) return self.parent_tree_part_voice.__name__ + '.' + str( self.parent_tree_part_voice.chords.index(self) + 1) # //public methods # add def add_action_dynamics(self, value, **kwargs): dynamics = self.add_dynamics(value, **kwargs)[0] direction = dynamics.up.up direction_type = direction.add_child(DirectionType()) direction_type.add_child(Words(value='»')) for child in direction.get_children(): if child.get_children_by_type(Dynamics): direction.remove_child(child) direction_type = direction.add_child(DirectionType()) direction_type.add_child(dynamics) direction_type = direction.add_child(DirectionType()) direction_type.add_child(Words(value='«', font_size=10)) def add_articulation(self, articulation, **kwargs): def add_type(dict): new_dict = dict.copy() if 'type' not in new_dict: new_dict['type'] = 'up' return new_dict def add_breath_mark_value(dict): new_dict = dict.copy() if 'value' not in new_dict: new_dict['value'] = 'comma' return new_dict articulations = { 'accent': Accent(**kwargs), 'strong-accent': StrongAccent(**add_type(kwargs)), 'staccato': Staccato(**kwargs), 'tenuto': Tenuto(**kwargs), 'detached-legato': DetachedLegato(**kwargs), 'staccatissimo': Staccatissimo(**kwargs), 'spiccato': Spiccato(**kwargs), 'scoop': Scoop(**kwargs), 'plop': Plop(**kwargs), 'doit': Doit(**kwargs), 'falloff': Falloff(**kwargs), 'breath-mark': BreathMark(**add_breath_mark_value(kwargs)), 'caesura': Caesura(**kwargs), 'stress': Stress(**kwargs), 'unstress': Unstress(**kwargs) } if articulation not in articulations: raise ValueError('articulation {} must be in {}'.format( articulation, list(articulations.keys()))) return self.add_articulation_object(articulations[articulation]) def add_articulation_object(self, articulation_object): try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) try: articulations = notations.get_children_by_type(Articulations)[0] except IndexError: articulations = notations.add_child(Articulations()) articulations.add_child(articulation_object) def add_bracket(self, type, line_end, placement='above', **kwargs): d = self.add_child(Direction(placement=placement)) dt = d.add_child(DirectionType()) dt.add_child(Bracket(type=type, line_end=line_end, **kwargs)) def add_clef(self, clef): try: attributes = self.get_children_by_type(Attributes)[0] except IndexError: attributes = self.add_child(Attributes()) clef_copy = clef.__deepcopy__() attributes.add_child(clef_copy) def add_dynamics(self, values, placement='below', **kwargs): dynamic_classes = [ P, PP, PPP, PPPP, PPPPP, PPPPPP, F, FF, FFF, FFFF, FFFFF, FFFFFF, MP, MF, SF, SFP, SFPP, FP, RF, RFZ, SFZ, SFFZ, FZ, N, PF, SFZP ] tags = [d._TAG for d in dynamic_classes] if isinstance(values, str) or not hasattr(values, '__iter__'): values = [values] direction = self.add_child(Direction(placement=placement)) direction_type = direction.add_child(DirectionType()) output = [] for value in values: try: index = tags.index(value) except ValueError: raise ValueError('wrong dynamics value') dynamics = direction_type.add_child(Dynamics(**kwargs)) dynamics.add_child(dynamic_classes[index]()) output.append(dynamics) return output def add_fermata(self, value='normal', **kwargs): fermata = Fermata(value, **kwargs) try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) notations.add_child(fermata) def add_flag(self, flag): if not isinstance(flag, TreeChordFlag1) and not isinstance(flag, TreeChordFlag2) \ and not isinstance(flag, TreeChordFlag3): raise TypeError( 'flag must be of type TreeChordFlag1, TreeChordFlag2 or TreeChordFlag3 not {}' .format(flag.__class__)) if self._flags is None: self._flags = set() self._flags.add(flag) def add_grace_chords(self, chords, mode='pre'): permitted = ['pre', 'post'] if mode not in permitted: raise ValueError('mode must be in {}'.format(permitted)) try: chords = list(chords) except TypeError: chords = [chords] for chord in chords: if not isinstance(chord, TreeChord): raise TypeError('wrong type {}'.format(type(chord))) for chord in chords: chord.quarter_duration = 0 chord.zero_mode = 'grace' if mode == 'pre': self._pre_grace_chords.append(chord) else: self._post_grace_chords.append(chord) def add_harmonic(self, interval): if len(self.midis) != 1: raise Exception( 'harmonic can only be added to chords with one midi') new_midi = self.add_midi(self.midis[0].value + interval) alter = self.midis[0].get_pitch_name()[1] if alter: if alter > 0: new_midi.accidental.mode = 'sharp' else: new_midi.accidental.mode = 'flat' else: new_midi.accidental.mode = 'flat' self.midis[-1].add_notehead('diamond', filled='no') def add_lyric(self, text=None, number=1, syllabic=None, extend=None, **kwargs): lyric = self.add_child(Lyric(number=str(number), **kwargs)) if syllabic is not None: lyric.add_child(Syllabic(syllabic)) if extend is not None: lyric.add_child(Extend(extend)) if text is not None: lyric.add_child(Text(str(text))) return lyric def add_pedal(self, type, placement='below', **kwargs): d = self.add_child(Direction(placement=placement)) dt = d.add_child(DirectionType()) dt.add_child(Pedal(type=type, **kwargs)) def add_midi(self, val): if val == 0: raise ValueError('midi with value 0 can not be added.') if not isinstance(val, Midi): val = Midi(val) self._midis.append(val) return val def add_notations_object(self, object): try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) notations.add_child(object) return object def add_slide(self, type, **kwargs): object = Slide(type, **kwargs) return self.add_notations_object(object) def add_slur_object(self, slur): try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) notations.add_child(slur) return slur def add_slur(self, type, **kwargs): slur = Slur(type, **kwargs) return self.add_slur_object(slur) def add_technical_object(self, technical_object): try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) try: technical = notations.get_children_by_type(Technical)[0] except IndexError: technical = notations.add_child(Technical()) technical.add_child(technical_object) def add_tuplet(self, type, number=1): normals = { 3: 2, 5: 4, 6: 4, 7: 4, 9: 8, 10: 8, 11: 8, 12: 8, 13: 8, 14: 8, 15: 8 } types = {8: '32nd', 4: '16th', 2: 'eighth'} actual_notes = self.parent_beat.best_div normal_notes = normals[actual_notes] normal_type = types[normal_notes / self.parent_beat.duration] if type != 'continue': try: notations = self.notations except AttributeError: notations = self.add_child(Notations()) v = self.get_children_by_type(Voice)[0] if int(v.value) % 2 == 0: placement = 'below' else: placement = 'above' notations.add_child( Tuplet(type=type, number=number, bracket='yes', placement=placement)) tm = self.add_child(TimeModification()) tm.add_child(ActualNotes(actual_notes)) tm.add_child(NormalNotes(normal_notes)) tm.add_child(NormalType(normal_type)) def add_tremolo(self, number=3, **kwargs): try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) try: ornaments = notations.get_children_by_type(Ornaments)[0] except IndexError: ornaments = notations.add_child(Ornaments()) ornaments.add_child(Tremolo(value=number, **kwargs)) def get_notations(self): try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) return notations def get_ornaments(self): notations = self.get_notations() try: ornaments = notations.get_children_by_type(Ornaments)[0] except IndexError: ornaments = notations.add_child(Ornaments()) return ornaments def add_trill_mark(self, **kwargs): ornaments = self.get_ornaments() return ornaments.add_child(TrillMark(**kwargs)) def add_wavy_line(self, **kwargs): ornaments = self.get_ornaments() return ornaments.add_child(WavyLine(**kwargs)) def add_tie(self, value): if value not in ('stop', 'start'): raise NotImplementedError( 'value {} cannot be a tie value'.format(value)) try: notations = self.get_children_by_type(Notations)[0] except IndexError: notations = self.add_child(Notations()) if value == 'start' and 'start' not in self.tie_types: self.add_child(Tie('start')) notations.add_child(Tied('start', orientation=self.tie_orientation)) elif value == 'stop' and 'stop' not in self.tie_types: self.add_child(Tie('stop')) notations.add_child(Tied('stop')) def add_wedge(self, value, placement='below', **kwargs): wedge_object = Wedge(value, **kwargs) direction = self.add_child(Direction(placement=placement)) direction_type = direction.add_child(DirectionType()) wedge = direction_type.add_child(wedge_object) # # dynamics.add_child(dynamic_classes[index]()) return wedge def add_words(self, words, placement='above', **kwargs): d = self.add_child(Direction(placement=placement)) dt = d.add_child(DirectionType()) if isinstance(words, Words): if kwargs: raise ValueError( 'no keywords possible if add_words gets a Words()') dt.add_child(words) else: dt.add_child(Words(value=str(words), **kwargs)) # get def get_articulations(self): output = [] for notations in self.get_children_by_type(Notations): articulations = notations.get_children_by_type(Articulations) for articulation in articulations: output.extend(articulation.get_children()) return output def get_clef(self): try: attributes = self.get_children_by_type(Attributes)[0] return attributes.get_children_by_type(TreeClef)[0] except IndexError: return None def get_dynamics(self): directions = self.get_children_by_type(Direction) for direction in directions: direction_types = direction.get_children_by_type(DirectionType) for direction_type in direction_types: for child in direction_type.get_children(): if isinstance(child, Dynamics): return child.get_children()[0].to_string()[1:-3] return None def get_notes(self): return self.notes def get_pre_grace_chords(self): return self._pre_grace_chords def get_post_grace_chords(self): return self._post_grace_chords def get_words(self): output = [] for dt in self._get_direction_types(): for child in dt.get_children(): if isinstance(child, Words): output.append(child) return output # remove def remove_dynamics(self): for direction_type in self._get_direction_types(): for child in direction_type.get_children(): if isinstance(child, Dynamics): direction_type.remove_child(child) if not direction_type.get_children(): self._remove_direction_type(direction_type) def remove_flag(self, flag): if flag in self._flags: self._flags.remove(flag) def remove_from_score(self): if 'stop' in self.tie_types and 'start' not in self.tie_types: self.remove_tie('stop') previous_chord = self.previous_in_score_voice if previous_chord: previous_chord.remove_tie('start') elif 'start' in self.tie_types and 'stop' not in self.tie_types: self.remove_tie('start') next_chord = self.next_in_score if next_chord: next_chord.remove_tie('stop') for l in self.get_children_by_type(Lyric): next_chord.add_child(l) for n in self.get_children_by_type(Notations): next_chord.add_child(n) for d in self.get_children_by_type(Direction): next_chord.add_child(d) clef = self.get_clef() if clef: next_chord.add_clef(clef) self.parent_beat.chords.remove(self) self.parent_tree_part_voice.chords.remove(self) def remove_notations(self): notations = self.get_children_by_type(Notations) for notation in notations: self.remove_child(notation) def remove_previous_tie(self): if 'stop' in self.tie_types: self.remove_tie('stop') try: self.previous_in_score_part.remove_tie('start') except AttributeError: pass def remove_slur(self, type): try: notations = self.get_children_by_type(Notations)[0] slurs = [ s for s in notations.get_children_by_type(Slur) if s.type == type ] for slur in slurs: notations.remove_child(slur) if not notations.get_children(): self.remove_child(notations) except IndexError: pass def remove_tie(self, type): if type in self.tie_types: notations = self.get_children_by_type(Notations)[0] tie = [ t for t in self.get_children_by_type(Tie) if t.type == type ][0] self.remove_child(tie) tied = [ t for t in notations.get_children_by_type(Tied) if t.type == type ][0] notations.remove_child(tied) if not notations.get_children(): self.remove_child(notations) def remove_voice(self): for voice in self.get_children_by_type(Voice): self.remove_child(voice) # set def set_manual_type(self, val, **kwargs): try: chord_type = self.get_children_by_type(Type)[0] self.remove_child(chord_type) except IndexError: self.add_child(Type(value=val, **kwargs)) self._manual_type = True def set_manual_dots(self, val): try: for dot in self.get_children_by_type(Dot): self.remove_child(dot) except IndexError: pass for i in range(val): self.add_child(Dot()) self._manual_dots = True def set_staff_number(self, staff_number): self.manual_staff_number = staff_number def set_tie_orientation(self, orientation): if self.is_tied_to_next: tied = [ t for t in self.get_children_by_type(Notations) [0].get_children_by_type(Tied) if t.type == 'start' ] for t in tied: t.orientation = orientation def set_voice_number(self, voice_number): self.manual_voice_number = voice_number # update def update_dot(self): if self._manual_dots is False: _dot = 0 if self.quarter_duration.numerator % 3 == 0: _dot = 1 elif self.quarter_duration == Fraction( 1, 2) and self.parent_beat.best_div == 6: _dot = 1 elif self.quarter_duration == Fraction( 1, 4) and self.parent_beat.best_div == 6: _dot = 1 elif (self.quarter_duration == Fraction(3, 9) or self.quarter_duration == Fraction( 6, 9)) and self.parent_beat.best_div == 9: _dot = 1 elif self.quarter_duration == Fraction(7, 8): _dot = 2 elif self.quarter_duration == Fraction(7, 4): _dot = 2 else: _dot = 0 for dot in self.get_children_by_type(Dot): self.remove_child(dot) for i in range(_dot): self.add_child(Dot()) def update_type(self): """get type of a Note() depending on its quantized duration and return it [whole, half, quarter, eighth, 16th, 32nd, 64th]""" _types = { (1, 12): '32nd', (1, 11): '32nd', (2, 11): '16th', (3, 11): '16th', (4, 11): 'eighth', (6, 11): 'eighth', (8, 11): 'quarter', (1, 10): '32nd', (3, 10): '16th', (1, 9): '32nd', (2, 9): '16th', (4, 9): 'eighth', (8, 9): 'quarter', (1, 8): '32nd', (3, 8): '16th', (7, 8): 'eighth', (1, 7): '16th', (2, 7): 'eighth', (3, 7): 'eighth', (4, 7): 'quarter', (6, 7): 'quarter', (1, 6): '16th', (1, 5): '16th', (2, 5): 'eighth', (3, 5): 'eighth', (4, 5): 'quarter', (1, 4): '16th', (2, 4): 'eighth', (3, 4): 'eighth', (7, 4): 'quarter', (1, 3): 'eighth', (2, 3): 'quarter', (3, 2): 'quarter', (1, 2): 'eighth', (1, 1): 'quarter', (2, 1): 'half', (3, 1): 'half', (4, 1): 'whole', (6, 1): 'whole', (8, 1): 'breve', (12, 1): 'breve' } if self._manual_type is False: tremoli_types = [tremolo.type for tremolo in self.tremoli] if self.quarter_duration == 0: value = 'eighth' elif ('start' in tremoli_types or 'stop' in tremoli_types): show_duration = self.quarter_duration * 2 value = _types[(show_duration.numerator, show_duration.denominator)] if not self.get_children_by_type(TimeModification): tm = TimeModification() tm.add_child(ActualNotes(2)) tm.add_child(NormalNotes(1)) self.add_child(tm) else: value = _types[(self.quarter_duration.numerator, self.quarter_duration.denominator)] try: chord_type = self.get_children_by_type(Type)[0] chord_type.value = value except IndexError: self.add_child(Type(value)) # others def change_range(self, min_midi, max_midi, microtone=2): if self.is_rest: pass else: scale = Scale(self.midis[0].value, self.midis[-1].value, min_midi, max_midi, step=2 / microtone) self.midis = [scale(midi.value) for midi in self.midis] def force_tie(self): self.is_adjoinable = False def has_same_pitches(self, other_chord): if len(self.notes) == len(other_chord.notes): for note_1, note_2 in zip(self.notes, other_chord.notes): if isinstance(note_1.event, Rest) or isinstance( note_2.event, Rest): return False pitch_1 = note_1.pitch.dump() pitch_2 = note_2.pitch.dump() if len(pitch_1) == len(pitch_2): for i in range(1, len(pitch_1)): if pitch_1[i].value != pitch_2[i].value: return False return True return False def inverse(self): intervals = xToD([midi.value for midi in self.midis]) intervals = [-interval for interval in intervals] self.midis = dToX(intervals, first_element=self.midis[0].value) def split(self, *ratios): if len(ratios) == 1: ratios = ratios[0] new_ratios = [ Fraction(Fraction(ratio), Fraction(sum(ratios))) for ratio in ratios ] old_duration = self.quarter_duration self.quarter_duration *= new_ratios[0] new_chords = [ self.split_copy(quarter_duration=ratio * old_duration) for ratio in new_ratios[1:] ] for ch in new_chords: ch.tie_orientation = self.tie_orientation if 'start' in self.tie_types: new_chords[-1].add_tie('start') if not self.is_adjoinable: self.is_adjoinable = True new_chords[-1].is_adjoinable = False for tremolo in self.tremoli: new_chords[-1].add_tremolo(number=tremolo.value) new_chords.insert(0, self) if self.midis[0].value != 0: for new_chord in new_chords[1:]: new_chord.add_tie('stop') for new_chord in new_chords[:-1]: new_chord.add_tie('start') return new_chords def to_rest(self): self.midis = [0] self.remove_tie('stop') self.remove_tie('start') for notation in self.get_children_by_type(Notations): articulations = notation.get_children_by_type(Articulations) for articulation in articulations: notation.remove_child(articulation) def transpose(self, interval): for midi in self.midis: if midi.value != 0: midi.value += interval def untie(self): if 'start' in self.tie_types: self.remove_tie('start') try: self.next.remove_tie('stop') except AttributeError: pass # //copy def tremolo_chord_copy(self): new_chord = self.split_copy(quarter_duration=0) # new_chord = TreeChord() # new_chord.midis = self.midis # new_chord.manual_staff_number = self.manual_staff_number return new_chord def split_copy(self, quarter_duration): new_chord = TreeChord(quarter_duration=quarter_duration) new_chord.midis = self.midis new_chord.parent_tree_part_voice = self.parent_tree_part_voice new_chord.parent_beat = self.parent_beat if self._flags is not None: new_chord._flags = [] for flag in self._flags: new_chord._flags.append(flag.__deepcopy__()) new_chord._offset = None new_chord.is_finger_tremolo = self.is_finger_tremolo for grace_chord in self.get_post_grace_chords(): new_chord.add_grace_chords(grace_chord.__deepcopy__(), mode='post') try: voice = self.get_children_by_type(Voice)[0] new_chord.add_child(voice) except IndexError: pass try: staff = self.get_children_by_type(StaffElement)[0] new_chord.add_child(staff) except IndexError: pass try: stem = self.get_children_by_type(Stem)[0] new_chord.add_child(stem) except IndexError: pass try: notehead = self.get_children_by_type(Notehead)[0] new_chord.add_child(notehead) except IndexError: pass return new_chord def deepcopy_for_SimpleFormat(self): new_chord = TreeChord(quarter_duration=self.quarter_duration, zero_mode=self.zero_mode) new_chord.midis = [midi.__deepcopy__() for midi in self.midis] for child in self.get_children(): if not isinstance(child, Voice): new_chord.add_child(child) for grace_chord in self.get_pre_grace_chords(): new_chord.add_grace_chords(grace_chord.deepcopy_for_SimpleFormat()) for grace_chord in self.get_post_grace_chords(): new_chord.add_grace_chords( grace_chord.deepcopy_for_SimpleFormat().__deepcopy__(), 'post') return new_chord def __deepcopy__(self, memodict={}): new_chord = TreeChord(quarter_duration=self.quarter_duration, zero_mode=self.zero_mode) new_chord.midis = [midi.__deepcopy__() for midi in self.midis] for child in self.get_children(): if isinstance(child, Notations): copied_notations = Notations() for grand_child in child.get_children(): copied_notations.add_child(grand_child) new_chord.add_child(copied_notations) else: new_chord.add_child(child) new_chord.is_adjoinable = self.is_adjoinable if self._flags: new_chord._flags = [] for flag in self._flags: new_chord._flags.append(flag.__deepcopy__()) new_chord._flags = self._flags new_chord._manual_type = self._manual_type new_chord._manual_staff_number = self._manual_staff_number new_chord.tie_orientation = self.tie_orientation for grace_chord in self.get_pre_grace_chords(): new_chord.add_grace_chords(grace_chord.__deepcopy__()) for grace_chord in self.get_post_grace_chords(): new_chord.add_grace_chords(grace_chord.__deepcopy__(), 'post') return new_chord
class Measure(XMLElement, MeasureAttributes): _DTD = Sequence(GroupReference(MusicData)) def __init__(self, number=None, *args, **kwargs): super().__init__(tag='measure', number=number, *args, **kwargs)
min_occurrence=0 ), Element(Text) , min_occurrence=0, max_occurrence=None ), Element(Extend, min_occurrence=0) ), Element(Extend), Element(Laughing), Element(Humming) ), Element(EndLine, min_occurrence=0), Element(EndParagraph, min_occurrence=0), GroupReference(Editorial) ) note_dtd = Sequence( Choice( Sequence( Element(Grace), Choice( Sequence( GroupReference(FullNote) ), Sequence( GroupReference(FullNote), Element(Tie, 0, 2) ), Sequence(
class Part(XMLElement, PartAttributes): _DTD = Sequence(GroupReference(MusicData)) def __init__(self, id, *args, **kwargs): super().__init__(tag='part', id=id, *args, **kwargs)
class ComplexTypeLyric(ComplexType, Justify, Position, Placement, Color, PrintObject, TimeOnly, OptionalUniqueId): """The lyric type represents text underlays for lyrics, based on Humdrum with support for other formats. Two text elements that are not separated by an elision element are part of the same syllable, but may have different text formatting. The MusicXML XSD is more strict than the DTD in enforcing this by disallowing a second syllabic element unless preceded by an elision element. The lyric number indicates multiple lines, though a name can be used as well (as in Finale's verse / chorus / section specification). Justification is center by default; placement is below by default. The print-object attribute can override a note's print-lyric attribute in cases where only some lyrics on a note are printed, as when lyrics for later verses are printed in a block of text rather than with each note. The time-only attribute precisely specifies which lyrics are to be sung which time through a repeated section. """ # todo: sorting mixed Sequence with min_occurrence = 0 and max_occurrence = None and check_occurrence. # Syllabic doubled by sort _DTD = Sequence( Choice( Sequence( Element(Syllabic, min_occurrence=0), Element(Text), # Sequence( # Sequence( # Element(Elision), # Element(Syllabic, min_occurrence=0), # min_occurrence=0 # ), # Element(Text) # # , # # min_occurrence=0, # # max_occurrence=None # ), Element(Extend, min_occurrence=0)), Element(Extend), Element(Laughing), Element(Humming)), Element(EndLine, min_occurrence=0), Element(EndParagraph, min_occurrence=0), GroupReference(Editorial)) def __init__(self, tag, number='1', *args, **kwargs): super().__init__(tag=tag, *args, **kwargs) self.number = number @property def number(self): return self.get_attribute('number') @number.setter def number(self, value): if value is None: self.remove_attribute('number') else: Token(value) self._ATTRIBUTES.insert(0, 'number') self.set_attribute('number', value) @property def name(self): return self.get_attribute('name') @name.setter def name(self, value): if value is None: self.remove_attribute('name') else: Token(value) self._ATTRIBUTES.insert(0, 'name') self.set_attribute('name', value)
_TAG = 'top-margin' def __init__(self, value=None, *args, **kwargs): super().__init__(tag=self._TAG, value=value, *args, **kwargs) class BottomMargin(XMLElement, TypeTenths): _TAG = 'bottom-margin' def __init__(self, value=None, *args, **kwargs): super().__init__(tag=self._TAG, value=value, *args, **kwargs) """ The left-right-margins group specifies horizontal margins in tenths. """ LeftRightMargins = Sequence( Element(LeftMargin), Element(RightMargin) ) """ The all-margins group specifies both horizontal and vertical margins in tenths. """ AllMargins = Sequence( GroupReference(LeftRightMargins), Element(TopMargin), Element(BottomMargin) )
) ) # seq4 = GroupReference(FullNote) # seq4 = Sequence( # Element(Chord, min_occurrence=0), # Choice( # Element(Pitch), # Element(Unpitched), # Element(Rest) # ) # ) seq4 = Sequence( GroupReference(FullNote) ) seq5 = Choice( Sequence( GroupReference(FullNote), Element(Tie, 0, 2) ) ) seq6 = Sequence( Element(Grace), Choice( Sequence( GroupReference(FullNote) ),