Transpose

This commit is contained in:
khodowany
2026-09-16 21:33:34 -05:00
parent c6adff8b3c
commit 01c6d4e786
14 changed files with 526 additions and 50 deletions
+15 -7
View File
@@ -80,15 +80,20 @@ def shift_key(key: dict, steps: int, semitones: int) -> dict:
result['custom'] = True
return result
def transpose_document(document: dict, target_key_name: str) -> dict:
def transposition_interval(document: dict, target_key_name: str) -> tuple[int, int]:
metadata = document['metadata']
origin = metadata['key'].get('tonic') or metadata.get('transpose_origin')
if origin is None:
raise ValueError('keyless practice requires a vamp:tonic score property')
return interval_between(origin, parse_pitch_name(target_key_name))
def transpose_document(document: dict, target_key_name: str, *, octave_shift: int = 0) -> dict:
if type(octave_shift) is not int:
raise ValueError('octave adjustment must be an integer')
steps, semitones = transposition_interval(document, target_key_name)
output = copy.deepcopy(document)
metadata = output['metadata']
key = metadata['key']
origin = key.get('tonic') or metadata.get('transpose_origin')
if origin is None:
raise ValueError('keyless melody practice requires a vamp:tonic score property')
target = parse_pitch_name(target_key_name)
steps, semitones = interval_between(origin, target)
metadata['key'] = shift_key(key, steps, semitones)
metadata['key_signature'] = key_label(metadata['key'])
metadata['variant_tonic'] = target_key_name
@@ -106,7 +111,10 @@ def transpose_document(document: dict, target_key_name: str) -> dict:
h['symbol'] = harmony.symbol(h)
for voice in measure['voices']:
for note in voice['notes']:
note['pitches'] = [shift_pitch(p, steps, semitones) for p in note.get('pitches', [])]
try:
note['pitches'] = [shift_pitch(p, steps + 7 * octave_shift, semitones + 12 * octave_shift) for p in note.get('pitches', [])]
except ValueError as error:
raise ValueError(f'measure {measure.get("number", measure["id"])}, voice {voice["id"]}, event {note["id"]}: {error}') from error
return output
def concert_document(document: dict) -> dict: