Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 145 additions & 88 deletions music21/figuredBass/checker.py

Large diffs are not rendered by default.

32 changes: 21 additions & 11 deletions music21/figuredBass/examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,19 @@
from __future__ import annotations

import copy
import typing as t
import unittest

from music21.figuredBass import realizer
from music21.figuredBass import rules

if t.TYPE_CHECKING:
from music21 import stream

# ------------------------------------------------------------------------------


def exampleA():
def exampleA() -> realizer.FiguredBassLine:
'''
This example was a homework assignment for 21M.302: Harmony & Counterpoint II
at MIT in the fall of 2010, taught by Charles Shadle of the MIT Music Program.
Expand Down Expand Up @@ -76,7 +80,7 @@ def exampleA():
return realizer.figuredBassFromStream(s)


def exampleD():
def exampleD() -> realizer.FiguredBassLine:
'''
This example was a homework assignment for 21M.302: Harmony & Counterpoint II
at MIT in the fall of 2010, taught by Charles Shadle of the MIT Music Program.
Expand Down Expand Up @@ -139,7 +143,7 @@ def exampleD():
return realizer.figuredBassFromStream(s)


def exampleB():
def exampleB() -> realizer.FiguredBassLine:
'''
This example was retrieved from page 114 of *The Music Theory Handbook* by Marjorie Merryman.

Expand Down Expand Up @@ -182,7 +186,7 @@ def exampleB():
return realizer.figuredBassFromStream(s)


def exampleC():
def exampleC() -> realizer.FiguredBassLine:
'''
This example was retrieved from page 114 of *The Music Theory Handbook* by Marjorie Merryman.

Expand Down Expand Up @@ -227,7 +231,7 @@ def exampleC():
return realizer.figuredBassFromStream(s)


def V43ResolutionExample():
def V43ResolutionExample() -> realizer.FiguredBassLine:
'''
The dominant 4,3 can resolve to either the tonic 5,3 or tonic 6,3. The proper resolution
is dependent on the bass note of the tonic, and is determined in context, as shown in the
Expand All @@ -248,7 +252,7 @@ def V43ResolutionExample():
return realizer.figuredBassFromStream(s)


def viio65ResolutionExample():
def viio65ResolutionExample() -> realizer.FiguredBassLine:
'''
For a fully diminished seventh chord resolving to the tonic, the resolution chord
can contain either a doubled third (standard resolution) or a doubled tonic (alternate
Expand Down Expand Up @@ -278,7 +282,7 @@ def viio65ResolutionExample():
return realizer.figuredBassFromStream(s)


def augmentedSixthResolutionExample():
def augmentedSixthResolutionExample() -> realizer.FiguredBassLine:
'''
This example was retrieved from page 61 of *The Music Theory Handbook* by Marjorie Merryman.

Expand Down Expand Up @@ -308,7 +312,7 @@ def augmentedSixthResolutionExample():
return realizer.figuredBassFromStream(s)


def italianA6ResolutionExample():
def italianA6ResolutionExample() -> realizer.FiguredBassLine:
'''
The Italian augmented sixth chord (It+6) is the only
augmented sixth chord to consist of only three
Expand Down Expand Up @@ -353,7 +357,7 @@ def italianA6ResolutionExample():
return realizer.figuredBassFromStream(s)


def twelveBarBlues():
def twelveBarBlues() -> realizer.FiguredBassLine:
'''
This is a progression in Bb major based on the twelve bar blues. The progression used is:

Expand Down Expand Up @@ -392,7 +396,10 @@ def twelveBarBlues():
# Functions that generate Boogie/Blues vamps.


def generateBoogieVamp(blRealization=None, numRepeats=5):
def generateBoogieVamp(
blRealization: realizer.Realization | None = None,
numRepeats: int = 5,
) -> stream.Score:
'''
Turns whole notes in twelve bar blues bass line to blues boogie woogie bass line. Takes
in numRepeats, which is the number of times to repeat the bass line. Also, takes in a
Expand Down Expand Up @@ -440,7 +447,10 @@ def generateBoogieVamp(blRealization=None, numRepeats=5):
return newScore


def generateTripletBlues(blRealization=None, numRepeats=5): # 12/8
def generateTripletBlues(
blRealization: realizer.Realization | None = None,
numRepeats: int = 5,
) -> stream.Score: # 12/8
'''
Turns whole notes in twelve bar blues bass line to triplet blues bass line. Takes
in numRepeats, which is the number of times to repeat the bass line. Also, takes in a
Expand Down
8 changes: 4 additions & 4 deletions music21/figuredBass/harmony.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __init__(self,
figureString: str = '',
*,
figureStrings: Iterable[str] = (),
**keywords):
**keywords) -> None:
super().__init__(**keywords)

self._figs: str = ''
Expand Down Expand Up @@ -100,7 +100,7 @@ def notation(self) -> notation.Notation:
return self._figNotation

@notation.setter
def notation(self, figureNotation: notation.Notation):
def notation(self, figureNotation: notation.Notation) -> None:
self._figNotation = figureNotation
self._figs = figureNotation.notationColumn

Expand All @@ -126,7 +126,7 @@ def figureString(self) -> str:
return self._figs

@figureString.setter
def figureString(self, figureString: str):
def figureString(self, figureString: str) -> None:
self._figs = figureString
self.notation = notation.Notation(self._figs)

Expand All @@ -153,7 +153,7 @@ def figureStrings(self) -> list[str]:
def figureStrings(self, figureStrings: Iterable[str]) -> None:
self.figureString = ','.join(figureStrings)

def _reprInternal(self):
def _reprInternal(self) -> str:
return self.notation.notationColumn


Expand Down
112 changes: 54 additions & 58 deletions music21/figuredBass/notation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@

import copy
import re
import typing as t
import unittest

from music21 import exceptions21
from music21 import pitch
from music21 import prebase

shorthandNotation = {(None,): (5, 3),
shorthandNotation: dict[tuple[int|None, ...], tuple[int, ...]] = {
(None,): (5, 3),
(5,): (5, 3),
(6,): (6, 3),
(7,): (7, 5, 3),
Expand Down Expand Up @@ -208,9 +210,9 @@ def __init__(self, notationColumn: str = '') -> None:
# Parse notation string
self.notationColumn: str = notationColumn or ''
self.figureStrings: list[str] = []
self.origNumbers: tuple[int|None, ...] = ()
self.origNumbers: tuple[int|str|None, ...] = ()
self.origModStrings: tuple[str|None, ...] = ()
self.numbers: list[int] = []
self.numbers: tuple[int|str|None, ...] = ()
self.modifierStrings: tuple[str|None, ...] = ()
self.extenders: list[bool] = []
self.hasExtenders: bool = False
Expand All @@ -224,10 +226,10 @@ def __init__(self, notationColumn: str = '') -> None:
self._getModifiers()
self._getFigures()

def _reprInternal(self):
def _reprInternal(self) -> str:
return str(self.notationColumn)

def _parseNotationColumn(self):
def _parseNotationColumn(self) -> None:
'''
Given a notation column below a pitch, defines both self.numbers
and self.modifierStrings, which provide the intervals above the
Expand Down Expand Up @@ -282,9 +284,9 @@ def _parseNotationColumn(self):
figures = re.split(delimiter, self.notationColumn)
patternA1 = '([0-9_]*)'
patternA2 = '([^0-9_]*)'
numbers = []
modifierStrings = []
figureStrings = []
numbers: list[int|str|None] = []
modifierStrings: list[str|None] = []
figureStrings: list[str] = []

for figure in figures:
figure = figure.strip()
Expand All @@ -298,8 +300,8 @@ def _parseNotationColumn(self):
if not (len(m1) <= 1 or len(m2) <= 1):
raise NotationException('Invalid Notation: ' + figure)

number = None
modifierString = None
number: int|str|None = None
modifierString: str|None = None
extender = False
if m1:
# if no number is there and only an extender is found.
Expand All @@ -322,16 +324,16 @@ def _parseNotationColumn(self):
modifierStrings.append(modifierString)
self.extenders.append(extender)

numbers = tuple(numbers)
modifierStrings = tuple(modifierStrings)
numbersTuple = tuple(numbers)
modifierStringsTuple = tuple(modifierStrings)

self.origNumbers = numbers # Keep original numbers
self.numbers = numbers # Will be converted to longhand
self.origModStrings = modifierStrings # Keep original modifier strings
self.modifierStrings = modifierStrings # Will be converted to longhand
self.origNumbers = numbersTuple # Keep original numbers
self.numbers = numbersTuple # Will be converted to longhand
self.origModStrings = modifierStringsTuple # Keep original modifier strings
self.modifierStrings = modifierStringsTuple # Will be converted to longhand
self.figureStrings = figureStrings

def _translateToLonghand(self):
def _translateToLonghand(self) -> None:
'''
Provided the numbers and modifierStrings of a parsed notation column,
translates it to longhand.
Expand All @@ -349,47 +351,36 @@ def _translateToLonghand(self):
('-', '-')
'''
oldNumbers = self.numbers
newNumbers = oldNumbers
newNumbers: tuple[int|str|None, ...] = oldNumbers
oldModifierStrings = self.modifierStrings
newModifierStrings = oldModifierStrings
newModifierStrings: tuple[str|None, ...] = oldModifierStrings

try:
newNumbers = shorthandNotation[oldNumbers]
newModifierStrings = []

oldNumbers = list(oldNumbers)
temp = []
for number in oldNumbers:
if number is None:
temp.append(3)
else:
temp.append(number)
shorthandKey = t.cast('tuple[int|None, ...]', oldNumbers)
newNumbers = shorthandNotation[shorthandKey]
modStrings: list[str|None] = []

oldNumbers = tuple(temp)
expandedOldNumbers = tuple(
3 if number is None else number for number in oldNumbers
)

for number in newNumbers:
newModifierString = None
if number in oldNumbers:
modifierStringIndex = oldNumbers.index(number)
newModifierString: str|None = None
if number in expandedOldNumbers:
modifierStringIndex = expandedOldNumbers.index(number)
newModifierString = oldModifierStrings[modifierStringIndex]
newModifierStrings.append(newModifierString)
modStrings.append(newModifierString)

newModifierStrings = tuple(newModifierStrings)
newModifierStrings = tuple(modStrings)
except KeyError:
newNumbers = list(newNumbers)
temp = []
for number in newNumbers:
if number is None:
temp.append(3)
else:
temp.append(number)

newNumbers = tuple(temp)
newNumbers = tuple(
3 if number is None else number for number in newNumbers
)

self.numbers = newNumbers
self.modifierStrings = newModifierStrings

def _getModifiers(self):
def _getModifiers(self) -> None:
'''
Turns the modifier strings into Modifier objects.
A modifier object keeps track of both the modifier string
Expand All @@ -406,7 +397,7 @@ def _getModifiers(self):
>>> notation1.modifiers[2]
<music21.figuredBass.notation.Modifier + sharp>
'''
modifiers = []
modifiers: list[Modifier] = []

for i in range(len(self.numbers)):
modifierString = self.modifierStrings[i]
Expand Down Expand Up @@ -510,12 +501,12 @@ class Figure(prebase.ProtoM21Object):

def __init__(
self,
number: int|None = 1,
number: int|str|None = 1,
modifierString: str|None = '',
*,
extender: bool = False
):
self.number: int|None = number
) -> None:
self.number: int|str|None = number
self.modifierString: str|None = modifierString
self.modifier: Modifier = Modifier(modifierString)
# look for extender's underscore
Expand All @@ -542,7 +533,7 @@ def isPureExtender(self) -> bool:
'''
return self.number == 1 and self.hasExtender

def _reprInternal(self):
def _reprInternal(self) -> str:
if self.isPureExtender:
num = 'pure-extender'
ext = ''
Expand Down Expand Up @@ -629,18 +620,18 @@ class Modifier(prebase.ProtoM21Object):
''',
}

def __init__(self, modifierString=None):
self.modifierString = modifierString
self.accidental = self._toAccidental()
def __init__(self, modifierString: str|None = None) -> None:
self.modifierString: str|None = modifierString
self.accidental: pitch.Accidental|None = self._toAccidental()

def _reprInternal(self):
def _reprInternal(self) -> str:
if self.accidental is not None:
acc = self.accidental.name
else:
acc = None
return f'{self.modifierString} {acc}'

def _toAccidental(self):
def _toAccidental(self) -> pitch.Accidental|None:
'''

>>> from music21.figuredBass import notation as n
Expand Down Expand Up @@ -677,7 +668,7 @@ def _toAccidental(self):

return a

def modifyPitchName(self, pitchNameToAlter):
def modifyPitchName(self, pitchNameToAlter: str) -> str:
'''
Given a pitch name, modify its accidental given the Modifier's
:attr:`~music21.figuredBass.notation.Modifier.accidental`.
Expand All @@ -697,7 +688,12 @@ def modifyPitchName(self, pitchNameToAlter):
self.modifyPitch(pitchToAlter, inPlace=True)
return pitchToAlter.name

def modifyPitch(self, pitchToAlter, *, inPlace=False):
def modifyPitch(
self,
pitchToAlter: pitch.Pitch,
*,
inPlace: bool = False
) -> pitch.Pitch|None:
'''
Given a :class:`~music21.pitch.Pitch`, modify its :attr:`~music21.pitch.Pitch.accidental`
given the Modifier's :attr:`~music21.figuredBass.notation.Modifier.accidental`.
Expand Down Expand Up @@ -751,7 +747,7 @@ class ModifierException(exceptions21.Music21Exception):
# Helper Methods


def convertToPitch(pitchString):
def convertToPitch(pitchString: str|pitch.Pitch) -> pitch.Pitch:
'''
Converts a pitchString to a :class:`~music21.pitch.Pitch`, only if necessary.

Expand Down
Loading
Loading