diff --git a/music21/scale/__init__.py b/music21/scale/__init__.py index b48563c63..ebb66478e 100644 --- a/music21/scale/__init__.py +++ b/music21/scale/__init__.py @@ -557,7 +557,7 @@ def getPitchFromNodeDegree(self, alteredDegrees=self._alteredDegrees, equateTermini=equateTermini ) - return copy.deepcopy(post) + return post def realizePitchByDegree(self, pitchReference: _PitchOrStr, @@ -654,7 +654,7 @@ def getNewTonicPitch(self, maxPitch=maxPitch, alteredDegrees=self._alteredDegrees ) - return copy.deepcopy(post) + return post # -------------------------------------------------------------------------- diff --git a/music21/scale/intervalNetwork.py b/music21/scale/intervalNetwork.py index 4b7e5eaec..d7e97b2ad 100644 --- a/music21/scale/intervalNetwork.py +++ b/music21/scale/intervalNetwork.py @@ -2707,15 +2707,16 @@ def getPitchFromNodeDegree( # environLocal.printDebug(['comparing', realizedNId, # 'nodeTargetId', nodeTargetId]) + # realizedPitch may be a cached realization: hand back a copy if realizedNId == nodeTargetId.id: - return realizedPitch[i] + return copy.deepcopy(realizedPitch[i]) # NOTE: this condition may be too generous, and was added to solve # a non-tracked problem. # only match this generously if we are equating termini if equateTermini: if ((realizedNId in (Terminus.HIGH, Terminus.LOW)) and (nodeTargetId.id in (Terminus.HIGH, Terminus.LOW))): - return realizedPitch[i] + return copy.deepcopy(realizedPitch[i]) # environLocal.printDebug(['getPitchFromNodeDegree() on trial', trial, ', # failed to find node', nodeTargetId]) diff --git a/music21/scale/test_intervalNetwork.py b/music21/scale/test_intervalNetwork.py index da1f3061b..6e2743521 100644 --- a/music21/scale/test_intervalNetwork.py +++ b/music21/scale/test_intervalNetwork.py @@ -627,6 +627,28 @@ def test_realize_descending_reversed_cached(self): self.assertEqual(descending_melodic_minor_reversed[0].nameWithOctave, 'C4') # was B-4 self.assertEqual(descending_melodic_minor_reversed[-1].nameWithOctave, 'B-4') # was C4 + def test_get_pitch_from_node_degree_returns_a_new_pitch(self): + ''' + A realization may be held in _ascendingCache or _descendingCache, so + the pitch taken out of one has to be the caller's own -- otherwise + writing to it edits the cached scale. + ''' + net = IntervalNetwork() + net.fillBiDirectedEdges(['M2', 'M2', 'm2', 'M2', 'M2', 'M2', 'm2']) + + first = net.getPitchFromNodeDegree('c4', 1, 1) + self.assertEqual(first.nameWithOctave, 'C4') + first.octave = 1 + + again = net.getPitchFromNodeDegree('c4', 1, 1) + self.assertEqual(again.nameWithOctave, 'C4') + + def test_next_pitch_does_not_move_a_cached_degree(self): + sc = scale.RagAsawari('c4') + self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') + self.assertEqual(str(sc.nextPitch('c1', Direction.ASCENDING)), 'D1') + self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') + # ------------------------------------------------------------------------------ if __name__ == '__main__': diff --git a/music21/scale/test_scale_main.py b/music21/scale/test_scale_main.py index bc3826c0f..ded1bd6d9 100644 --- a/music21/scale/test_scale_main.py +++ b/music21/scale/test_scale_main.py @@ -456,7 +456,7 @@ def testRagAsawari(self): getNeighbor=Direction.DESCENDING)), 'F1') - self.assertEqual(str(sc.pitchFromDegree(1)), 'C1') + self.assertEqual(str(sc.pitchFromDegree(1)), 'C4') # there is no third step in ascending form self.assertEqual(str(sc.pitchFromDegree(3)), 'None') self.assertEqual(str(sc.pitchFromDegree(3, direction=Direction.DESCENDING)),