Skip to content

Commit

Permalink
Make _dissonanceScore independent of octaves
Browse files Browse the repository at this point in the history
Fixes #1689 where the spelling of a pitch was dependent on its octave.
  • Loading branch information
TimFelixBeyer authored Feb 27, 2024
1 parent 0927e39 commit 6371669
Showing 1 changed file with 13 additions and 7 deletions.
20 changes: 13 additions & 7 deletions music21/pitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,10 @@ def _convertHarmonicToCents(value: int|float) -> int:
# -----------------------------------------------------------------------------


def _dissonanceScore(pitches, smallPythagoreanRatio=True, accidentalPenalty=True, triadAward=True):
def _dissonanceScore(pitches: list[Pitch],
smallPythagoreanRatio: bool = True,
accidentalPenalty: bool = True,
triadAward: bool = True):
r'''
Calculates the 'dissonance' of a list of pitches based on three criteria:
it is considered more consonant if 1. the numerator and denominator of the
Expand All @@ -575,17 +578,20 @@ def _dissonanceScore(pitches, smallPythagoreanRatio=True, accidentalPenalty=True

if smallPythagoreanRatio or triadAward:
try:
intervals = [interval.Interval(noteStart=p1, noteEnd=p2)
for p1, p2 in itertools.combinations(pitches, 2)]
intervals = []
for p1, p2 in itertools.combinations(pitches, 2):
p2 = copy.deepcopy(p2)
p2.octave = None
this_interval = interval.Interval(noteStart=p1, noteEnd=p2)
intervals.append(this_interval)
except interval.IntervalException:
return math.inf
if smallPythagoreanRatio:
# score_ratio = Pythagorean ratio complexity per pitch
for this_interval in intervals:
# does not accept weird intervals, e.g. with semitones
ratio = interval.intervalToPythagoreanRatio(this_interval)
# d2 is 1.0
penalty = math.log(ratio.numerator * ratio.denominator / ratio) * 0.03792663444
penalty = math.log(ratio.denominator) * 0.07585326888
score_ratio += penalty

score_ratio /= len(pitches)
Expand All @@ -605,7 +611,7 @@ def _dissonanceScore(pitches, smallPythagoreanRatio=True, accidentalPenalty=True
+ accidentalPenalty + triadAward)


def _bruteForceEnharmonicsSearch(oldPitches, scoreFunc=_dissonanceScore):
def _bruteForceEnharmonicsSearch(oldPitches: list[Pitch], scoreFunc=_dissonanceScore):
'''
A brute-force way of simplifying -- useful if there are fewer than 5 pitches
'''
Expand All @@ -615,7 +621,7 @@ def _bruteForceEnharmonicsSearch(oldPitches, scoreFunc=_dissonanceScore):
return oldPitches[:1] + list(newPitches)


def _greedyEnharmonicsSearch(oldPitches, scoreFunc=_dissonanceScore):
def _greedyEnharmonicsSearch(oldPitches: list[Pitch], scoreFunc=_dissonanceScore):
newPitches = oldPitches[:1]
for oldPitch in oldPitches[1:]:
candidates = [oldPitch] + oldPitch.getAllCommonEnharmonics()
Expand Down

0 comments on commit 6371669

Please sign in to comment.