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

Fix #1689: Make _dissonanceScore independent of octaves #1690

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 2 additions & 4 deletions music21/analysis/reduceChords.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,7 @@ def procedure(timespan):
)
print(msg)
# raise ChordReducerException(msg)
if offset < previousTimespan.endTime:
offset = previousTimespan.endTime
offset = min(offset, previousTimespan.endTime)
scoreTree.removeTimespan(group[0])
subtree.removeTimespan(group[0])
newTimespan = group[0].new(offset=offset)
Expand Down Expand Up @@ -542,8 +541,7 @@ def reduceMeasureToNChords(
measureObject.flatten().notes,
weightAlgorithm,
)
if maximumNumberOfChords > len(chordWeights):
maximumNumberOfChords = len(chordWeights)
maximumNumberOfChords = min(maximumNumberOfChords, len(chordWeights))
sortedChordWeights = sorted(
chordWeights,
key=chordWeights.get,
Expand Down
5 changes: 2 additions & 3 deletions music21/analysis/reduceChordsOld.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,14 @@ def reduceMeasureToNChords(self,
4.0
'''
from music21 import note
if measureObj.isFlat is False:
if not measureObj.isFlat:
mObj = measureObj.flatten().notes.stream()
else:
mObj = measureObj.notes.stream()

chordWeights = self.computeMeasureChordWeights(mObj, weightAlgorithm)

if numChords > len(chordWeights):
numChords = len(chordWeights)
numChords = min(numChords, len(chordWeights))

maxNChords = sorted(chordWeights, key=chordWeights.get, reverse=True)[:numChords]
if not maxNChords:
Expand Down
9 changes: 2 additions & 7 deletions music21/analysis/windowed.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,8 @@ def analyze(self, windowSize, windowType='overlap'):
elif windowType == 'noOverlap':
start = 0
end = start + windowSize
i = 0
while True:
if end >= len(self._windowedStream):
end = len(self._windowedStream)
for i in range(windowCount):
end = min(end, len(self._windowedStream))

current = stream.Stream()
for j in range(start, end):
Expand All @@ -210,9 +208,6 @@ def analyze(self, windowSize, windowType='overlap'):

start = end
end = start + windowSize
i += 1
if i >= windowCount:
break

elif windowType == 'adjacentAverage':
# first get overlapping windows
Expand Down
23 changes: 7 additions & 16 deletions music21/braille/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,7 @@ def recenterHeadings(self):
if self.allLines[i].isHeading:
break
lineLength = self.allLines[i].textLocation
if lineLength > maxLineLength:
maxLineLength = lineLength
maxLineLength = max(maxLineLength, lineLength)
for j in range(indexStart, indexFinal):
brailleTextLine = self.allLines[j]
lineStrToCenter = str(brailleTextLine)
Expand Down Expand Up @@ -565,12 +564,10 @@ def insert(self, textLocation, text):
'''
if not self.canInsert(textLocation, text):
raise BrailleTextException('Text cannot be inserted at specified location.')
self.textLocation = textLocation
for char in list(text):
self.allChars[self.textLocation] = char
self.textLocation += 1
if self.textLocation > self.highestUsedLocation:
self.highestUsedLocation = self.textLocation
for i, char in enumerate(text, start=textLocation):
self.allChars[i] = char
self.textLocation = textLocation + len(text)
self.highestUsedLocation = max(self.highestUsedLocation, self.textLocation)

def canAppend(self, text, addSpace=True):
'''
Expand Down Expand Up @@ -600,15 +597,9 @@ def canAppend(self, text, addSpace=True):
>>> btl.canAppend('1234', addSpace=False)
False
'''
if self.highestUsedLocation > self.textLocation:
searchLocation = self.highestUsedLocation
else:
searchLocation = self.textLocation
searchLocation = max(self.highestUsedLocation, self.textLocation)
addSpaceAmount = 1 if addSpace else 0
if (searchLocation + len(text) + addSpaceAmount) > self.lineLength:
return False
else:
return True
return (searchLocation + len(text) + addSpaceAmount) <= self.lineLength

def canInsert(self, textLocation, text):
'''
Expand Down
3 changes: 1 addition & 2 deletions music21/common/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ class RelativeCounter(collections.Counter):

def __iter__(self):
sortedKeys = sorted(super().__iter__(), key=lambda x: self[x], reverse=True)
for k in sortedKeys:
yield k
yield from sortedKeys

def items(self):
for k in self:
Expand Down
3 changes: 1 addition & 2 deletions music21/features/jSymbolic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3016,8 +3016,7 @@ def process(self):
for p in c.pitches:
for gSub in p.groups:
g.append(gSub) # add to temporary group; will act as a set
if len(g) > found:
found = len(g)
found = max(found, len(g))
self.feature.vector[0] = found


Expand Down
5 changes: 1 addition & 4 deletions music21/figuredBass/realizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,7 @@ def addLyricsToBassNote(bassNote, notationString=None):
n = notation.Notation(notationString)
if not n.figureStrings:
return
maxLength = 0
for fs in n.figureStrings:
if len(fs) > maxLength:
maxLength = len(fs)
maxLength = max([len(fs) for fs in n.figureStrings])
for fs in n.figureStrings:
spacesInFront = ''
for i in range(maxLength - len(fs)):
Expand Down
3 changes: 1 addition & 2 deletions music21/humdrum/spineParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,7 @@ def parseEventListFromDataStream(self, dataStream=None):
self.eventList.append(GlobalCommentLine(self.parsePositionInStream, line))
else:
thisLine = SpineLine(self.parsePositionInStream, line)
if thisLine.numSpines > self.maxSpines:
self.maxSpines = thisLine.numSpines
self.maxSpines = max(self.maxSpines, thisLine.numSpines)
self.eventList.append(thisLine)
self.parsePositionInStream += 1
self.fileLength = self.parsePositionInStream
Expand Down
3 changes: 1 addition & 2 deletions music21/musicxml/m21ToXml.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,8 +1551,7 @@ def setPartsAndRefStream(self) -> None:
else:
innerStream.transferOffsetToElements()
ht = innerStream.highestTime
if ht > self.highestTime:
self.highestTime = ht
self.highestTime = max(self.highestTime, ht)
self.refStreamOrTimeRange = [0.0, self.highestTime]
self.parts = list(s.parts)

Expand Down
3 changes: 1 addition & 2 deletions music21/musicxml/xmlToM21.py
Original file line number Diff line number Diff line change
Expand Up @@ -1967,8 +1967,7 @@ def xmlMeasureToMeasure(self, mxMeasure: ET.Element) -> stream.Measure:

self.lastMeasureParser = measureParser

if measureParser.staves > self.maxStaves:
self.maxStaves = measureParser.staves
self.maxStaves = max(self.maxStaves, measureParser.staves)

if measureParser.transposition is not None:
self.updateTransposition(measureParser.transposition)
Expand Down
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
12 changes: 6 additions & 6 deletions music21/scale/intervalNetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,8 +828,8 @@ def degreeMin(self):
for n in self.nodes.values():
if x is None:
x = n.degree
if n.degree < x:
x = n.degree
else:
x = min(x, n.degree)
return x

@property
Expand All @@ -847,8 +847,8 @@ def degreeMax(self):
for n in self.nodes.values():
if x is None:
x = n.degree
if n.degree > x:
x = n.degree
else:
x = max(x, n.degree)
return x

@property
Expand All @@ -870,8 +870,8 @@ def degreeMaxUnique(self):
continue
if x is None:
x = n.degree
if n.degree > x:
x = n.degree
else:
x = max(x, n.degree)
return x

@property
Expand Down
11 changes: 3 additions & 8 deletions music21/stream/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2828,8 +2828,7 @@ def insertAndShift(self, offsetOrItemOrList, itemOrNone=None):
o = insertList[i]
e = insertList[i + 1]
qL = e.duration.quarterLength
if o + qL > highestTimeInsert:
highestTimeInsert = o + qL
highestTimeInsert = max(highestTimeInsert, o + qL)
if lowestOffsetInsert is None or o < lowestOffsetInsert:
lowestOffsetInsert = o
i += 2
Expand Down Expand Up @@ -8425,8 +8424,7 @@ def highestTime(self):
for e in self._elements:
candidateOffset = (self.elementOffset(e)
+ e.duration.quarterLength)
if candidateOffset > highestTimeSoFar:
highestTimeSoFar = candidateOffset
highestTimeSoFar = max(highestTimeSoFar, candidateOffset)
self._cache['HighestTime'] = opFrac(highestTimeSoFar)
return self._cache['HighestTime']

Expand Down Expand Up @@ -11183,10 +11181,7 @@ def makeVoices(self, *, inPlace=False, fillGaps=True):
# environLocal.printDebug(['makeVoices(): olDict', olDict])
# find the max necessary voices by finding the max number
# of elements in each group; these may not all be necessary
maxVoiceCount = 1
for group in olDict.values():
if len(group) > maxVoiceCount:
maxVoiceCount = len(group)
maxVoiceCount = max([len(group) for group in olDict.values()] + [1])
if maxVoiceCount == 1: # nothing to do here
if not inPlace:
return returnObj
Expand Down
3 changes: 1 addition & 2 deletions music21/stream/iterator.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,7 @@ def __contains__(self, item):
def __reversed__(self):
me = self.matchingElements()
me.reverse()
for item in me:
yield item
yield from me

def clone(self: StreamIteratorType) -> StreamIteratorType:
'''
Expand Down
3 changes: 1 addition & 2 deletions music21/stream/makeNotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,7 @@ def makeMeasures(
refStreamHighestTime = refStreamOrTimeRange.highestTime
else: # assume it's a list
refStreamHighestTime = max(refStreamOrTimeRange)
if refStreamHighestTime > oMax:
oMax = refStreamHighestTime
oMax = max(oMax, refStreamHighestTime)

# create a stream of measures to contain the offsets range defined
# create as many measures as needed to fit in oMax
Expand Down
9 changes: 3 additions & 6 deletions music21/test/treeYield.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,14 @@ def run(self, obj, memo=None):
dictTuple = ('dict', keyX)
self.stackVals.append(dictTuple)
x = obj[keyX]
for z in self.run(x, memo=memo):
yield z
yield from self.run(x, memo=memo)
self.stackVals.pop()

elif tObj in [list, tuple]:
for i, x in enumerate(obj):
listTuple = ('listLike', i)
self.stackVals.append(listTuple)
for z in self.run(x, memo=memo):
yield z
yield from self.run(x, memo=memo)
self.stackVals.pop()

else: # objects or uncaught types...
Expand All @@ -95,8 +93,7 @@ def run(self, obj, memo=None):
objTuple = ('getattr', x)
self.stackVals.append(objTuple)
try:
for z in self.run(gotValue, memo=memo):
yield z
yield from self.run(gotValue, memo=memo)
except RuntimeError:
raise ValueError(f'Maximum recursion on:\n{self.currentLevel()}')
self.stackVals.pop()
Expand Down
6 changes: 2 additions & 4 deletions music21/tree/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,12 +519,10 @@ def __iter__(self):
def recurse(node):
if node is not None:
if node.leftChild is not None:
for n in recurse(node.leftChild):
yield n
yield from recurse(node.leftChild)
yield node
if node.rightChild is not None:
for n in recurse(node.rightChild):
yield n
yield from recurse(node.rightChild)
return recurse(self.rootNode)

def populateFromSortedList(self, listOfTuples):
Expand Down
24 changes: 8 additions & 16 deletions music21/tree/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,18 +273,14 @@ def updateEndTimes(self):
leftChild = self.leftChild
if leftChild:
leftChild.updateEndTimes()
if leftChild.endTimeLow < endTimeLow:
endTimeLow = leftChild.endTimeLow
if endTimeHigh < leftChild.endTimeHigh:
endTimeHigh = leftChild.endTimeHigh
endTimeLow = min(endTimeLow, leftChild.endTimeLow)
endTimeHigh = max(endTimeHigh, leftChild.endTimeHigh)

rightChild = self.rightChild
if rightChild:
rightChild.updateEndTimes()
if rightChild.endTimeLow < endTimeLow:
endTimeLow = rightChild.endTimeLow
if endTimeHigh < rightChild.endTimeHigh:
endTimeHigh = rightChild.endTimeHigh
endTimeLow = min(endTimeLow, rightChild.endTimeLow)
endTimeHigh = max(endTimeHigh, rightChild.endTimeHigh)
self.endTimeLow = endTimeLow
self.endTimeHigh = endTimeHigh

Expand Down Expand Up @@ -515,18 +511,14 @@ def updateEndTimes(self):
leftChild = self.leftChild
if leftChild:
leftChild.updateEndTimes()
if leftChild.endTimeLow < endTimeLow:
endTimeLow = leftChild.endTimeLow
if endTimeHigh < leftChild.endTimeHigh:
endTimeHigh = leftChild.endTimeHigh
endTimeLow = min(endTimeLow, leftChild.endTimeLow)
endTimeHigh = max(endTimeHigh, leftChild.endTimeHigh)

rightChild = self.rightChild
if rightChild:
rightChild.updateEndTimes()
if rightChild.endTimeLow < endTimeLow:
endTimeLow = rightChild.endTimeLow
if endTimeHigh < rightChild.endTimeHigh:
endTimeHigh = rightChild.endTimeHigh
endTimeLow = min(endTimeLow, rightChild.endTimeLow)
endTimeHigh = max(endTimeHigh, rightChild.endTimeHigh)
self.endTimeLow = endTimeLow
self.endTimeHigh = endTimeHigh

Expand Down
Loading
Loading