Source code for music21.tie

# ------------------------------------------------------------------------------
# Name:         tie.py
# Purpose:      music21 classes for representing ties (visual and conceptual)
#
# Authors:      Michael Scott Asato Cuthbert
#               Christopher Ariza
#
# Copyright:    Copyright © 2009-2023 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# ------------------------------------------------------------------------------
'''
The `tie` module contains a single class, `Tie` that represents the visual and
conceptual idea of tied notes.  They can be start or stop ties.
'''
from __future__ import annotations

import unittest

from music21 import exceptions21
from music21.common.objects import SlottedObjectMixin
from music21 import prebase

# Delete in v10.  Raise ValueError instead.
class TieException(ValueError, exceptions21.Music21Exception):
    pass


# ------------------------------------------------------------------------------
[docs] class Tie(prebase.ProtoM21Object, SlottedObjectMixin): # pylint: disable=line-too-long ''' An object added to Notes that are tied to other notes. The `type` value is one of start, stop, or continue. >>> note1 = note.Note() >>> note1.tie = tie.Tie('start') # start, stop, or continue >>> note1.tie.style = 'normal' # default; could also be 'dotted' or 'dashed' or 'hidden' >>> note1.tie.type 'start' >>> note1.tie <music21.tie.Tie start> Generally Ties have a placement of None, but if they are defined as 'above' or 'below' this will be retained. (see: `this discussion`_ for how orientation and placement in musicxml are essentially the same content). .. _this discussion: https://web.archive.org/web/20210302051136/https://forums.makemusic.com/viewtopic.php?f=12&t=2179&start=0 >>> note1.tie.placement is None True Differences from MusicXML: * notes do not need to know if they are tied from a previous note. i.e., you can tie n1 to n2 just with a tie start on n1. However, if you want proper musicXML output you need a tie stop on n2. * one tie with "continue" implies tied from and tied to. The tie.style only applies to Tie objects of type 'start' or 'continue' (and then only to the next part of the tie). For instance, if there are two tied notes, and the first note has a 'dotted'-start tie, and the second note has a 'dashed'-stop tie, the graphical tie itself will be dotted. A type of tie that is unknown raises a ValueError: >>> tie.Tie('hello') Traceback (most recent call last): music21.tie.TieException: Type must be one of ('start', 'stop', 'continue', 'let-ring', 'continue-let-ring'), not hello OMIT_FROM_DOCS optional (to know what notes are next): .to = note() # not implemented yet, b/c of garbage coll. .from = note() (question: should notes be able to be tied to multiple notes for the case where a single note is tied both voices of a two-note-head unison?) ''' # CLASS VARIABLES # __slots__ = ( 'id', 'placement', 'style', 'type', ) _DOC_ATTR: dict[str, str] = { 'type': ''' The tie type, can be 'start', 'stop', 'continue', 'let-ring', or 'continue-let-ring'. ''', 'style': ''' The style of the tie. Currently can be 'normal', 'dotted', 'dashed' or 'hidden'. ''', 'placement': ''' Whether the tie should go up or down. Can be None, meaning it is unknown or should be determined from context, or 'above' or 'below'. ''', } VALID_TIE_TYPES = ('start', 'stop', 'continue', 'let-ring', 'continue-let-ring') # pylint: disable=redefined-builtin def __init__(self, type='start'): # super().__init__() # no need for ProtoM21Object or SlottedObjectMixin if type not in self.VALID_TIE_TYPES: raise TieException( f'Type must be one of {self.VALID_TIE_TYPES}, not {type}') # naming this 'type' was a mistake, because cannot create a property of this name. # this is not the correct way we want to do this, I don't think self.id = id(self) self.type = type self.style = 'normal' # or dashed self.placement = None # = unknown, can be 'above' or 'below' # SPECIAL METHODS #
[docs] def __eq__(self, other): # noinspection PyComparisonWithNone ''' Equality. Based entirely on Tie.type. >>> t1 = tie.Tie('start') >>> t2 = tie.Tie('start') >>> t3 = tie.Tie('stop') >>> t1 == t2 True >>> t2 == t3, t3 == t1 (False, False) >>> t2 == None False ''' if not isinstance(other, type(self)): return False elif self.type == other.type: return True return False
def __hash__(self): return id(self) >> 4 def _reprInternal(self): return self.type
class Test(unittest.TestCase): def testCopyAndDeepcopy(self): from music21.test.commonTest import testCopyAll testCopyAll(self, globals()) # ------------------------------------------------------------------------------ if __name__ == '__main__': import music21 music21.mainTest(Test)