From b7ae781a4d5ca9ac573cb280cf423880b97831cd Mon Sep 17 00:00:00 2001 From: Moritz Heffter Date: Mon, 19 Jun 2023 22:48:18 +0200 Subject: [PATCH 1/9] introduces FiguredBass object and makes some changes to Notation and Figures objects --- music21/figuredBass/notation.py | 142 +++++++++++++++++++++++++++++++- music21/harmony.py | 48 +++++++++++ 2 files changed, 186 insertions(+), 4 deletions(-) diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index 774e1236e1..e16ddcbd2c 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -30,6 +30,33 @@ (2,): (6, 4, 2), } +prefixes = ['+', '#', '++', '##'] +suffixes = ['\\'] + +modifiersDictXmlToM21 = {'sharp': '#', + 'flat': 'b', + 'natural': '\u266e', + 'double-sharp': '##', + 'flat-flat': 'bb', + 'backslash': '\\', + 'slash': '/', + 'cross': '+'} + +modifiersDictM21ToXml = {'#': 'sharp', + 'b': 'flat', + '##': 'double-sharp', + 'bb': 'flat-flat', + '\\': 'backslash', + '/': 'slash', + '+': 'sharp', + '\u266f': 'sharp', + '\u266e': 'natural', + '\u266d': 'flat', + '\u20e5': 'sharp', + '\u0338': 'slash', + '\U0001D12A': 'double-sharp', + '\U0001D12B': 'flat-flat', + } class Notation(prebase.ProtoM21Object): ''' @@ -79,6 +106,7 @@ class Notation(prebase.ProtoM21Object): * '13' -> '13,11,9,7,5,3' + * '_' -> treated as an extender Figures are saved in order from left to right as found in the notationColumn. @@ -139,6 +167,23 @@ class Notation(prebase.ProtoM21Object): > >>> n3.figures[1] > + >>> n3.extenders + [False, False] + >>> n3.hasExtenders + False + >>> n4 = notation.Notation('b6,\U0001D12B_,#') + >>> n4.figures + [>, + >, + >] + >>> n4.figuresFromNotationColumn + [>, + >, + >] + >>> n4.extenders + [False, True, False] + >>> n4.hasExtenders + True ''' _DOC_ORDER = ['notationColumn', 'figureStrings', 'numbers', 'modifiers', 'figures', 'origNumbers', 'origModStrings', 'modifierStrings'] @@ -189,12 +234,15 @@ def __init__(self, notationColumn=None): self.origModStrings = None self.numbers = None self.modifierStrings = None + self.extenders:list[bool] = [] + self.hasExtenders: bool = False self._parseNotationColumn() self._translateToLonghand() # Convert to convenient notation self.modifiers = None self.figures = None + self.figuresFromNotationColumn: list(Figure) = [] self._getModifiers() self._getFigures() @@ -224,11 +272,19 @@ def _parseNotationColumn(self): (6, None) >>> notation2.origModStrings ('-', '-') + + hasExtenders is set True if an underscore is parsed within a notation string + >>> notation2.hasExtenders + False + + >>> notation3 = n.Notation('7_') + >>> notation3.hasExtenders + True ''' delimiter = '[,]' figures = re.split(delimiter, self.notationColumn) - patternA1 = '([0-9]*)' - patternA2 = '([^0-9]*)' + patternA1 = '([0-9_]*)' + patternA2 = '([^0-9_]*)' numbers = [] modifierStrings = [] figureStrings = [] @@ -247,17 +303,40 @@ def _parseNotationColumn(self): number = None modifierString = None + extender = False if m1: - number = int(m1[0].strip()) + # if no number is there and only an extender is found. + if '_' in m1: + self.hasExtenders = True + number = '_' + extender = True + else: + # is an extender part of the number string? + if '_' in m1[0]: + self.hasExtenders = True + extender = True + number = int(m1[0].strip('_')) + else: + number = int(m1[0].strip()) if m2: modifierString = m2[0].strip() numbers.append(number) modifierStrings.append(modifierString) + self.extenders.append(extender) numbers = tuple(numbers) modifierStrings = tuple(modifierStrings) + # extenders come from the optional argument when instantionting the object. + # If nothing is provided, no extenders will be set. + # Otherwise we have to look if amount of extenders and figure numbers match + # + if not self.extenders: + self.extenders = [False for i in range(len(modifierStrings))] + else: + extenders = tuple(self.extenders) + self.origNumbers = numbers # Keep original numbers self.numbers = numbers # Will be converted to longhand self.origModStrings = modifierStrings # Keep original modifier strings @@ -366,11 +445,26 @@ def _getFigures(self): for i in range(len(self.numbers)): number = self.numbers[i] modifierString = self.modifierStrings[i] + if self.extenders: + if i < len(self.extenders): + extender = self.extenders[i] + else: + extender = False + figure = Figure(number, modifierString, extender) figure = Figure(number, modifierString) figures.append(figure) self.figures = figures + figuresFromNotaCol = [] + + for i, number in enumerate(self.origNumbers): + modifierString = self.origModStrings[i] + figure = Figure(number, modifierString) + figuresFromNotaCol.append(figure) + + self.figuresFromNotationColumn = figuresFromNotaCol + class NotationException(exceptions21.Music21Exception): pass @@ -395,6 +489,20 @@ class Figure(prebase.ProtoM21Object): '+' >>> f1.modifier + >>> f1.hasExtender + False + >>> f1.isExtender + False + >>> f2 = notation.Figure(6, '#', extender=True) + >>> f2.hasExtender + True + >>> f2.isExtender + False + >>> f3 = notation.Figure(extender=True) + >>> f3.isExtender + True + >>> f3.hasExtender + True ''' _DOC_ATTR: dict[str, str] = { 'number': ''' @@ -410,15 +518,34 @@ class Figure(prebase.ProtoM21Object): associated with an expanded :attr:`~music21.figuredBass.notation.Notation.notationColumn`. ''', + 'hasExtender': ''' + A bool value that indicates whether an extender is part of the figure. + It is set by a keyword argument. + ''', + 'isExtender': ''' + A bool value that returns true if an extender is part of the figure but no + number is given. Pure extender if you will. + It is set by evaluating the number and extender arguments. + ''' } - def __init__(self, number=1, modifierString=None): + isExtender: bool + + def __init__(self, number=1, modifierString=None, extender=False): self.number = number self.modifierString = modifierString self.modifier = Modifier(modifierString) + # look for extender's underscore + self.hasExtender: bool = extender + self._updateIsExtenderProperty() + + def _updateIsExtenderProperty(self): + self.isExtender = (self.number == 1 and self.hasExtender) def _reprInternal(self): mod = repr(self.modifier).replace('music21.figuredBass.notation.', '') + if self.isExtender or self.hasExtender: + return f'{self.number} {mod} extender: {True}' return f'{self.number} {mod}' @@ -433,6 +560,13 @@ def _reprInternal(self): '++': '##', '+++': '###', '++++': '####', + '\u266f': '#', + '\u266e': 'n', + '\u266d': 'b', + '\u20e5': '#', + '\u0338': '#', + '\U0001d12a': '##', + '\U0001d12b': '--' } diff --git a/music21/harmony.py b/music21/harmony.py index 73787a2355..3b78fc8701 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -31,6 +31,7 @@ from music21 import environment from music21 import exceptions21 from music21.figuredBass import realizerScale +from music21.figuredBass import notation from music21 import interval from music21 import key from music21 import pitch @@ -2499,6 +2500,53 @@ def transpose(self: NCT, _value, *, inPlace=False) -> NCT | None: # ------------------------------------------------------------------------------ +class FiguredBass(Harmony): + ''' + The FiguredBassIndication objects store information about thorough bass figures. + It is created as a representation for tags in MEI and tags in MusicXML. + The FiguredBassIndication object derives from the Harmony object and can be used + in the following way: + >>> fb = harmony.FiguredBass('#,6#', correspondingPart='P1') + >>> fb + + + The single figures are stored as figuredBass.notation.Figure objects: + >>> fb.figNotation.figures[0] + > + ''' + + isFigure: bool = True + corresPart: str | None = None + _figs: str = '' + + def __init__(self, figureString: str | list[str] | None = None, + correspondingPart: str | None=None, **keywords): + super().__init__(**keywords) + if figureString: + if isinstance(figureString, list): + self._figs = ','.join(figureString) + elif isinstance(figureString, str): + if ',' in figureString: + self._figs = figureString + else: + self._figs = ','.join(figureString) + else: + self._figs = '' + self._figNotation = notation.Notation(self._figs) + self.corresPart = correspondingPart + + @property + def figNotation(self) -> notation.Notation: + return self._figNotation + + @figNotation.setter + def figNotation(self, figs: str | list[str] | None): + self._figNotation = notation.Notation(figs) + + def __repr__(self): + return f'<{self.__class__.__name__} figures: {self.figNotation.notationColumn} correspondingPart: {self.corresPart}>' + +# ------------------------------------------------------------------------------ def realizeChordSymbolDurations(piece): ''' From 94f76b46b8fa7961f27c75d9f287b6bd0db9649f Mon Sep 17 00:00:00 2001 From: Moritz Heffter Date: Mon, 19 Jun 2023 23:07:38 +0200 Subject: [PATCH 2/9] indentation update --- music21/harmony.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/music21/harmony.py b/music21/harmony.py index 3b78fc8701..52eac4ac72 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -2515,13 +2515,17 @@ class FiguredBass(Harmony): > ''' - isFigure: bool = True - corresPart: str | None = None - _figs: str = '' - def __init__(self, figureString: str | list[str] | None = None, - correspondingPart: str | None=None, **keywords): + def __init__(self, + figureString: str | list[str] | None = None, + correspondingPart: str | None=None, + **keywords): super().__init__(**keywords) + + self.isFigure: bool = True + self.corresPart: str | None = None + self._figs: str = '' + if figureString: if isinstance(figureString, list): self._figs = ','.join(figureString) From d6ee7844692d816193434b01ac865a53d390f4a8 Mon Sep 17 00:00:00 2001 From: Moritz Heffter Date: Tue, 20 Jun 2023 18:58:03 +0200 Subject: [PATCH 3/9] corrections to notation PR #1614 --- music21/figuredBass/notation.py | 140 ++++++++++++++++++-------------- music21/harmony.py | 34 ++++---- 2 files changed, 98 insertions(+), 76 deletions(-) diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index e16ddcbd2c..f341d77100 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -33,30 +33,33 @@ prefixes = ['+', '#', '++', '##'] suffixes = ['\\'] -modifiersDictXmlToM21 = {'sharp': '#', - 'flat': 'b', - 'natural': '\u266e', - 'double-sharp': '##', - 'flat-flat': 'bb', - 'backslash': '\\', - 'slash': '/', - 'cross': '+'} - -modifiersDictM21ToXml = {'#': 'sharp', - 'b': 'flat', - '##': 'double-sharp', - 'bb': 'flat-flat', - '\\': 'backslash', - '/': 'slash', - '+': 'sharp', - '\u266f': 'sharp', - '\u266e': 'natural', - '\u266d': 'flat', - '\u20e5': 'sharp', - '\u0338': 'slash', - '\U0001D12A': 'double-sharp', - '\U0001D12B': 'flat-flat', - } +modifiersDictXmlToM21 = { + 'sharp': '#', + 'flat': 'b', + 'natural': '\u266e', + 'double-sharp': '##', + 'flat-flat': 'bb', + 'backslash': '\\', + 'slash': '/', + 'cross': '+' +} + +modifiersDictM21ToXml = { + '#': 'sharp', + 'b': 'flat', + '##': 'double-sharp', + 'bb': 'flat-flat', + '\\': 'backslash', + '/': 'slash', + '+': 'sharp', + '\u266f': 'sharp', + '\u266e': 'natural', + '\u266d': 'flat', + '\u20e5': 'sharp', + '\u0338': 'slash', + '\U0001D12A': 'double-sharp', + '\U0001D12B': 'flat-flat', +} class Notation(prebase.ProtoM21Object): ''' @@ -234,7 +237,7 @@ def __init__(self, notationColumn=None): self.origModStrings = None self.numbers = None self.modifierStrings = None - self.extenders:list[bool] = [] + self.extenders: list[bool] = [] self.hasExtenders: bool = False self._parseNotationColumn() self._translateToLonghand() @@ -242,7 +245,7 @@ def __init__(self, notationColumn=None): # Convert to convenient notation self.modifiers = None self.figures = None - self.figuresFromNotationColumn: list(Figure) = [] + self.figuresFromNotationColumn: list[Figure] = [] self._getModifiers() self._getFigures() @@ -328,10 +331,9 @@ def _parseNotationColumn(self): numbers = tuple(numbers) modifierStrings = tuple(modifierStrings) - # extenders come from the optional argument when instantionting the object. + # extenders come from the optional argument when instantiating the object. # If nothing is provided, no extenders will be set. # Otherwise we have to look if amount of extenders and figure numbers match - # if not self.extenders: self.extenders = [False for i in range(len(modifierStrings))] else: @@ -491,15 +493,15 @@ class Figure(prebase.ProtoM21Object): >>> f1.hasExtender False - >>> f1.isExtender + >>> f1.isPureExtender False >>> f2 = notation.Figure(6, '#', extender=True) >>> f2.hasExtender True - >>> f2.isExtender + >>> f2.isPureExtender False >>> f3 = notation.Figure(extender=True) - >>> f3.isExtender + >>> f3.isPureExtender True >>> f3.hasExtender True @@ -522,52 +524,66 @@ class Figure(prebase.ProtoM21Object): A bool value that indicates whether an extender is part of the figure. It is set by a keyword argument. ''', - 'isExtender': ''' - A bool value that returns true if an extender is part of the figure but no - number is given. Pure extender if you will. - It is set by evaluating the number and extender arguments. + 'isPureExtender': ''' + A bool value that returns true if an extender is part of the figure but no number + is given. It is set on the fly by evaluating the number and extender arguments. ''' } - isExtender: bool - - def __init__(self, number=1, modifierString=None, extender=False): + def __init__(self, number=1, modifierString=None, extender: bool = False): self.number = number self.modifierString = modifierString self.modifier = Modifier(modifierString) # look for extender's underscore self.hasExtender: bool = extender - self._updateIsExtenderProperty() - - def _updateIsExtenderProperty(self): - self.isExtender = (self.number == 1 and self.hasExtender) + + @property + def isPureExtender(self) -> bool: + ''' + Read-only boolean property that returns True if an extender is part of the figure + but no number is given (a number of 1 means no-number). It is a pure extender. + + >>> n = notation.Figure(1, '#', extender=True) + >>> n.isPureExtender + True + >>> n.number = 2 + >>> n.isPureExtender + False + ''' + + return self.number == 1 and self.hasExtender + def _reprInternal(self): + if self.isPureExtender: + return '
' mod = repr(self.modifier).replace('music21.figuredBass.notation.', '') - if self.isExtender or self.hasExtender: - return f'{self.number} {mod} extender: {True}' + ext = 'extender: __' if self.hasExtender else '' + if self.hasExtender: + return f'{self.number} {mod} {ext}' return f'{self.number} {mod}' # ------------------------------------------------------------------------------ -specialModifiers = {'+': '#', - '/': '-', - '\\': '#', - 'b': '-', - 'bb': '--', - 'bbb': '---', - 'bbbb': '-----', - '++': '##', - '+++': '###', - '++++': '####', - '\u266f': '#', - '\u266e': 'n', - '\u266d': 'b', - '\u20e5': '#', - '\u0338': '#', - '\U0001d12a': '##', - '\U0001d12b': '--' - } +specialModifiers = { + '+': '#', + '/': '-', + '\\': '#', + 'b': '-', + 'bb': '--', + 'bbb': '---', + 'bbbb': '-----', + '++': '##', + '+++': '###', + '++++': '####', + '\u266f': '#', + '\u266e': 'n', + '\u266d': 'b', + '\u20e5': '#', + '\u0338': '#', + '\U0001d12a': '##', + '\U0001d12b': '--' +} class Modifier(prebase.ProtoM21Object): diff --git a/music21/harmony.py b/music21/harmony.py index 52eac4ac72..9e53b8ed1f 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -2506,19 +2506,26 @@ class FiguredBass(Harmony): It is created as a representation for tags in MEI and tags in MusicXML. The FiguredBassIndication object derives from the Harmony object and can be used in the following way: - >>> fb = harmony.FiguredBass('#,6#', correspondingPart='P1') + + >>> fb = harmony.FiguredBass('#,6#', partRef='P1') >>> fb - + The single figures are stored as figuredBass.notation.Figure objects: >>> fb.figNotation.figures[0] > + >>> fb2 = harmony.FiguredBass(figureStrings=['#_','6#'], partRef='P2') + >>> fb2 + + >>> fb2.figNotation.hasExtenders + True ''' def __init__(self, - figureString: str | list[str] | None = None, - correspondingPart: str | None=None, + figureString: str = '', + figureStrings: list[str] = [], + partRef: str | None = None, **keywords): super().__init__(**keywords) @@ -2526,18 +2533,17 @@ def __init__(self, self.corresPart: str | None = None self._figs: str = '' - if figureString: - if isinstance(figureString, list): + if isinstance(figureString, str) and figureString != '': + if ',' in figureString: + self._figs = figureString + else: self._figs = ','.join(figureString) - elif isinstance(figureString, str): - if ',' in figureString: - self._figs = figureString - else: - self._figs = ','.join(figureString) + elif figureStrings != []: + self._figs = ','.join(figureStrings) else: self._figs = '' - self._figNotation = notation.Notation(self._figs) - self.corresPart = correspondingPart + self._figNotation: notation.Notation = notation.Notation(self._figs) + self.partRef = partRef @property def figNotation(self) -> notation.Notation: @@ -2548,7 +2554,7 @@ def figNotation(self, figs: str | list[str] | None): self._figNotation = notation.Notation(figs) def __repr__(self): - return f'<{self.__class__.__name__} figures: {self.figNotation.notationColumn} correspondingPart: {self.corresPart}>' + return f'<{self.__class__.__name__} figures: {self.figNotation.notationColumn} partRef: {self.partRef}>' # ------------------------------------------------------------------------------ From 7b099f2e617a50b5a29327593293b5208cf29247 Mon Sep 17 00:00:00 2001 From: Moritz Heffter Date: Wed, 21 Jun 2023 12:25:15 +0200 Subject: [PATCH 4/9] added requested changes to figuredbass and notation object --- music21/figuredBass/notation.py | 1 + music21/harmony.py | 85 ++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 23 deletions(-) diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index f341d77100..114fd23f87 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -543,6 +543,7 @@ def isPureExtender(self) -> bool: Read-only boolean property that returns True if an extender is part of the figure but no number is given (a number of 1 means no-number). It is a pure extender. + >>> from music21.figuredBass import notation >>> n = notation.Figure(1, '#', extender=True) >>> n.isPureExtender True diff --git a/music21/harmony.py b/music21/harmony.py index 9e53b8ed1f..60172f571d 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -2507,17 +2507,17 @@ class FiguredBass(Harmony): The FiguredBassIndication object derives from the Harmony object and can be used in the following way: - >>> fb = harmony.FiguredBass('#,6#', partRef='P1') + >>> fb = harmony.FiguredBass('#,6#') >>> fb - + The single figures are stored as figuredBass.notation.Figure objects: - >>> fb.figNotation.figures[0] + >>> fb.notation.figures[0] > - >>> fb2 = harmony.FiguredBass(figureStrings=['#_','6#'], partRef='P2') + >>> fb2 = harmony.FiguredBass(figureStrings=['#_','6#']) >>> fb2 - - >>> fb2.figNotation.hasExtenders + + >>> fb2.notation.hasExtenders True ''' @@ -2525,36 +2525,75 @@ class FiguredBass(Harmony): def __init__(self, figureString: str = '', figureStrings: list[str] = [], - partRef: str | None = None, **keywords): super().__init__(**keywords) - self.isFigure: bool = True - self.corresPart: str | None = None self._figs: str = '' - if isinstance(figureString, str) and figureString != '': - if ',' in figureString: - self._figs = figureString - else: - self._figs = ','.join(figureString) + if figureString != '': + self.figureString = figureString elif figureStrings != []: - self._figs = ','.join(figureStrings) + self.figureString = ','.join(figureStrings) else: - self._figs = '' + self.figureString = '' + self._figNotation: notation.Notation = notation.Notation(self._figs) - self.partRef = partRef @property - def figNotation(self) -> notation.Notation: + def notation(self) -> notation.Notation: return self._figNotation - @figNotation.setter - def figNotation(self, figs: str | list[str] | None): - self._figNotation = notation.Notation(figs) + @notation.setter + def notation(self, figureNotation: notation.Notation): + ''' + Sets the notation property of the FiguresBass object and updates the + figureString property if needed. + + >>> from music21 import harmony, figuredBass + >>> fb = harmony.FiguredBass('6,#') + >>> fb.figureString, fb.notation + ('6,#', ) + + >>> fb.notation = figuredBass.notation.Notation('7b,b') + >>> fb.figureString, fb.notation + ('7b,b', ) + ''' + + self._figNotation = figureNotation + if figureNotation.notationColumn != self._figs: + self.figureString = figureNotation.notationColumn - def __repr__(self): - return f'<{self.__class__.__name__} figures: {self.figNotation.notationColumn} partRef: {self.partRef}>' + @property + def figureString(self) -> str: + return self._figs + + @figureString.setter + def figureString(self, figureString: str): + ''' + Sets the figureString property of the FiguresBass object and updates the + notation property if needed. + + >>> from music21 import harmony + >>> fb = harmony.FiguredBass('6,#') + >>> fb.figureString, fb.notation + ('6,#', ) + + >>> fb.figureString = '5,b' + >>> fb.figureString, fb.notation + ('5,b', ) + ''' + + if isinstance(figureString, str) and figureString != '': + if ',' in figureString: + self._figs = figureString + else: + self._figs = ','.join(figureString) + + self.notation = notation.Notation(self._figs) + + + def _reprInternal(self): + return f'figures: {self.notation.notationColumn}' # ------------------------------------------------------------------------------ From c4609bfe68af5bc0a0e3c71f6d5be2ee5d4b4cdc Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sat, 1 Jul 2023 13:54:45 -1000 Subject: [PATCH 5/9] first commit for fixing issues in place. --- music21/figuredBass/notation.py | 92 ++++++++++++++------------------- music21/harmony.py | 31 +++++++---- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index 114fd23f87..3999a46ead 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -68,47 +68,26 @@ class Notation(prebase.ProtoM21Object): and an optional modifier. The figures are delimited using commas. Examples include '7,5,#3', '6,4', and '6,4+,2'. - Valid modifiers include those accepted by :class:`~music21.pitch.Accidental`, such as #, -, and n, as well as those which can correspond to one, such as +, /, and b. - .. note:: If a figure has a modifier but no number, the number is assumed to be 3. - Notation also translates many forms of shorthand notation into longhand. It understands all the forms of shorthand notation listed below. This is true even if a number is accompanied by a modifier, or if a stand-alone modifier implies a 3. - * None, '' or '5' -> '5,3' - - * '6' -> '6,3' - - * '7' -> '7,5,3' - - * '6,5' -> '6,5,3' - - * '4,3' -> '6,4,3' - - * '4,2' or '2' -> '6,4,2' - - * '9' -> '9,7,5,3' - - * '11' -> '11,9,7,5,3' - - * '13' -> '13,11,9,7,5,3' - * '_' -> treated as an extender Figures are saved in order from left to right as found in the notationColumn. @@ -141,10 +120,8 @@ class Notation(prebase.ProtoM21Object): >>> n1.figures[2] > - Here, a stand-alone '#' is being passed to Notation. - >>> n2 = notation.Notation('#') >>> n2.numbers (5, 3) @@ -156,10 +133,8 @@ class Notation(prebase.ProtoM21Object): >>> n2.figures[1] > - Now, a stand-alone b is being passed to Notation as part of a larger notationColumn. - >>> n3 = notation.Notation('b6,b') >>> n3.numbers (6, 3) @@ -174,10 +149,13 @@ class Notation(prebase.ProtoM21Object): [False, False] >>> n3.hasExtenders False - >>> n4 = notation.Notation('b6,\U0001D12B_,#') + + Here we will use the unicode symbol for double-flat for the extender: + + >>> n4 = notation.Notation('b6, \U0001D12B_, #') >>> n4.figures [>, - >, + >, >] >>> n4.figuresFromNotationColumn [>, @@ -227,10 +205,8 @@ class Notation(prebase.ProtoM21Object): ''', } - def __init__(self, notationColumn=None): + def __init__(self, notationColumn: str = '') -> None: # Parse notation string - if notationColumn is None: - notationColumn = '' self.notationColumn = notationColumn self.figureStrings = None self.origNumbers = None @@ -259,16 +235,32 @@ def _parseNotationColumn(self): bass and (if necessary) how to modify the corresponding pitches accordingly. + `_parseNotationColumn` is called from `__init__` and thus + is not explicitly demonstrated below. >>> from music21.figuredBass import notation as n - >>> notation1 = n.Notation('#6,5') #__init__ method calls _parseNotationColumn() + >>> notation1 = n.Notation('#6, 5') + + The figureStrings are left alone: + >>> notation1.figureStrings ['#6', '5'] + + And in this case the original numbers (`origNumbers`) are + the same: + >>> notation1.origNumbers (6, 5) + + Since 6 has a sharp on it, it has something in the original modifier + strings (`origModStrings`) + >>> notation1.origModStrings ('#', None) - >>> notation2 = n.Notation('-6,-') + + A second example of flat 6 and flat 3: + + >>> notation2 = n.Notation('-6, -') >>> notation2.figureStrings ['-6', '-'] >>> notation2.origNumbers @@ -276,13 +268,17 @@ def _parseNotationColumn(self): >>> notation2.origModStrings ('-', '-') - hasExtenders is set True if an underscore is parsed within a notation string - >>> notation2.hasExtenders - False + + An example of a seventh chord with extender: >>> notation3 = n.Notation('7_') + + `hasExtenders` is set True if an underscore is parsed within a notation string + >>> notation3.hasExtenders True + >>> notation2.hasExtenders + False ''' delimiter = '[,]' figures = re.split(delimiter, self.notationColumn) @@ -333,11 +329,9 @@ def _parseNotationColumn(self): # extenders come from the optional argument when instantiating the object. # If nothing is provided, no extenders will be set. - # Otherwise we have to look if amount of extenders and figure numbers match + # Otherwise, we have to look if amount of extenders and figure numbers match if not self.extenders: self.extenders = [False for i in range(len(modifierStrings))] - else: - extenders = tuple(self.extenders) self.origNumbers = numbers # Keep original numbers self.numbers = numbers # Will be converted to longhand @@ -350,7 +344,6 @@ def _translateToLonghand(self): Provided the numbers and modifierStrings of a parsed notation column, translates it to longhand. - >>> from music21.figuredBass import notation as n >>> notation1 = n.Notation('#6,5') # __init__ method calls _parseNotationColumn() >>> str(notation1.origNumbers) + ' -> ' + str(notation1.numbers) @@ -447,13 +440,10 @@ def _getFigures(self): for i in range(len(self.numbers)): number = self.numbers[i] modifierString = self.modifierStrings[i] - if self.extenders: - if i < len(self.extenders): - extender = self.extenders[i] - else: - extender = False - figure = Figure(number, modifierString, extender) - figure = Figure(number, modifierString) + extender = False + if self.extenders and i < len(self.extenders): + extender = self.extenders[i] + figure = Figure(number, modifierString, extender=extender) figures.append(figure) self.figures = figures @@ -530,7 +520,7 @@ class Figure(prebase.ProtoM21Object): ''' } - def __init__(self, number=1, modifierString=None, extender: bool = False): + def __init__(self, number=1, modifierString: str = '', *, extender: bool = False): self.number = number self.modifierString = modifierString self.modifier = Modifier(modifierString) @@ -551,18 +541,14 @@ def isPureExtender(self) -> bool: >>> n.isPureExtender False ''' - return self.number == 1 and self.hasExtender - def _reprInternal(self): if self.isPureExtender: return '
' mod = repr(self.modifier).replace('music21.figuredBass.notation.', '') - ext = 'extender: __' if self.hasExtender else '' - if self.hasExtender: - return f'{self.number} {mod} {ext}' - return f'{self.number} {mod}' + ext = '(extender)' if self.hasExtender else '' + return f'{self.number}{ext} {mod}' # ------------------------------------------------------------------------------ diff --git a/music21/harmony.py b/music21/harmony.py index 60172f571d..466c130ee5 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -1936,7 +1936,7 @@ def _parseFigure(self) -> None: kindStr. ''' if self.figure == 'Chord Symbol Cannot Be Identified': - return self.figure + return # remove spaces from prelim Figure... prelimFigure = self.figure prelimFigure = re.sub(r'\s', '', prelimFigure) @@ -2502,29 +2502,42 @@ def transpose(self: NCT, _value, *, inPlace=False) -> NCT | None: class FiguredBass(Harmony): ''' - The FiguredBassIndication objects store information about thorough bass figures. + *BETA*: FiguredBass objects are currently in beta and may change without warning. + + The FiguredBass objects store information about thorough bass figures. It is created as a representation for tags in MEI and tags in MusicXML. The FiguredBassIndication object derives from the Harmony object and can be used in the following way: >>> fb = harmony.FiguredBass('#,6#') >>> fb - + The single figures are stored as figuredBass.notation.Figure objects: + >>> fb.notation.figures[0] > - >>> fb2 = harmony.FiguredBass(figureStrings=['#_','6#']) + + The figures can be accessed and manipulated individually by passing in `figureStrings` + (plural), and extenders are allowed as with `_`: + + >>> fb2 = harmony.FiguredBass(figureStrings=['#_', '6#']) >>> fb2 - + >>> fb2.notation.hasExtenders True - ''' + Currently, figured bass objects do not have associated pitches. This will change. + + >>> fb.pitches + () + * new in v9.3 + ''' def __init__(self, figureString: str = '', - figureStrings: list[str] = [], + *, + figureStrings: Iterable[str] = (), **keywords): super().__init__(**keywords) @@ -2532,7 +2545,7 @@ def __init__(self, if figureString != '': self.figureString = figureString - elif figureStrings != []: + elif figureStrings: self.figureString = ','.join(figureStrings) else: self.figureString = '' @@ -2593,7 +2606,7 @@ def figureString(self, figureString: str): def _reprInternal(self): - return f'figures: {self.notation.notationColumn}' + return self.notation.notationColumn # ------------------------------------------------------------------------------ From c847113352c1d803851c58d6ffcd3b08b9bef5ec Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sat, 1 Jul 2023 14:10:56 -1000 Subject: [PATCH 6/9] move FiguredBass object to figuredBass.FiguredBass --- music21/figuredBass/__init__.py | 5 ++ music21/figuredBass/harmony.py | 140 ++++++++++++++++++++++++++++++++ music21/harmony.py | 117 ++------------------------ 3 files changed, 150 insertions(+), 112 deletions(-) create mode 100644 music21/figuredBass/harmony.py diff --git a/music21/figuredBass/__init__.py b/music21/figuredBass/__init__.py index 9bf3682a4e..5439798c73 100644 --- a/music21/figuredBass/__init__.py +++ b/music21/figuredBass/__init__.py @@ -4,6 +4,7 @@ __all__ = [ 'checker', 'examples', + 'harmony', 'notation', 'possibility', 'realizer', @@ -11,10 +12,14 @@ 'resolution', 'rules', 'segment', + + 'FiguredBass', ] from music21.figuredBass import checker from music21.figuredBass import examples +from music21.figuredBass import harmony +from music21.figuredBass.harmony import FiguredBass from music21.figuredBass import notation from music21.figuredBass import possibility from music21.figuredBass import realizer diff --git a/music21/figuredBass/harmony.py b/music21/figuredBass/harmony.py new file mode 100644 index 0000000000..113725aa28 --- /dev/null +++ b/music21/figuredBass/harmony.py @@ -0,0 +1,140 @@ +# ------------------------------------------------------------------------------ +# Name: figuredBass.harmony.py +# Purpose: Music21Object for FiguredBass as a Harmony subclass +# +# Authors: Moritz Heffter +# Michael Scott Asato Cuthbert +# +# Copyright: Copyright © 2011-2023 Michael Scott Asato Cuthbert +# License: BSD, see license.txt +# ------------------------------------------------------------------------------ +''' +The FiguredBass object is a subclass of Harmony that will (eventually) be able +to represent figured bass symbols in music notation and also realize it. + +BETA at this point. + +Based on work by Jose Cabal-Ugaz. +''' +from __future__ import annotations + +from music21.harmony import Harmony + +from music21.figuredBass import notation + + +class FiguredBass(Harmony): + ''' + *BETA*: FiguredBass objects are currently in beta and may change without warning. + + The FiguredBass objects store information about thorough bass figures. + It is created as a representation for tags in MEI and tags in MusicXML. + The FiguredBass object derives from the Harmony object and can be used + in the following way: + + >>> fb = figuredBass.FiguredBass('#,6#') + >>> fb + + + (note that the FiguredBass object can be found in either music21.figuredBass.FiguredBass + or music21.figuredBass.harmony.FiguredBass. It is the same class) + + The single figures are stored as figuredBass.notation.Figure objects: + + >>> fb.notation.figures[0] + > + + The figures can be accessed and manipulated individually by passing in `figureStrings` + (plural), and extenders are allowed as with `_`: + + >>> fb2 = figuredBass.FiguredBass(figureStrings=['#_', '6#']) + >>> fb2 + + >>> fb2.notation.hasExtenders + True + + Currently, figured bass objects do not have associated pitches. This will change. + + >>> fb.pitches + () + + * new in v9.3 + ''' + def __init__(self, + figureString: str = '', + *, + figureStrings: Iterable[str] = (), + **keywords): + super().__init__(**keywords) + + self._figs: str = '' + + if figureString != '': + self.figureString = figureString + elif figureStrings: + self.figureString = ','.join(figureStrings) + else: + self.figureString = '' + + self._figNotation: notation.Notation = notation.Notation(self._figs) + + @property + def notation(self) -> notation.Notation: + return self._figNotation + + @notation.setter + def notation(self, figureNotation: notation.Notation): + ''' + Sets the notation property of the FiguresBass object and updates the + figureString property if needed. + + >>> fb = figuredBass.FiguredBass('6,#') + >>> fb.figureString, fb.notation + ('6,#', ) + + >>> fb.notation = figuredBass.notation.Notation('7b,b') + >>> fb.figureString, fb.notation + ('7b,b', ) + ''' + + self._figNotation = figureNotation + if figureNotation.notationColumn != self._figs: + self.figureString = figureNotation.notationColumn + + @property + def figureString(self) -> str: + return self._figs + + @figureString.setter + def figureString(self, figureString: str): + ''' + Sets the figureString property of the FiguresBass object and updates the + notation property if needed. + + >>> fb = figuredBass.FiguredBass('6,#') + >>> fb.figureString, fb.notation + ('6,#', ) + + >>> fb.figureString = '5,b' + >>> fb.figureString, fb.notation + ('5,b', ) + ''' + + if isinstance(figureString, str) and figureString != '': + if ',' in figureString: + self._figs = figureString + else: + self._figs = ','.join(figureString) + + self.notation = notation.Notation(self._figs) + + + def _reprInternal(self): + return self.notation.notationColumn + + +# ------------------------------------------------------------------------------ +if __name__ == '__main__': + import music21 + music21.mainTest() + diff --git a/music21/harmony.py b/music21/harmony.py index 466c130ee5..581ae7f233 100644 --- a/music21/harmony.py +++ b/music21/harmony.py @@ -30,8 +30,6 @@ from music21 import duration from music21 import environment from music21 import exceptions21 -from music21.figuredBass import realizerScale -from music21.figuredBass import notation from music21 import interval from music21 import key from music21 import pitch @@ -43,6 +41,9 @@ T = t.TypeVar('T', bound='ChordSymbol') NCT = t.TypeVar('NCT', bound='NoChord') +if t.TYPE_CHECKING: + from music21.figuredBass import realizerScale + # -------------------------------------------------------------------------- @@ -2111,6 +2112,8 @@ def _updatePitches(self): if 'root' not in self._overrides or 'bass' not in self._overrides or self.chordKind is None: return + from music21.figuredBass import realizerScale + # create figured bass scale with root as scale scaleInitTuple = (self._overrides['root'].name, 'major') if scaleInitTuple in realizerScaleCache: @@ -2500,116 +2503,6 @@ def transpose(self: NCT, _value, *, inPlace=False) -> NCT | None: # ------------------------------------------------------------------------------ -class FiguredBass(Harmony): - ''' - *BETA*: FiguredBass objects are currently in beta and may change without warning. - - The FiguredBass objects store information about thorough bass figures. - It is created as a representation for tags in MEI and tags in MusicXML. - The FiguredBassIndication object derives from the Harmony object and can be used - in the following way: - - >>> fb = harmony.FiguredBass('#,6#') - >>> fb - - - The single figures are stored as figuredBass.notation.Figure objects: - - >>> fb.notation.figures[0] - > - - The figures can be accessed and manipulated individually by passing in `figureStrings` - (plural), and extenders are allowed as with `_`: - - >>> fb2 = harmony.FiguredBass(figureStrings=['#_', '6#']) - >>> fb2 - - >>> fb2.notation.hasExtenders - True - - Currently, figured bass objects do not have associated pitches. This will change. - - >>> fb.pitches - () - - * new in v9.3 - ''' - def __init__(self, - figureString: str = '', - *, - figureStrings: Iterable[str] = (), - **keywords): - super().__init__(**keywords) - - self._figs: str = '' - - if figureString != '': - self.figureString = figureString - elif figureStrings: - self.figureString = ','.join(figureStrings) - else: - self.figureString = '' - - self._figNotation: notation.Notation = notation.Notation(self._figs) - - @property - def notation(self) -> notation.Notation: - return self._figNotation - - @notation.setter - def notation(self, figureNotation: notation.Notation): - ''' - Sets the notation property of the FiguresBass object and updates the - figureString property if needed. - - >>> from music21 import harmony, figuredBass - >>> fb = harmony.FiguredBass('6,#') - >>> fb.figureString, fb.notation - ('6,#', ) - - >>> fb.notation = figuredBass.notation.Notation('7b,b') - >>> fb.figureString, fb.notation - ('7b,b', ) - ''' - - self._figNotation = figureNotation - if figureNotation.notationColumn != self._figs: - self.figureString = figureNotation.notationColumn - - @property - def figureString(self) -> str: - return self._figs - - @figureString.setter - def figureString(self, figureString: str): - ''' - Sets the figureString property of the FiguresBass object and updates the - notation property if needed. - - >>> from music21 import harmony - >>> fb = harmony.FiguredBass('6,#') - >>> fb.figureString, fb.notation - ('6,#', ) - - >>> fb.figureString = '5,b' - >>> fb.figureString, fb.notation - ('5,b', ) - ''' - - if isinstance(figureString, str) and figureString != '': - if ',' in figureString: - self._figs = figureString - else: - self._figs = ','.join(figureString) - - self.notation = notation.Notation(self._figs) - - - def _reprInternal(self): - return self.notation.notationColumn - -# ------------------------------------------------------------------------------ - def realizeChordSymbolDurations(piece): ''' Returns music21 stream with duration attribute of chord symbols correctly From 70f9f2c04bb2ca9c4676a609a360849004b12a62 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sat, 1 Jul 2023 14:30:40 -1000 Subject: [PATCH 7/9] lint and mypy --- music21/figuredBass/harmony.py | 2 ++ music21/figuredBass/notation.py | 43 +++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/music21/figuredBass/harmony.py b/music21/figuredBass/harmony.py index 113725aa28..fa5ea61b44 100644 --- a/music21/figuredBass/harmony.py +++ b/music21/figuredBass/harmony.py @@ -18,6 +18,8 @@ ''' from __future__ import annotations +from collections.abc import Iterable + from music21.harmony import Harmony from music21.figuredBass import notation diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index 3999a46ead..13738a8acf 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -170,7 +170,7 @@ class Notation(prebase.ProtoM21Object): 'figures', 'origNumbers', 'origModStrings', 'modifierStrings'] _DOC_ATTR: dict[str, str] = { 'modifiers': ''' - A list of :class:`~music21.figuredBass.notation.Modifier` + A tuple of :class:`~music21.figuredBass.notation.Modifier` objects associated with the expanded :attr:`~music21.figuredBass.notation.Notation.notationColumn`. ''', @@ -207,20 +207,20 @@ class Notation(prebase.ProtoM21Object): def __init__(self, notationColumn: str = '') -> None: # Parse notation string - self.notationColumn = notationColumn - self.figureStrings = None - self.origNumbers = None - self.origModStrings = None - self.numbers = None - self.modifierStrings = None + self.notationColumn: str = notationColumn or '' + self.figureStrings: list[str] = [] + self.origNumbers: tuple[int | None, ...] = () + self.origModStrings: tuple[str | None, ...] = () + self.numbers: list[int] = [] + self.modifierStrings: tuple[str | None, ...] = () self.extenders: list[bool] = [] self.hasExtenders: bool = False self._parseNotationColumn() self._translateToLonghand() # Convert to convenient notation - self.modifiers = None - self.figures = None + self.modifiers: tuple[Modifier, ...] = () + self.figures: list[Figure] = [] self.figuresFromNotationColumn: list[Figure] = [] self._getModifiers() self._getFigures() @@ -403,9 +403,10 @@ def _getModifiers(self): A modifier object keeps track of both the modifier string and its corresponding pitch Accidental. + The `__init__` method calls `_getModifiers()` so it is not called below. >>> from music21.figuredBass import notation as n - >>> notation1 = n.Notation('#4,2+') #__init__ method calls _getModifiers() + >>> notation1 = n.Notation('#4,2+') >>> notation1.modifiers[0] >>> notation1.modifiers[1] @@ -422,7 +423,7 @@ def _getModifiers(self): self.modifiers = tuple(modifiers) - def _getFigures(self): + def _getFigures(self) -> None: ''' Turns the numbers and Modifier objects into Figure objects, each corresponding to a number with its Modifier. @@ -435,7 +436,7 @@ def _getFigures(self): >>> notation2.figures[1] > ''' - figures = [] + figures: list[Figure] = [] for i in range(len(self.numbers)): number = self.numbers[i] @@ -450,9 +451,9 @@ def _getFigures(self): figuresFromNotaCol = [] - for i, number in enumerate(self.origNumbers): + for i, origNumber in enumerate(self.origNumbers): modifierString = self.origModStrings[i] - figure = Figure(number, modifierString) + figure = Figure(origNumber, modifierString) figuresFromNotaCol.append(figure) self.figuresFromNotationColumn = figuresFromNotaCol @@ -520,10 +521,16 @@ class Figure(prebase.ProtoM21Object): ''' } - def __init__(self, number=1, modifierString: str = '', *, extender: bool = False): - self.number = number - self.modifierString = modifierString - self.modifier = Modifier(modifierString) + def __init__( + self, + number: int | None = 1, + modifierString: str | None = '', + *, + extender: bool = False + ): + self.number: int | None = number + self.modifierString: str | None = modifierString + self.modifier: Modifier = Modifier(modifierString) # look for extender's underscore self.hasExtender: bool = extender From f232f9d91a5373471f275c2384aaf9a1a7d42793 Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sat, 1 Jul 2023 15:03:56 -1000 Subject: [PATCH 8/9] Cover uncovered test lines. https://coveralls.io/builds/60910555 Check for what is not being covered by tests when adding new features please. --- music21/figuredBass/harmony.py | 80 +++++++++++++++++++++------------ music21/figuredBass/notation.py | 20 +++++---- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/music21/figuredBass/harmony.py b/music21/figuredBass/harmony.py index fa5ea61b44..34ae67d7f2 100644 --- a/music21/figuredBass/harmony.py +++ b/music21/figuredBass/harmony.py @@ -75,61 +75,83 @@ def __init__(self, self.figureString = figureString elif figureStrings: self.figureString = ','.join(figureStrings) - else: - self.figureString = '' self._figNotation: notation.Notation = notation.Notation(self._figs) @property def notation(self) -> notation.Notation: - return self._figNotation - - @notation.setter - def notation(self, figureNotation: notation.Notation): ''' - Sets the notation property of the FiguresBass object and updates the - figureString property if needed. + Gets or sets the notation property of the FiguresBass object >>> fb = figuredBass.FiguredBass('6,#') - >>> fb.figureString, fb.notation - ('6,#', ) + >>> fb.figureString + '6,#' + >>> fb.notation + + + When setting, this updates the figureString property as well. >>> fb.notation = figuredBass.notation.Notation('7b,b') - >>> fb.figureString, fb.notation - ('7b,b', ) + >>> fb.notation + + >>> fb.figureString + '7b,b' ''' + return self._figNotation + @notation.setter + def notation(self, figureNotation: notation.Notation): self._figNotation = figureNotation - if figureNotation.notationColumn != self._figs: - self.figureString = figureNotation.notationColumn + self._figs = figureNotation.notationColumn @property def figureString(self) -> str: + ''' + Get the figures as strings of the FiguresBass object. + + >>> fb = figuredBass.FiguredBass('6,#') + >>> fb.figureString + '6,#' + >>> fb.notation + + + When setting the figureString the notation property is updated as well: + + >>> fb.figureString = '5,b' + >>> fb.figureString + '5,b' + >>> fb.notation + + ''' return self._figs @figureString.setter def figureString(self, figureString: str): + self._figs = figureString + self.notation = notation.Notation(self._figs) + + @property + def figureStrings(self) -> list[str]: ''' - Sets the figureString property of the FiguresBass object and updates the - notation property if needed. + Does the same as figureStrings but returns a list: >>> fb = figuredBass.FiguredBass('6,#') - >>> fb.figureString, fb.notation - ('6,#', ) + >>> fb.figureStrings + ['6', '#'] - >>> fb.figureString = '5,b' - >>> fb.figureString, fb.notation - ('5,b', ) - ''' + Like figureString, figureStrings can be set as well and updates the notation property: - if isinstance(figureString, str) and figureString != '': - if ',' in figureString: - self._figs = figureString - else: - self._figs = ','.join(figureString) - - self.notation = notation.Notation(self._figs) + >>> fb.figureStrings = ['5', 'b'] + >>> fb.figureStrings + ['5', 'b'] + >>> fb.notation + + ''' + return self._figs.split(',') + @figureStrings.setter + def figureStrings(self, figureStrings: Iterable[str]) -> None: + self._figs = ','.join(figureStrings) def _reprInternal(self): return self.notation.notationColumn diff --git a/music21/figuredBass/notation.py b/music21/figuredBass/notation.py index 13738a8acf..51fdb43dfd 100644 --- a/music21/figuredBass/notation.py +++ b/music21/figuredBass/notation.py @@ -327,12 +327,6 @@ def _parseNotationColumn(self): numbers = tuple(numbers) modifierStrings = tuple(modifierStrings) - # extenders come from the optional argument when instantiating the object. - # If nothing is provided, no extenders will be set. - # Otherwise, we have to look if amount of extenders and figure numbers match - if not self.extenders: - self.extenders = [False for i in range(len(modifierStrings))] - self.origNumbers = numbers # Keep original numbers self.numbers = numbers # Will be converted to longhand self.origModStrings = modifierStrings # Keep original modifier strings @@ -544,18 +538,26 @@ def isPureExtender(self) -> bool: >>> n = notation.Figure(1, '#', extender=True) >>> n.isPureExtender True + >>> n + > + >>> n.number = 2 >>> n.isPureExtender False + >>> n + > ''' return self.number == 1 and self.hasExtender def _reprInternal(self): if self.isPureExtender: - return '
' + num = 'pure-extender' + ext = '' + else: + num = str(self.number) + ext = '(extender)' if self.hasExtender else '' mod = repr(self.modifier).replace('music21.figuredBass.notation.', '') - ext = '(extender)' if self.hasExtender else '' - return f'{self.number}{ext} {mod}' + return f'{num}{ext} {mod}' # ------------------------------------------------------------------------------ From 88b8ed97df68d9b06d37405d2f88b98805b3d91f Mon Sep 17 00:00:00 2001 From: Michael Scott Asato Cuthbert Date: Sat, 1 Jul 2023 15:20:29 -1000 Subject: [PATCH 9/9] reuse your setters, Myke! --- music21/figuredBass/harmony.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/music21/figuredBass/harmony.py b/music21/figuredBass/harmony.py index 34ae67d7f2..aa456d3cd7 100644 --- a/music21/figuredBass/harmony.py +++ b/music21/figuredBass/harmony.py @@ -147,11 +147,11 @@ def figureStrings(self) -> list[str]: >>> fb.notation ''' - return self._figs.split(',') + return self.figureString.split(',') @figureStrings.setter def figureStrings(self, figureStrings: Iterable[str]) -> None: - self._figs = ','.join(figureStrings) + self.figureString = ','.join(figureStrings) def _reprInternal(self): return self.notation.notationColumn