Skip to content

Commit

Permalink
Merge pull request #1695 from TimFelixBeyer/dissonance-score-fix
Browse files Browse the repository at this point in the history
Make `_dissonanceScore` independent of octaves
  • Loading branch information
mscuthbert authored Apr 29, 2024
2 parents e532560 + 6371669 commit 3fae7a1
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 @@ -577,17 +580,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 @@ -607,7 +613,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 @@ -617,7 +623,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 3fae7a1

Please sign in to comment.