Skip to content

Commit

Permalink
Merge pull request #10 from mxordn/test-figured-bass
Browse files Browse the repository at this point in the history
Test figured bass
  • Loading branch information
mxordn authored Jun 17, 2023
2 parents 774c126 + f62fc34 commit 03ca64e
Show file tree
Hide file tree
Showing 12 changed files with 4,749 additions and 67 deletions.
3 changes: 2 additions & 1 deletion music21/figuredBass/notation.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
'double-sharp': '##',
'flat-flat': 'bb',
'backslash': '\\',
'slash': '/'}
'slash': '/',
'cross': '+'}

modifiersDictM21ToXml = {'#': 'sharp',
'b': 'flat',
Expand Down
27 changes: 25 additions & 2 deletions music21/harmony.py
Original file line number Diff line number Diff line change
Expand Up @@ -2501,8 +2501,30 @@ def transpose(self: NCT, _value, *, inPlace=False) -> NCT | None:
# ------------------------------------------------------------------------------

class FiguredBassIndication(Harmony):
'''
The FiguredBassIndication objects store information about thorough bass figures.
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:
>>> fbi = harmony.FiguredBassIndication('#,6#', part='1')
>>> fbi
<FiguredBassIndication figures: #,6# part: 1>
The single figures are stored as figuredBass.notation.Figure objects:
>>> fbi.fig_notation.figures
[<music21.figuredBass.notation.Figure 3 Mods: <Modifier # sharp> hasExt: False>,
<music21.figuredBass.notation.Figure 6 Mods: <Modifier # sharp> hasExt: False>]
'''

isFigure: bool = True
def __init__(self, figs: str | list | None = None, extenders: list[bool] | None = None , **keywords):
part: str | None = None
_figs: str = ''

def __init__(self, figs: str | list | None = None, extenders: list[bool] | None = None ,
part: str | None=None, **keywords):
super().__init__(**keywords)
if figs:
if isinstance(figs, list):
Expand All @@ -2515,6 +2537,7 @@ def __init__(self, figs: str | list | None = None, extenders: list[bool] | None
else:
_figs = ''
self._fig_notation = notation.Notation(_figs, extenders)
self.part = part

@property
def fig_notation(self) -> notation.Notation:
Expand All @@ -2525,7 +2548,7 @@ def fig_notation(self, figs, extenders=None):
self._fig_notation = notation.Notation(figs, extenders)

def __repr__(self):
return f'<{self.__class__.__name__} figures: {self.fig_notation.notationColumn}>'
return f'<{self.__class__.__name__} figures: {self.fig_notation.notationColumn} part: {self.part}>'

def realizeChordSymbolDurations(piece):
'''
Expand Down
45 changes: 21 additions & 24 deletions music21/mei/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,10 +471,9 @@ def allPartsPresent(scoreElem) -> tuple[str, ...]:
# Get information of possible <harm> tags in the score. If there are tags prepare a list to
# store them and process them later.
# TODO: Maybe to be put in a separate function e.g. like allPartsPresent
figuredBassQuery = f'.//{MEI_NS}fb'
if scoreElem.findall(figuredBassQuery):
environLocal.printDebug('harm tag found!')
partNs.append('fb')
#figuredBassQuery = f'.//{MEI_NS}fb'
#if scoreElem.findall(figuredBassQuery):
# environLocal.printDebug('harm tag found!')
# here other <harm> elements (e.g. chordsymbols) can be added.
# … 'if …'

Expand Down Expand Up @@ -2527,13 +2526,14 @@ def harmFromElement(elem, slurBundle=None) -> tuple:

fb_harmony_tag: tuple = ()

# Collect all elements in a measure and go throug extenders
# Collect all elements in a measure and go through extenders
# tstamp has to be used as a duration marker between two elements
for subElement in _processEmbeddedElements(elem.findall('*'),
tagToFunction,
elem.tag, slurBundle):
subElement.tstamp = float(elem.get('tstamp'))
subElement.offset = subElement.tstamp - 1
subElement.part = elem.get('staff')
fb_harmony_tag = (subElement.tstamp - 1, subElement)

return fb_harmony_tag
Expand Down Expand Up @@ -2581,7 +2581,8 @@ def figuredbassFromElement(elem, slurBundle=None) -> harmony.FiguredBassIndicati

# Generate a FiguredBassIndication object and set the collected information
fb_notation = ','.join(fb_notation_list)
theFbNotation = harmony.FiguredBassIndication(fb_notation, extenders=fb_extenders)
theFbNotation = harmony.FiguredBassIndication(fb_notation, extenders=fb_extenders,
part=elem.get('n'))
theFbNotation.id = fb_id
theFbNotation.duration = duration.Duration(quarterLength=dauer)

Expand Down Expand Up @@ -3236,7 +3237,11 @@ def measureFromElement(elem, backupNum, expectedNs, slurBundle=None, activeMeter

# iterate all immediate children
for eachElem in elem.iterfind('*'):
if staffTag == eachElem.tag:
# first get all information stored in <harm> tags.
# They are stored on the same level as <staff>.
if harmTag == eachElem.tag:
harmElements['fb'].append(harmFromElement(eachElem))
elif staffTag == eachElem.tag:
staves[eachElem.get('n')] = stream.Measure(staffFromElement(eachElem,
slurBundle=slurBundle),
number=int(elem.get('n', backupNum)))
Expand All @@ -3249,9 +3254,6 @@ def measureFromElement(elem, backupNum, expectedNs, slurBundle=None, activeMeter
environLocal.warn(_UNIMPLEMENTED_IMPORT.format('<staffDef>', '@n'))
else:
stavesWaiting[whichN] = staffDefFromElement(eachElem, slurBundle)
elif harmTag == eachElem.tag:
# get all information stored in <harm> tags
harmElements['fb'].append(harmFromElement(eachElem))

elif eachElem.tag not in _IGNORE_UNPROCESSED:
environLocal.printDebug(_UNPROCESSED_SUBELEMENT.format(eachElem.tag, elem.tag))
Expand All @@ -3266,7 +3268,15 @@ def measureFromElement(elem, backupNum, expectedNs, slurBundle=None, activeMeter
staves[whichN].insert(0, eachObj)

# Add <harm> objects to the staves dict
staves['fb'] = harmElements
#staves['fb'] = harmElements

# Add <harm> objects to the corrresponding staff within the Measure
for fb in harmElements['fb']:
offset = fb[0]
fbi = fb[1]
m = staves.get(fbi.part)
m.insert(offset, fbi)

# other childs of <harm> tags can be added here…

# create rest-filled measures for expected parts that had no <staff> tag in this <measure>
Expand Down Expand Up @@ -3601,12 +3611,6 @@ def scoreFromElement(elem, slurBundle):
# document. Iterating the keys in "parsed" would not preserve the order.
environLocal.printDebug('*** making the Score')

# Extract collected <harm> information stored in the dict unter the 'fb' key
harms: list[dict] | None = None
if 'fb' in parsed.keys():
harms = parsed['fb'][0]
del parsed['fb']
allPartNs = allPartNs[0:-1]

theScore = [stream.Part() for _ in range(len(allPartNs))]
for i, eachN in enumerate(allPartNs):
Expand All @@ -3616,13 +3620,6 @@ def scoreFromElement(elem, slurBundle):
theScore[i].append(eachObj)
theScore = stream.Score(theScore)

# loop through measures to insert harm elements from harms list at the right offsets
if harms:
for index, measureOffset in enumerate(theScore.measureOffsetMap().keys()):
hms = harms[index]['fb']
for h in hms:
theScore.insert(measureOffset + h[0], h[1])

# put slurs in the Score
theScore.append(list(slurBundle))
# TODO: when all the Slur objects are at the end, they'll only be outputted properly if the
Expand Down
Loading

0 comments on commit 03ca64e

Please sign in to comment.