Fix voice crossing detection

- Replace index-based voice crossing check with pitch ordering comparison
- Now properly detects when voice ordering changes between chords
- This was causing edges with voice crossing to be incorrectly allowed
This commit is contained in:
Michael Winter 2026-03-13 01:25:18 +01:00
parent c63b6cb1d4
commit ba3ded82d8

View file

@ -540,11 +540,20 @@ class HarmonicSpace:
cents = abs(src_pitch.to_cents() - dst_pitch.to_cents())
cent_diffs.append(cents)
# Check voice_crossing: True if any voice moves to different position
num_voices = len(c1.pitches)
voice_crossing = not all(
movements.get(i, i) == i for i in range(num_voices)
)
# Check voice_crossing: compare pitch ordering before/after
# Voice crossing = True if ordering changes (e.g., bass below tenor -> tenor below bass)
source = list(c1.pitches)
ordered_source = sorted(source, key=lambda p: p.to_fraction())
source_order = [ordered_source.index(p) for p in source]
# Destination pitches: transpose back to get sounding pitches
destination = [
c2_transposed.pitches[movements[p]] for p in range(len(source))
]
ordered_destination = sorted(destination, key=lambda p: p.to_fraction())
destination_order = [ordered_destination.index(p) for p in destination]
voice_crossing = source_order != destination_order
# Check is_directly_tunable: changing pitches are adjacent to staying pitch
is_directly_tunable = self._is_directly_tunable(