Add harmonic compactness factor
- Compute sum of harmonic distances between all pitch pairs - Lower sum = more compact chord = higher probability - Uses pitch_difference() to get diff, then log2(n*d) for distance - Add CLI arg: --weight-harmonic-compactness
This commit is contained in:
parent
65e3a64a0c
commit
62e6a75f4f
|
|
@ -435,6 +435,12 @@ def main():
|
|||
default=5,
|
||||
help="Maximum consecutive selections before blocking (default: 5)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--weight-harmonic-compactness",
|
||||
type=float,
|
||||
default=0,
|
||||
help="Weight for harmonic compactness factor - favors compact chords (0=disabled, default: 0)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--weight-target-register",
|
||||
type=float,
|
||||
|
|
@ -673,6 +679,7 @@ def main():
|
|||
weights_config["weight_dca_voice_movement"] = args.weight_dca_voice_movement
|
||||
weights_config["weight_rgr_voice_movement"] = args.weight_rgr_voice_movement
|
||||
weights_config["rgr_voice_movement_threshold"] = args.rgr_voice_movement_threshold
|
||||
weights_config["weight_harmonic_compactness"] = args.weight_harmonic_compactness
|
||||
|
||||
# Target register
|
||||
if args.target_register != 0:
|
||||
|
|
|
|||
40
src/path.py
40
src/path.py
|
|
@ -142,6 +142,10 @@ class Path:
|
|||
change_before,
|
||||
self.weights_config,
|
||||
),
|
||||
"harmonic_compactness": self._factor_harmonic_compactness(
|
||||
destination_chord,
|
||||
self.weights_config,
|
||||
),
|
||||
"target_register": self._factor_target_register(
|
||||
path_chords,
|
||||
destination_chord,
|
||||
|
|
@ -207,6 +211,7 @@ class Path:
|
|||
hamiltonian_values = [c.scores.get("dca_hamiltonian", 0) for c in candidates]
|
||||
dca_values = [c.scores.get("dca_voice_movement", 0) for c in candidates]
|
||||
rgr_values = [c.scores.get("rgr_voice_movement", 0) for c in candidates]
|
||||
compact_values = [c.scores.get("harmonic_compactness", 0) for c in candidates]
|
||||
target_values = [c.scores.get("target_register", 0) for c in candidates]
|
||||
|
||||
def sum_normalize(values: list) -> list | None:
|
||||
|
|
@ -221,6 +226,7 @@ class Path:
|
|||
hamiltonian_norm = sum_normalize(hamiltonian_values)
|
||||
dca_norm = sum_normalize(dca_values)
|
||||
rgr_norm = sum_normalize(rgr_values)
|
||||
compact_norm = sum_normalize(compact_values)
|
||||
target_norm = sum_normalize(target_values)
|
||||
|
||||
weights = []
|
||||
|
|
@ -250,6 +256,8 @@ class Path:
|
|||
w *= dca_norm[i] * config.get("weight_dca_voice_movement", 1)
|
||||
if rgr_norm:
|
||||
w *= rgr_norm[i] * config.get("weight_rgr_voice_movement", 1)
|
||||
if compact_norm:
|
||||
w *= compact_norm[i] * config.get("weight_harmonic_compactness", 1)
|
||||
if target_norm:
|
||||
w *= target_norm[i] * config.get("weight_target_register", 1)
|
||||
|
||||
|
|
@ -262,6 +270,7 @@ class Path:
|
|||
"dca_hamiltonian": hamiltonian_norm[i] if hamiltonian_norm else None,
|
||||
"dca_voice_movement": dca_norm[i] if dca_norm else None,
|
||||
"rgr_voice_movement": rgr_norm[i] if rgr_norm else None,
|
||||
"harmonic_compactness": compact_norm[i] if compact_norm else None,
|
||||
"target_register": target_norm[i] if target_norm else None,
|
||||
}
|
||||
|
||||
|
|
@ -413,6 +422,32 @@ class Path:
|
|||
|
||||
return sum_repeating
|
||||
|
||||
def _factor_harmonic_compactness(
|
||||
self,
|
||||
destination_chord: Chord,
|
||||
config: dict,
|
||||
) -> float:
|
||||
"""Returns probability based on sum of harmonic distances between all pairs.
|
||||
|
||||
Lower sum = more compact = higher probability.
|
||||
"""
|
||||
if config.get("weight_harmonic_compactness", 0) == 0:
|
||||
return 1.0
|
||||
|
||||
pitches = destination_chord.pitches
|
||||
if len(pitches) < 2:
|
||||
return 1.0
|
||||
|
||||
total_distance = 0
|
||||
for i in range(len(pitches)):
|
||||
for j in range(i + 1, len(pitches)):
|
||||
diff = pitches[i].pitch_difference(pitches[j])
|
||||
ratio = diff.to_fraction()
|
||||
distance = math.log2(ratio.numerator * ratio.denominator)
|
||||
total_distance += distance
|
||||
|
||||
return 1.0 / (1.0 + total_distance)
|
||||
|
||||
def _factor_target_register(
|
||||
self,
|
||||
path_chords: list[Chord],
|
||||
|
|
@ -543,6 +578,7 @@ class Path:
|
|||
"dca_hamiltonian": 0.0,
|
||||
"dca_voice_movement": 0.0,
|
||||
"rgr_voice_movement": 0.0,
|
||||
"harmonic_compactness": 0.0,
|
||||
"target_register": 0.0,
|
||||
}
|
||||
|
||||
|
|
@ -551,6 +587,7 @@ class Path:
|
|||
w_hamiltonian = weights.get("weight_dca_hamiltonian", 1)
|
||||
w_dca = weights.get("weight_dca_voice_movement", 1)
|
||||
w_rgr = weights.get("weight_rgr_voice_movement", 1)
|
||||
w_compact = weights.get("weight_harmonic_compactness", 1)
|
||||
w_target = weights.get("weight_target_register", 1)
|
||||
|
||||
for step in self.steps:
|
||||
|
|
@ -569,6 +606,9 @@ class Path:
|
|||
influence["rgr_voice_movement"] += (
|
||||
norm.get("rgr_voice_movement") or 0
|
||||
) * w_rgr
|
||||
influence["harmonic_compactness"] += (
|
||||
norm.get("harmonic_compactness") or 0
|
||||
) * w_compact
|
||||
influence["target_register"] += (
|
||||
norm.get("target_register") or 0
|
||||
) * w_target
|
||||
|
|
|
|||
Loading…
Reference in a new issue