Fix transcriber to compute ref and dim_diff from chord data

- Add _is_adjacent() to check if pitches differ by ±1 in one dimension
- Add _compute_dim_diff() to compute prime * direction for dimension changes
- Add _find_ref_and_dim_diff() to find the ref voice and dim_diff for changed pitches
- Update output_chords_to_music_data() to compute ref and dim_diff by comparing consecutive chords
- Update format_dim_diff() to include dim_diff == 0 check and fix ref mapping for 4 voices
This commit is contained in:
Michael Winter 2026-03-23 19:12:22 +01:00
parent d0bd15574d
commit 658837b83e

View file

@ -155,7 +155,7 @@ def format_cents_deviation(freq):
def format_dim_diff(dim_diff, ref): def format_dim_diff(dim_diff, ref):
"""Format dimensional difference markup.""" """Format dimensional difference markup."""
if dim_diff is None or ref is None or ref < 0: if dim_diff is None or ref is None or ref < 0 or dim_diff == 0:
return "" return ""
diff_str = str(abs(dim_diff)) diff_str = str(abs(dim_diff))
@ -164,7 +164,8 @@ def format_dim_diff(dim_diff, ref):
elif dim_diff < 0: elif dim_diff < 0:
diff_str += "" diff_str += ""
ref_name = ["III", "II", "I"][ref] if 0 <= ref <= 2 else "" ref_names = ["IV", "III", "II", "I"]
ref_name = ref_names[ref] if 0 <= ref <= 3 else ""
return f'_\\markup {{ \\lower #3 \\pad-markup #0.2 \\concat{{ "{ref_name}"\\normal-size-super " {diff_str}" }} }}' return f'_\\markup {{ \\lower #3 \\pad-markup #0.2 \\concat{{ "{ref_name}"\\normal-size-super " {diff_str}" }} }}'
@ -332,6 +333,60 @@ def generate_parts(music_data, name, output_dir="lilypond"):
print(f"Generated: {part_file}") print(f"Generated: {part_file}")
def _is_adjacent(hs1: tuple, hs2: tuple) -> bool:
"""Check if two hs_arrays are adjacent (differ by ±1 in exactly one dimension, excluding dim 0)."""
diff_count = 0
for i in range(1, len(hs1)):
diff = abs(hs1[i] - hs2[i])
if diff > 1:
return False
if diff == 1:
diff_count += 1
return diff_count == 1
def _compute_dim_diff(current: tuple, prev: tuple) -> int:
"""Compute dim_diff between two hs_arrays. Returns prime * direction."""
primes = [3, 5, 7]
for i in range(1, 4):
diff = current[i] - prev[i]
if diff == 1:
return primes[i - 1]
if diff == -1:
return -primes[i - 1]
return 0
def _find_ref_and_dim_diff(
current_hs: tuple, prev_chord: list, staying_voices: list
) -> tuple[int, int]:
"""Find ref (staying voice index) and dim_diff for a changed pitch.
Args:
current_hs: hs_array of current pitch
prev_chord: list of hs_arrays from previous chord
staying_voices: indices of voices that stay
Returns:
(ref, dim_diff) tuple
"""
if not staying_voices:
return -1, 0
adjacent = []
for idx in staying_voices:
prev_hs = prev_chord[idx]
if _is_adjacent(current_hs, prev_hs):
dim_diff = _compute_dim_diff(current_hs, prev_hs)
adjacent.append((idx, dim_diff))
if not adjacent:
return -1, 0
adjacent.sort(key=lambda x: abs(x[1]))
return adjacent[0]
def output_chords_to_music_data(chords, fundamental=55, chord_duration=4): def output_chords_to_music_data(chords, fundamental=55, chord_duration=4):
"""Convert output_chords.json format to generic music data. """Convert output_chords.json format to generic music data.
@ -350,20 +405,41 @@ def output_chords_to_music_data(chords, fundamental=55, chord_duration=4):
music_data = [[] for _ in range(num_voices)] music_data = [[] for _ in range(num_voices)]
prev_chord = None
for chord in chords: for chord in chords:
current_hs = [tuple(p["hs_array"]) for p in chord]
if prev_chord is None:
staying_voices = []
else:
staying_voices = [
i for i in range(num_voices) if current_hs[i] == prev_chord[i]
]
for voice_idx, pitch in enumerate(chord): for voice_idx, pitch in enumerate(chord):
if voice_idx >= num_voices: if voice_idx >= num_voices:
break break
frac = Fraction(pitch["fraction"]) frac = Fraction(pitch["fraction"])
freq = fundamental * float(frac) freq = fundamental * float(frac)
current_hs_array = current_hs[voice_idx]
ref = None if prev_chord is None:
ref = -1
dim_diff = 0 dim_diff = 0
elif current_hs_array == prev_chord[voice_idx]:
ref = -1
dim_diff = 0
else:
ref, dim_diff = _find_ref_and_dim_diff(
current_hs_array, prev_chord, staying_voices
)
event = [freq, chord_duration, ref, dim_diff] event = [freq, chord_duration, ref, dim_diff]
music_data[voice_idx].append(event) music_data[voice_idx].append(event)
prev_chord = current_hs
return music_data return music_data