Skip to content

New PedalMarks implementation #1766

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

Draft
wants to merge 76 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
08174e6
New implementation. Also fixed a bug where positions were being impor…
gregchapman-dev Apr 4, 2025
76c325b
Fix the tests to match the new API shape, and fix two bugs found.
gregchapman-dev Apr 5, 2025
c34b0a6
Add bounceUp and bounceDown properties to PedalBounce, because I grew…
gregchapman-dev Apr 5, 2025
2d9b38e
MusicXML export: preallocate PedalMark localIds for PedalBounces that…
gregchapman-dev Apr 12, 2025
313ea4d
MusicXML exporter: For unknown reasons, putting a list() around the O…
gregchapman-dev Apr 12, 2025
d6a378e
MusicXML import: Allow spanners to be started with a <forward> tag (i…
gregchapman-dev Apr 12, 2025
1533ab2
Make mypy happy.
gregchapman-dev Apr 13, 2025
577d020
Fix the failing tests.
gregchapman-dev Apr 13, 2025
faed972
Use SpannerAnchors in xmlToM21.py:xmlDirectionTypeToSpanners, so we n…
gregchapman-dev Apr 13, 2025
205a102
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 13, 2025
2fe7c44
Better SpannerAnchor debug output. Bump version to re-import corpus.
gregchapman-dev Apr 15, 2025
ae3af61
lint.
gregchapman-dev Apr 15, 2025
c1e75d1
When splitting part staves, SpannerAnchors should stay in the staff w…
gregchapman-dev Apr 15, 2025
5806b93
In Spanner.fill, if you remove and re-add the endElement, also restor…
gregchapman-dev Apr 15, 2025
a8df0dc
Update some tests.
gregchapman-dev Apr 15, 2025
9886dd9
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 15, 2025
bd3a9ed
More test fixes.
gregchapman-dev Apr 15, 2025
6e73166
Doctest fixes
gregchapman-dev Apr 15, 2025
e590561
More test fixes (due to extra backups being exported due to spanners …
gregchapman-dev Apr 15, 2025
570c84f
Test result tweak ('beach' has more elements now).
gregchapman-dev Apr 15, 2025
e760f9d
Lint.
gregchapman-dev Apr 15, 2025
0d5c2c3
m21ToXml.py: Don't move forward by duration of measure, move forward …
gregchapman-dev Apr 16, 2025
87fc100
MusicXML import: no more hidden rests from xmlForward.
gregchapman-dev Apr 16, 2025
3764320
Pick up more of PR #1636 (no rests for the forwards).
gregchapman-dev Apr 16, 2025
83eea9e
Update tests for new reality.
gregchapman-dev Apr 16, 2025
fa83430
More test updates.
gregchapman-dev Apr 16, 2025
ee3f686
One last test update.
gregchapman-dev Apr 16, 2025
485e728
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 16, 2025
6412ef9
Lint.
gregchapman-dev Apr 16, 2025
94f9b9b
Lint.
gregchapman-dev Apr 16, 2025
7ec655a
Merge branch 'master' into gregc/betterPedalMarks
gregchapman-dev Apr 16, 2025
2b50e65
Merge branch 'master' into gregc/moreAccurateSpannerStartStop
gregchapman-dev Apr 16, 2025
1d8c47f
Remove some dead code.
gregchapman-dev Apr 16, 2025
05c34df
Remove more dead code.
gregchapman-dev Apr 17, 2025
43410d3
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 17, 2025
9abc002
makeRests always produces exportable (non-complex duration) rests.
gregchapman-dev Apr 17, 2025
9b0308b
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 17, 2025
dc31653
Don't crash on export to MusicXML when a rest duration is complex (mo…
gregchapman-dev Apr 17, 2025
9777adc
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 17, 2025
8cf3ffd
Add a test for (MusicXML) import/export/re-import of spanners with of…
gregchapman-dev Apr 18, 2025
ae0eb99
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev Apr 18, 2025
725ad05
First bit of review.
gregchapman-dev Apr 25, 2025
a15e065
Works almost all the time; still have a problem importing MusicXML fi…
gregchapman-dev Apr 29, 2025
daea68f
Instead of looping over self.pendingAnchors (and missing any pending …
gregchapman-dev May 2, 2025
e36df6e
Looks like a spanner end element can be assigned before we see the pe…
gregchapman-dev May 2, 2025
67c882c
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 2, 2025
39db861
Don't leave uncompleted spanners in the xmlToM21.py spannerBundle. T…
gregchapman-dev May 3, 2025
fa17fdd
Oops, make an exception for ArpeggioMarkSpanners.
gregchapman-dev May 3, 2025
d748f42
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 3, 2025
5f4db7d
xmlOneSpanner also needs to deal with out-of-order start/stop due to …
gregchapman-dev May 6, 2025
eb2e66b
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 6, 2025
15bbe7d
Bump version number in hopes that's why tests are failing.
gregchapman-dev May 6, 2025
86993ad
Fix that last regression (triggered by spanner start and spanner stop…
gregchapman-dev May 6, 2025
2ab6551
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 6, 2025
34e445b
A better fix for that regression; don't let 'continue' complete a spa…
gregchapman-dev May 7, 2025
13f00f4
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 7, 2025
6be5a0b
Fixes for all those review comments.
gregchapman-dev May 7, 2025
52f71ed
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 7, 2025
1a0cca8
An attempt at a more efficient implementation of insertFirstSpannedEl…
gregchapman-dev May 7, 2025
f682f07
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 7, 2025
f75373c
Bump version (again).
gregchapman-dev May 7, 2025
fe88d12
Document the new pendingFirstSpannedElementAssignment stuff, and add …
gregchapman-dev May 7, 2025
a2f224b
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 7, 2025
ebaef99
PendingSpannedElement APIs are now just the old ones, with some optio…
gregchapman-dev May 15, 2025
ab636c6
Merge branch 'master' into gregc/moreAccurateSpannerStartStop
gregchapman-dev May 15, 2025
c07d160
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 15, 2025
7b6724a
Bump version number (again).
gregchapman-dev May 15, 2025
e484070
Merge branch 'gregc/forwardIsHiddenRestOnlyForFinale' into gregc/more…
gregchapman-dev May 15, 2025
ec17b2e
Merge branch 'gregc/forwardIsHiddenRestOnlyForFinale' into gregc/more…
gregchapman-dev May 15, 2025
58624d6
Somehow in the merge I lost the removal of the makeRests() call. Fix…
gregchapman-dev May 15, 2025
2dc9b60
Fix another merge failure: put back in the "remove that last hidden r…
gregchapman-dev May 15, 2025
faf0dcf
Merging changes from gregc/moreAccurateSpannerStartStop.
gregchapman-dev May 15, 2025
d512983
Merge branch 'master' into gregc/moreAccurateSpannerStartStop
gregchapman-dev May 22, 2025
78d2034
A few tweaks after the merge from master.
gregchapman-dev May 22, 2025
05f0cb2
Tweaks to the tweaks.
gregchapman-dev May 22, 2025
22807a2
Merge branch 'gregc/moreAccurateSpannerStartStop' into gregc/betterPe…
gregchapman-dev May 22, 2025
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
2 changes: 1 addition & 1 deletion music21/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
'''
from __future__ import annotations

__version__ = '9.7.0'
__version__ = '9.7.0b2'

def get_version_tuple(vv):
v = vv.split('.')
Expand Down
2 changes: 1 addition & 1 deletion music21/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<class 'music21.base.Music21Object'>

>>> music21.VERSION_STR
'9.7.0'
'9.7.0b2'

Alternatively, after doing a complete import, these classes are available
under the module "base":
Expand Down
125 changes: 106 additions & 19 deletions music21/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,16 +327,27 @@ def nextMark(self):

# ------------------------------------------------------------------------------
class PedalType(common.StrEnum):
Sustain = 'sustain'
Sostenuto = 'sostenuto'
Soft = 'soft'
Silent = 'silent'
Unspecified = 'Unspecified'
Sustain = 'Sustain'
Sostenuto = 'Sostenuto'
Soft = 'Soft'
Silent = 'Silent'

class PedalForm(common.StrEnum):
Line = 'line'
Symbol = 'symbol'
SymbolAlt = 'symbolalt'
SymbolLine = 'symbolline'
Unspecified = 'Unspecified'
PedalName = 'PedalName'
Ped = 'Ped' # sometimes seen even for sostenuto, etc.
Star = 'Star'
VerticalLine = 'VerticalLine'
NoMark = 'NoMark'
SlantedLine = 'SlantedLine'
Inherit = 'Inherit' # inherit from enclosing PedalMark

class PedalLine(common.StrEnum):
Unspecified = 'Unspecified'
NoLine = 'NoLine'
Line = 'Line'
Dashed = 'Dashed'

class PedalMark(spanner.Spanner):
'''
Expand All @@ -349,8 +360,8 @@ class PedalMark(spanner.Spanner):
examples use a pedal mark with one "bounce" in the middle::

Pedal marks can be lines: |_______^________|
Pedal marks can be normal symbolic: Ped. * Ped. *
Pedal marks can be altered symbolic: Ped. Ped. *
Pedal marks can be symbols: Ped. *Ped. *
Pedal bounces might not have a *: Ped. Ped. *
Pedal marks can be symbol and line: Ped.____^________|

Pedal marks, whether lines, symbols, or a combination, can
Expand All @@ -362,55 +373,131 @@ class PedalMark(spanner.Spanner):
of Ped., S. instead of Sost.

Pedal marks that are lines can have non-printed portions
(gaps) in them; these are usually started with "simile",
(gaps) in them; these gaps are usually started with "simile",
but not necessarily.
'''
def __init__(
self,
*spannedElements,
pedalType: PedalType = PedalType.Unspecified,
startForm: PedalForm = PedalForm.Unspecified,
continueLine: PedalLine = PedalLine.Unspecified,
bounceUp: PedalForm = PedalForm.Unspecified,
bounceDown: PedalForm = PedalForm.Unspecified,
endForm: PedalForm = PedalForm.Unspecified,
abbreviated: bool = False,
**keywords
) -> None:
super().__init__(*spannedElements, **keywords)
from music21 import note
self.fillElementTypes = [note.GeneralNote]

self.pedalType: PedalType|None = None
self.pedalForm: PedalForm|None = None
self.abbreviated: bool = False
self.pedalType: PedalType = pedalType
self.startForm: PedalForm = startForm
self.continueLine: PedalLine = continueLine
self.bounceUp: PedalForm = bounceUp
self.bounceDown: PedalForm = bounceDown
self.endForm: PedalForm = endForm
self.abbreviated: bool = abbreviated

self.placement: str|None = None

def hasLine(self) -> bool:
if self.startForm == PedalForm.VerticalLine:
# it's all lines
return True
if self.continueLine in (PedalLine.Line, PedalLine.Dashed):
# it's Ped followed by a line
return True
return False


class PedalObject(base.Music21Object):
class PedalTransition(base.Music21Object):
'''
Base class of individual objects that mark various transitions
in a PedalMark spanner.
'''
def __init__(self, **keywords) -> None:
super().__init__(**keywords)
self.placement: str | None = None
self.placement: str|None = None

def _reprInternal(self) -> str:
if self.activeSite is None:
return 'uninserted'
return f'at {self.offset}'


class PedalBounce(PedalObject):
class PedalBounce(PedalTransition):
'''
This object, when seen in a PedalMark spanner, represents an up/down bounce
of the pedal.

>>> pm = expressions.PedalMark()
>>> pm.bounceUp = expressions.PedalForm.Star
>>> pm.bounceDown = expressions.PedalForm.Ped
>>> pb = expressions.PedalBounce()
>>> pm.addSpannedElements(pb)

By default, PedalBounce objects inherit their bounceUp/bounceDown forms
from the enclosing PedalMark spanner.

>>> pb.bounceUp
<PedalForm.Star>
>>> pb.bounceDown
<PedalForm.Ped>

But individual PedalBounce objects can override that bounceUp/bounceDown
inheritance.

>>> pb.overrideBounceUp = expressions.PedalForm.SlantedLine
>>> pb.overrideBounceDown = expressions.PedalForm.SlantedLine
>>> pb.bounceUp
<PedalForm.SlantedLine>
>>> pb.bounceDown
<PedalForm.SlantedLine>
>>>
'''
def __init__(
self,
overrideBounceUp: PedalForm = PedalForm.Inherit,
overrideBounceDown: PedalForm = PedalForm.Inherit,
**keywords
) -> None:
super().__init__(**keywords)
self.overrideBounceUp = overrideBounceUp
self.overrideBounceDown = overrideBounceDown

@property
def bounceUp(self) -> PedalForm:
if self.overrideBounceUp != PedalForm.Inherit:
return self.overrideBounceUp
pmList: list[spanner.Spanner] = self.getSpannerSites((PedalMark,))
if not pmList:
return self.overrideBounceUp
if t.TYPE_CHECKING:
assert isinstance(pmList[0], PedalMark)
return pmList[0].bounceUp

class PedalGapStart(PedalObject):
@property
def bounceDown(self) -> PedalForm:
if self.overrideBounceDown != PedalForm.Inherit:
return self.overrideBounceDown
pmList: list[spanner.Spanner] = self.getSpannerSites((PedalMark,))
if not pmList:
return self.overrideBounceDown
if t.TYPE_CHECKING:
assert isinstance(pmList[0], PedalMark)
return pmList[0].bounceDown

class PedalGapStart(PedalTransition):
'''
This object, when seen in a PedalMark spanner, represents a disappearance of
the pedal line, usually with a TextExpression('simile'). The pedaling should
continue (similarly to before), but the line is invisible during this gap.
'''


class PedalGapEnd(PedalObject):
class PedalGapEnd(PedalTransition):
'''
This object, when seen in a PedalMark spanner, represents the reappearance of
the pedal line.
Expand Down
Loading