-
Notifications
You must be signed in to change notification settings - Fork 406
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
base: master
Are you sure you want to change the base?
FretBend update #1580
Changes from 44 commits
03fdb47
aca37e2
7118189
766a113
17bc30d
4bcdffc
274c904
0fdac57
6d778f1
7e6ced8
ef52fe0
e3f88ee
f1eae7d
22d07da
c157a53
67540ea
34ecc42
13ac21f
9c3b1a3
0839230
cd534fe
b1c9277
caa8bd2
a21d3f6
7855e26
b95c2c0
97297be
3d5092a
e45f7b8
4f07cbb
97e33a0
edf7934
27974be
3cf71e0
d63c78a
03e6117
85cc6a8
c11ce14
d93af6d
2e1ea43
58eeb04
0b2074f
f215c8c
e4499dc
0ba3f33
32d5135
030935a
a40c9d6
4938f1e
ded5058
bb8ab1d
4c5d96d
6646a04
640f051
5ab284d
30c2a85
8262559
e36d0ff
5cf8b7d
63b9df5
5dc55d9
11b6e7e
ba04559
b2bd258
04d369e
8ef43f0
43e9277
38c0d61
adc65e3
1420a42
3ee1fba
2773315
467317b
dae3be6
5c3b870
60fcfed
2c41586
96782de
e7e0db3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,11 +83,9 @@ | |
from music21 import common | ||
from music21.common.classTools import tempAttribute | ||
from music21 import environment | ||
from music21 import style | ||
from music21 import spanner | ||
from music21 import style | ||
|
||
if t.TYPE_CHECKING: | ||
from music21 import interval | ||
|
||
|
||
environLocal = environment.Environment('articulations') | ||
|
@@ -582,10 +580,47 @@ 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 | ||
''' | ||
Bend indication for fretted instruments | ||
|
||
Bend in musicxml | ||
|
||
Number is an identifier for the articulation. Defaults to 0. | ||
|
||
BendAlter is the interval of the bend in number of semitones, | ||
bend-alter in musicxml. Defaults to None. | ||
|
||
PreBend indicates if the string is bent before | ||
the onset of the note. Defaults to False. | ||
|
||
Release is the quarterLength value from the start | ||
of the note for releasing the bend, if Any. Defaults to None. | ||
|
||
WithBar indicates if the bend is done using a whammy bar movement. Defaults to False. | ||
|
||
>>> fb = articulations.FretBend(bendAlter=interval.ChromaticInterval(-2), release=0.5) | ||
>>> fb | ||
<music21.articulations.FretBend 0> | ||
>>> fb.preBend | ||
False | ||
>>> fb.withBar | ||
False | ||
>>> fb.bendAlter | ||
<music21.interval.ChromaticInterval -2> | ||
>>> fb.release | ||
0.5 | ||
''' | ||
bendAlter: interval.IntervalBase | None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we need to have a number of semitones, maybe make this I'm not a guitar player so forgive me, but is there a concept of an indefinitely sized bend upwards or downwards? where the direction is known but not the size? (this could be something to put in later) |
||
preBend: bool | ||
release: float | None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of float, type as OffsetQL which is |
||
withBar: bool | ||
|
||
def __init__(self, number=0, bendAlter=None, preBend=False, release=None, withBar=False, **keywords): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type needs to be repeated here. Also make all but number keyword only so people don't have the remember the order.
|
||
super().__init__(**keywords) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bug. needs to be |
||
self.bendAlter = bendAlter | ||
self.preBend = preBend | ||
self.release = release | ||
self.withBar = withBar | ||
|
||
class FretTap(FretIndication): | ||
pass | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,7 +65,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') | ||
|
@@ -5411,11 +5411,18 @@ def articulationToXmlTechnical(self, articulationMark: articulations.Articulatio | |
>>> mxOther = MEX.articulationToXmlTechnical(g) | ||
>>> MEX.dump(mxOther) | ||
<other-technical>unda maris</other-technical> | ||
|
||
Same with technical marks not yet supported. | ||
TODO: support HammerOn, PullOff, Hole, Arrow. | ||
|
||
>>> h = articulations.HammerOn() | ||
>>> mxOther = MEX.articulationToXmlTechnical(h) | ||
>>> MEX.dump(mxOther) | ||
<other-technical /> | ||
''' | ||
# these technical have extra information | ||
# TODO: hammer-on | ||
# TODO: pull-off | ||
# TODO: bend | ||
# TODO: hole | ||
# TODO: arrow | ||
musicXMLTechnicalName = None | ||
|
@@ -5427,7 +5434,7 @@ def articulationToXmlTechnical(self, articulationMark: articulations.Articulatio | |
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) | ||
|
@@ -5461,7 +5468,8 @@ def articulationToXmlTechnical(self, articulationMark: articulations.Articulatio | |
if t.TYPE_CHECKING: | ||
assert isinstance(articulationMark, articulations.FretIndication) | ||
mxTechnicalMark.text = str(articulationMark.number) | ||
|
||
if musicXMLTechnicalName == 'bend': | ||
self.setBend(mxTechnicalMark, articulationMark) | ||
# 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': | ||
|
@@ -5477,6 +5485,46 @@ def articulationToXmlTechnical(self, articulationMark: articulations.Articulatio | |
# mxArticulations.append(mxArticulationMark) | ||
return mxTechnicalMark | ||
|
||
@staticmethod | ||
def setBend(mxh: Element, bend: articulations.FretBend) -> None: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks great! |
||
''' | ||
Sets the bend-alter SubElement and the pre-bend, | ||
release and with-bar SubElements when present. | ||
|
||
Called from articulationToXmlTechnical | ||
|
||
>>> MEXClass = musicxml.m21ToXml.MeasureExporter | ||
|
||
>>> a = articulations.FretBend() | ||
|
||
>>> from xml.etree.ElementTree import Element | ||
>>> mxh = Element('bend') | ||
|
||
>>> MEXClass.setBend(mxh, a) | ||
>>> MEXClass.dump(mxh) | ||
<bend> | ||
<bend-alter></bend-alter> | ||
</bend> | ||
''' | ||
bendAlterSubElement = SubElement(mxh, 'bend-alter') | ||
alter = bend.bendAlter | ||
if alter is not None: | ||
# musicxml expects a number of semitones but not sure how to get it | ||
# from a GeneralInterval | ||
pass | ||
Comment on lines
+5512
to
+5514
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. now with the change in typing above this can be possible. |
||
if bend.preBend: | ||
SubElement(mxh, 'pre-bend') | ||
if bend.release is not None: | ||
# Specifies where the release starts in terms of | ||
# divisions relative to the current note. | ||
releaseSubElement = SubElement(mxh, 'release') | ||
quarterLengthValue = bend.release | ||
divisionsValue = defaults.divisionsPerQuarter * quarterLengthValue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Musicxml "prefers" that offset be an integer: |
||
releaseSubElement.set('offset', str(divisionsValue)) | ||
if bend.withBar is not None: | ||
withBarSubElement = SubElement(mxh, 'with-bar') | ||
withBarSubElement.text = str(bend.withBar) | ||
|
||
@staticmethod | ||
def setHarmonic(mxh: Element, harm: articulations.StringHarmonic) -> None: | ||
# noinspection PyShadowingNames | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,12 +67,12 @@ | |
('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), | ||
('bend', articulations.FretBend), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this line appears to be added twice? (see line 70) |
||
('tap', articulations.FretTap), | ||
('fret', articulations.FretIndication), | ||
('heel', articulations.OrganHeel), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3861,17 +3861,32 @@ 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': | ||
self.setBend(mxObj, tech) | ||
# 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: | ||
environLocal.printDebug(f'Cannot translate {tag} in {mxObj}.') | ||
return None | ||
|
||
@staticmethod | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not going to be able to be a staticmethod -- see below. |
||
def setBend(mxh, bend): | ||
alter = mxh.find('bend-alter') | ||
if alter is not None: | ||
if alter.text is not None: | ||
bend.bendAlter = interval.Interval(int(alter.text)) | ||
if mxh.find('pre-bend') is not None: | ||
bend.preBend = True | ||
if mxh.find('release') is not None: | ||
try: | ||
divisions = float(mxh.find('release').get('offset')) | ||
bend.release = divisions / defaults.divisionsPerQuarter | ||
Comment on lines
+3885
to
+3886
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In writing to musicxml, music21 uses defaults.divisionsPerQuarter. But in reading from musicxml, we need to use the divisions defined earlier in the score, which is in self.divisions on the measure parser. Write
opFrac will convert it to Fraction(1, 3) if it's released within a triplet, etc. and leave it alone as 0.5 if it's a regular eighth note, etc. |
||
except (ValueError, TypeError) as unused_err: | ||
bend.release = 0.0 | ||
|
||
@staticmethod | ||
def setHarmonic(mxh, harm): | ||
''' | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add number to this to show that it appears in the fb representation below.