Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/figuredbass object #1614

Merged
merged 10 commits into from
Jul 2, 2023

Conversation

mxordn
Copy link
Contributor

@mxordn mxordn commented Jun 19, 2023

I'm opening this as a smaller step towards representing and importing figures bass information. PR #1613 is the big PR that is split here into three parts as suggested there.

One of the first things is to add the FiguredBass Object, that stores the information in the score/stream.

I'm comenting on the remarks by mscuthbert:

Leave the repr for fb.notation.Figure alone for the common case where hasExtension is False. Add "+extension" or "--" or something for that case. in general, touch as little as needed.

Done so now.

Make isExtension a property that computes automatically or at least is updated if number or figure is changed.

The Figure init does this now.

When adding an additional less-used feature to an existing method, generally make it keyword only. (such as extenders)

need typing on extenders -- no idea what it is expecting. (all new additions to m21 must be typed). Try to avoid X | None types where falsey X (like an empty tuple) can suffice.

I changes the handling of extenders now slightly and decided that they only can be parsed as underscores. Extenders are a rather big problem. For now also the musicxml and mei parser can work with the underscore char.

only camelCase not underscore_case for exposed properties, etc.

changed the snake case things

what is "originalString" doing in Modifier that is different than modifierString?

it stores an "original" version with the original values. E. g. a figure number 3 would not be added to a pure '#'

in FiguedBassIndication:

  • rename to FiguredBass

Done

  • figs -- neds typing of list of what? remove None option and make default be () or ''

Done

  • watch indentation there. See format elsewhere -- all arguments on one line or each argument on its own line,.

Alright didn't know that. Done.

  • Python's not TypeScript: isFigure, part, _figs should be defined on the instance not the Class.

Done

  • do not use str for "part" -- people will expect that "part" refers to a stream.Part object

renamed to "correspondingPart"

  • fig_notation --> notation, since it returns a Notation object (and no snake case). needs typing and docs. Currently violates these property: f = harmony.FiguredBassIndication(...); f.fig_notation = f.fig_notation should not change f.fig_notation

Should be solved. Although I'm not sure, if I got your point.

Hope this is going better in this way.

@coveralls
Copy link

coveralls commented Jun 19, 2023

Coverage Status

coverage: 93.037% (+0.01%) from 93.027% when pulling 88b8ed9 on mxordn:feature/figuredbass-object into 9951a8e on cuthbertLab:master.

Copy link
Member

@mscuthbert mscuthbert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the right track -- some changes requested (and more tests and documentation).

In general, objects do not keep track of what Part they are in; so I'd need a very compelling reason why FiguredBass needs a correspondingPart when ChordSymbols etc. don't.

Thanks!

prefixes = ['+', '#', '++', '##']
suffixes = ['\\']

modifiersDictXmlToM21 = {'sharp': '#',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent as

modifiersDictXmlToM21 = {
    'sharp' :'#',
    'flat': 'b',
    ...etc.
}

so that all keys begin on the same column and have four spaces showing they're inside a top-level dictionary. (same below)

@@ -189,12 +234,15 @@ def __init__(self, notationColumn=None):
self.origModStrings = None
self.numbers = None
self.modifierStrings = None
self.extenders:list[bool] = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space before list[bool]

self._parseNotationColumn()
self._translateToLonghand()

# Convert to convenient notation
self.modifiers = None
self.figures = None
self.figuresFromNotationColumn: list(Figure) = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list[Figure] -- square brackets not parentheses.


numbers = tuple(numbers)
modifierStrings = tuple(modifierStrings)

# extenders come from the optional argument when instantionting the object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo. instantiating.

# 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
#
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove blank #

self._figs = ','.join(figureString)
else:
self._figs = ''
self._figNotation = notation.Notation(self._figs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs typing.



def __init__(self,
figureString: str | list[str] | None = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After music21 moved to the world of typing extensions (around v6) figureString should just take a string. If someone wants to pass in a list of figures that can be a different keyword-only argument like figureStrings.

There are lots of cases in the current code that haven't moved to this paradigm because they're grandfathered in, but we're trying to make it so that new code mostly takes one type of object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would then add the two keywords figureString and figureStrings. If nothing at all is handed over, it will result in an empty FiguredBass.

Comment on lines 2543 to 2548
def figNotation(self) -> notation.Notation:
return self._figNotation

@figNotation.setter
def figNotation(self, figs: str | list[str] | None):
self._figNotation = notation.Notation(figs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as noted above, this doesn't pass reflexive setters. (also no abbreviations. This should just be notation)

Needs docs and tests.

it seems like there should be two sets of properties. One for getting/setting the Notation object, and one for getting/setting the string of figures -- changing one should change the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I'm pushing a proposal.

It is created as a representation for <fb> tags in MEI and <figured-bass> 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')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space before.

Comment on lines 2550 to 2551
def __repr__(self):
return f'<{self.__class__.__name__} figures: {self.figNotation.notationColumn} correspondingPart: {self.corresPart}>'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Music21Objects don't define __repr__ just _reprInternal, which would be just the part from figures: {...} to the end before > -- let the Music21Object __repr__ take care of the class name, etc.

Generally the representation just provides the most relevant information, so I think it could just be return self.notation.notationColumn. in .show('text') it'll be obvious what Part the FiguredBass object belongs to.

@mscuthbert
Copy link
Member

Keeping track of the associated bass Note or Pitch object for the figure and the Key object seems like it could be important--since unlike RomanNumerals or ChordSymbols, a FB doesn't make sense without a bass (and unlike ChordSymbols but like RN it doesn't make sense without a Key object).

One thing that there are not yet tests on is .pitches -- we need to be able to get reasonable pitches out of the FB object. That's where breaking this PR up into its component parts has helped with the reasoning.

There may be parts of the RN code that might be factored out to make more reusable there. Do you mind if I push code to this PR? I think I have an idea of how things may work...

@mxordn
Copy link
Contributor Author

mxordn commented Jun 20, 2023 via email

@mscuthbert
Copy link
Member

yes -- there are definitely cases where the pitch being attached to is other than the one sounding (definitely in the case of 4-3 suspensions, etc.) so that's where a reference to a previous pitch would be helpful.

@mscuthbert
Copy link
Member

I'm a little bit behind w/ other work, so I'll extend work on this into my sabbatical for a little bit (until July 10) -- if we can get this merged by then, I'll continue working on it.

extender = self.extenders[i]
else:
extender = False
figure = Figure(number, modifierString, extender)
figure = Figure(number, modifierString)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is immediately wiped out -- mistake? Not tested enough.

@mscuthbert mscuthbert merged commit 24dcb41 into cuthbertLab:master Jul 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants