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

FretBend update #1580

Draft
wants to merge 79 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
03fdb47
:arrow_heading_up: bend export
adhooge Nov 21, 2022
aca37e2
add some properties to note.Note
adhooge Nov 23, 2022
7118189
fix missing import
adhooge Dec 2, 2022
766a113
`FretBend`: update preBend type
adhooge Jan 12, 2023
17bc30d
`FretBend`: update Release type
adhooge Jan 12, 2023
4bcdffc
noteToNotations: export string fret art in chord
Feb 28, 2023
274c904
new changes
May 22, 2023
0fdac57
Merge remote-tracking branch 'upstream/master'
May 22, 2023
6d778f1
read bends
May 23, 2023
7e6ced8
restore README
May 23, 2023
ef52fe0
single quotes
May 23, 2023
e3f88ee
typing withBar
May 23, 2023
f1eae7d
python3.9 compatibility
May 23, 2023
22d07da
remove useless files
May 23, 2023
c157a53
bendAlter typing
May 23, 2023
67540ea
rm useless file
May 23, 2023
34ecc42
restore file
May 23, 2023
13ac21f
fix some conflicts
Mar 18, 2024
9c3b1a3
add some properties to note.Note
adhooge Nov 23, 2022
0839230
fix missing import
adhooge Dec 2, 2022
cd534fe
finish rebase
Mar 18, 2024
b1c9277
`FretBend`: update Release type
adhooge Jan 12, 2023
caa8bd2
noteToNotations: export string fret art in chord
Feb 28, 2023
a21d3f6
new changes
May 22, 2023
7855e26
read bends
May 23, 2023
b95c2c0
restore README
May 23, 2023
97297be
single quotes
May 23, 2023
3d5092a
typing withBar
May 23, 2023
e45f7b8
python3.9 compatibility
May 23, 2023
4f07cbb
bendAlter typing
May 23, 2023
97e33a0
restore file
May 23, 2023
edf7934
add mute articulation
Aug 24, 2023
27974be
Merge branch 'pr-bend' of github.com:adhooge/music21 into pr-bend
Mar 18, 2024
3cf71e0
alphabetical imports
Mar 18, 2024
d63c78a
rework types and default values
Mar 18, 2024
03e6117
FretBend: rework documentation
Mar 18, 2024
85cc6a8
separate method for FretBend
Mar 18, 2024
c11ce14
comment from musicXML doc
Mar 18, 2024
d93af6d
release to divisions
Mar 18, 2024
2e1ea43
make setBend a sub-method
Mar 18, 2024
58eeb04
convert offset to quarter length
Mar 18, 2024
0b2074f
remove my properties
Mar 18, 2024
f215c8c
FretBend: add tests
Mar 18, 2024
e4499dc
Merge branch 'cuthbertLab:master' into pr-bend
adhooge Aug 13, 2024
0ba3f33
fix some conflicts
Mar 18, 2024
32d5135
add some properties to note.Note
adhooge Nov 23, 2022
030935a
fix missing import
adhooge Dec 2, 2022
a40c9d6
finish rebase
Mar 18, 2024
4938f1e
`FretBend`: update Release type
adhooge Jan 12, 2023
ded5058
noteToNotations: export string fret art in chord
Feb 28, 2023
bb8ab1d
new changes
May 22, 2023
4c5d96d
read bends
May 23, 2023
6646a04
restore README
May 23, 2023
640f051
single quotes
May 23, 2023
5ab284d
typing withBar
May 23, 2023
30c2a85
python3.9 compatibility
May 23, 2023
8262559
bendAlter typing
May 23, 2023
e36d0ff
restore file
May 23, 2023
5cf8b7d
add mute articulation
Aug 24, 2023
63b9df5
:arrow_heading_up: bend export
adhooge Nov 21, 2022
5dc55d9
`FretBend`: update preBend type
adhooge Jan 12, 2023
11b6e7e
`FretBend`: update Release type
adhooge Jan 12, 2023
ba04559
noteToNotations: export string fret art in chord
Feb 28, 2023
b2bd258
single quotes
May 23, 2023
04d369e
typing withBar
May 23, 2023
8ef43f0
python3.9 compatibility
May 23, 2023
43e9277
bendAlter typing
May 23, 2023
38c0d61
restore file
May 23, 2023
adc65e3
alphabetical imports
Mar 18, 2024
1420a42
rework types and default values
Mar 18, 2024
3ee1fba
FretBend: rework documentation
Mar 18, 2024
2773315
separate method for FretBend
Mar 18, 2024
467317b
comment from musicXML doc
Mar 18, 2024
dae3be6
release to divisions
Mar 18, 2024
5c3b870
make setBend a sub-method
Mar 18, 2024
60fcfed
convert offset to quarter length
Mar 18, 2024
2c41586
remove my properties
Mar 18, 2024
96782de
FretBend: add tests
Mar 18, 2024
e7e0db3
Merge branch 'pr-bend' of github.com:adhooge/music21 into pr-bend
adhooge Aug 13, 2024
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
26 changes: 22 additions & 4 deletions music21/articulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
from music21.common.classTools import tempAttribute
from music21 import environment
from music21 import style
from music21 import interval
Copy link
Member

Choose a reason for hiding this comment

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

alphabetical order (existing mistake with style before spanner can be fixed in this PR also)

Copy link
Member

Choose a reason for hiding this comment

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

and if this is added it needs to be removed from they TYPE_CHECKING import below (but I don't think it will need to be, see below)

Copy link
Author

Choose a reason for hiding this comment

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

Done with 3cf71e0

from music21 import spanner

if t.TYPE_CHECKING:
Expand Down Expand Up @@ -582,10 +583,27 @@ class PullOff(spanner.Spanner, TechnicalIndication):
pass

class FretBend(FretIndication):
bendAlter: interval.IntervalBase | None = None
preBend: t.Any = None
release: t.Any = None
withBar: t.Any = None
bendAlter: interval.IntervalBase
preBend: bool = False
release: t.Optional[float]
withBar: t.Optional[bool]

def __init__(self, number=0, preBend=False, release=None, withBar=None, **keywords):
Copy link
Member

Choose a reason for hiding this comment

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

number is too generic a term -- and we usually reserve the term for identifying which of multiple simultaneous items (like slur number=1 vs slur number=2). Are fret-bends always chromatic intervals? it seems like you can also have a diatonic interval of a fretbend. hence why bendAlter was typed as IntervalBase not ChromaticInterval. It seems better to have bendAlter as described above interval.IntervalBase | None = None where None indicates unspecified. Having a default of 0, becoming ChromaticInterval(0) indicates that this fretBend does not alter the pitch at all, which would be a very strange fret bend. This would also make it so that interval.py does not need to be imported here.

The other keywords should be typed in the __init__ declaration in addition to the class.

We no longer use t.Optional[X] in the code base. Use X | None instead.

Copy link
Author

Choose a reason for hiding this comment

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

Changed with d63c78a

'''
bend indication for fretted instruments

bend in musicxml

number is the interval of the bend in number of semitones, bend-alter in musicxml
preBend indicates wether the note is prebended or not
release is the offset value for releasing the bend, if Any
withBar indicates if the bend is done using a whammy bar movement
Copy link
Member

Choose a reason for hiding this comment

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

Capitalization and use asterisks and line breaks for documentation -- run documentation/build.py and look at the difference between the docs produced here and elsewhere in the system.

preBend indicates wether the note is prebended or not -- I don't know what prebended means.

what is release measured in? Is it an offset (which is relative to the start of the Measure/stream)? That doesn't sound right. Is it a quarterLength measured from the start of the note? or from the end of the note.

withBar doc is fine.

Copy link
Author

Choose a reason for hiding this comment

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

I've tried to improve it in 03e6117
Let me know if that seems good to you

Copy link
Author

Choose a reason for hiding this comment

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

tests are on their way

'''
super().__init__(**keywords)
Copy link
Member

Choose a reason for hiding this comment

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

bug. needs to be super().__init__(number=number, **keywords)

self.bendAlter = interval.ChromaticInterval(number)
self.preBend = preBend
self.release = release
self.withBar = withBar

class FretTap(FretIndication):
pass
Expand Down
17 changes: 13 additions & 4 deletions music21/musicxml/m21ToXml.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
from music21.musicxml import helpers
from music21.musicxml.partStaffExporter import PartStaffExporterMixin
from music21.musicxml import xmlObjects
from music21.musicxml.xmlObjects import MusicXMLExportException
from music21.musicxml.xmlObjects import MusicXMLExportException, booleanToYesNo
from music21.musicxml.xmlObjects import MusicXMLWarning

environLocal = environment.Environment('musicxml.m21ToXml')
Expand Down Expand Up @@ -5486,7 +5486,6 @@ def articulationToXmlTechnical(self, articulationMark):
# these technical have extra information
# TODO: hammer-on
# TODO: pull-off
# TODO: bend
# TODO: hole
# TODO: arrow
musicXMLTechnicalName = None
Expand All @@ -5498,7 +5497,7 @@ def articulationToXmlTechnical(self, articulationMark):
musicXMLTechnicalName = 'other-technical'

# TODO: support additional technical marks listed above
if musicXMLTechnicalName in ('bend', 'hole', 'arrow'):
if musicXMLTechnicalName in ('hole', 'arrow'):
musicXMLTechnicalName = 'other-technical'

mxTechnicalMark = Element(musicXMLTechnicalName)
Expand All @@ -5523,7 +5522,17 @@ def articulationToXmlTechnical(self, articulationMark):
mxTechnicalMark.text = str(articulationMark.number)
if musicXMLTechnicalName == 'fret':
mxTechnicalMark.text = str(articulationMark.number)

if musicXMLTechnicalName == 'bend':
bendAlterSubElement = SubElement(mxTechnicalMark, 'bend-alter')
bendAlterSubElement.text = str(articulationMark.bendAlter.semitones)
Copy link
Member

Choose a reason for hiding this comment

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

this has enough ifs that it should be brought out as its own small method that allows for easier documentation and testing. articulationMark will probably need to be cast as a FretBend.

We can solve the case of GenericInterval later, which doesn't have .semitones. Perhaps it always needs to be a full Interval object or None.

Copy link
Author

Choose a reason for hiding this comment

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

New submethod added in 85cc6a8
Now I indeed have the problem that I need to retrieve the number of semitones. I could probably do something with GenericInterval.value but I don't know how to do the conversion properly

if articulationMark.preBend:
preBendSubElement = SubElement(mxTechnicalMark, 'pre-bend')
if articulationMark.release is not None:
releaseSubElement = SubElement(mxTechnicalMark, 'release')
releaseSubElement.set('offset', str(articulationMark.release))
Copy link
Member

Choose a reason for hiding this comment

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

musicxml's definition of offset is very different from music21's and (despite me now being editor of the musicxml spec) we shouldn't use musicxml offsets anywhere in music21 -- these need to be converted according to the current divisionsPerQuarter setting.

Copy link
Author

Choose a reason for hiding this comment

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

should work with d93af6d though I need to write the tests

if articulationMark.withBar is not None:
withBarSubElement = SubElement(mxTechnicalMark, 'with-bar')
withBarSubElement.text = str(articulationMark.withBar)
# harmonic needs to check for whether it is artificial or natural, and
# whether it is base-pitch, sounding-pitch, or touching-pitch
if musicXMLTechnicalName == 'harmonic':
Expand Down
3 changes: 1 addition & 2 deletions music21/musicxml/xmlObjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,11 @@
('stopped', articulations.Stopped),
('snap-pizzicato', articulations.SnapPizzicato),
('string', articulations.StringIndication),
('bend', articulations.FretBend),
# hammer-on and pull-off not implemented because handled
# in method objectAttachedSpannersToTechnicals of m21ToXml.py
# ('hammer-on', articulations.HammerOn),
# ('pull-off', articulations.PullOff),
# bend not implemented because it needs many subcomponents
# ('bend', articulations.FretBend),
('tap', articulations.FretTap),
('fret', articulations.FretIndication),
('heel', articulations.OrganHeel),
Expand Down
11 changes: 9 additions & 2 deletions music21/musicxml/xmlToM21.py
Original file line number Diff line number Diff line change
Expand Up @@ -3881,11 +3881,18 @@ def xmlTechnicalToArticulation(self, mxObj):
if tag in ('heel', 'toe'):
if mxObj.get('substitution') is not None:
tech.substitution = xmlObjects.yesNoToBoolean(mxObj.get('substitution'))
if tag == 'bend':
tech.bendAlter = interval.Interval(int(mxObj.find('bend-alter').text))
Copy link
Member

Choose a reason for hiding this comment

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

this has too many potentials for crashing the processing. What if there is no bend-alter or no bend-alter.text? check that before typing to assign.

(Better to call out into a separate sub-method. This method was designed just for one-to-two lines per technical

Copy link
Author

Choose a reason for hiding this comment

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

sub-method and bend-alter check in 2e1ea43

if mxObj.find('pre-bend') is not None:
tech.preBend = True
if mxObj.find('release') is not None:
tech.release = int(mxObj.find('release').get('offset'))
Copy link
Member

Choose a reason for hiding this comment

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

as above -- offset needs to be converted to music21 quarterLengths.

except clause without Try.

Copy link
Author

Choose a reason for hiding this comment

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

Both fixed with 58eeb04

except TypeError:
# offset is not mandatory
tech.release = 0
Copy link
Member

Choose a reason for hiding this comment

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

release is a float. needs to be 0.0

Copy link
Author

Choose a reason for hiding this comment

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

also fixed with 58eeb04

# TODO: <bend> attr: accelerate, beats, first-beat, last-beat, shape (4.0)
# TODO: <bent> sub-elements: bend-alter, pre-bend, with-bar, release
# TODO: musicxml 4: release sub-element as offset attribute


self.setPlacement(mxObj, tech)
return tech
else:
Expand Down
44 changes: 44 additions & 0 deletions music21/note.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from music21 import style
from music21 import tie
from music21 import volume
from music21 import articulations

if t.TYPE_CHECKING:
from music21 import articulations
Copy link
Member

Choose a reason for hiding this comment

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

as above -- alphabetical and remove from type-checking if imported at top. (but won't be necessary).

Expand Down Expand Up @@ -1784,6 +1785,49 @@ def pitchChanged(self):
if self._chordAttached is not None:
self._chordAttached.clearCache()

@property
def string(self):
Copy link
Member

Choose a reason for hiding this comment

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

this new property and all properties below are rejected. Note is the most common object in music21 and users who are not working with guitar music will never use these routines but will need to figure out what they mean in the docs. (we don't have "accent" or "staccato" etc. either) -- and parallel routines would need to be added to Chord, etc.

In general for any mature open-source project, don't make additions or substantive changes to core parts of the system without a prior discussion.

Copy link
Author

Choose a reason for hiding this comment

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

oops sorry about that, they were just for my personal convenience. Removed with 0b2074f

'''
Returns articulations.StringIndication if object has one.
'''
for art in self.articulations:
if isinstance(art, articulations.StringIndication):
return art
return None


@property
def fret(self):
'''
Returns articulations.FretIndication if object has one.
'''
for art in self.articulations:
if isinstance(art, articulations.FretIndication):
return art
return None

@property
def isBended(self):
'''
Returns True if the Note has a FretBend articulation. False otherwise.
'''
for art in self.articulations:
if isinstance(art, articulations.FretBend):
return True
return False

@property
def bend(self):
'''
Returns the articulations.FretBend object if there is one in self.articulations.
Returns None otherwise.
'''
if not self.isBended:
return None
for art in self.articulations:
if isinstance(art, articulations.FretBend):
return art

# ------------------------------------------------------------------------------
# convenience classes

Expand Down