@@ -4,7 +4,7 @@ import copy
|
||||
from dataclasses import dataclass
|
||||
|
||||
from generator import harmony
|
||||
from generator.model import LETTERS, NATURAL, key_from_fifths, key_label, parse_pitch_name, pitch_number
|
||||
from generator.model import LETTERS, NATURAL, alteration, key_from_fifths, key_label, parse_pitch_name, pitch_number, rational
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class KeyProfile:
|
||||
@@ -23,7 +23,7 @@ def find_key(name: str) -> KeyProfile:
|
||||
for fifths in range(-14, 15):
|
||||
key = key_from_fifths(fifths)
|
||||
if key['tonic'] == tonic:
|
||||
return KeyProfile(name, (NATURAL[tonic['letter']] + tonic['alter']) % 12, fifths)
|
||||
return KeyProfile(name, int(NATURAL[tonic['letter']] + alteration(tonic['alter'])) % 12, fifths)
|
||||
raise ValueError(f'unsupported key signature: {name}')
|
||||
|
||||
def signed_interval(source: KeyProfile, target: KeyProfile) -> int:
|
||||
@@ -31,7 +31,7 @@ def signed_interval(source: KeyProfile, target: KeyProfile) -> int:
|
||||
return value - 12 if value > 6 else value
|
||||
|
||||
def interval_between(source: dict, target: dict) -> tuple[int, int]:
|
||||
semitones = (NATURAL[target['letter']] + target['alter'] - NATURAL[source['letter']] - source['alter']) % 12
|
||||
semitones = (NATURAL[target['letter']] + alteration(target['alter']) - NATURAL[source['letter']] - alteration(source['alter'])) % 12
|
||||
if semitones > 6:
|
||||
semitones -= 12
|
||||
steps = (LETTERS.index(target['letter']) - LETTERS.index(source['letter'])) % 7
|
||||
@@ -41,16 +41,24 @@ def interval_between(source: dict, target: dict) -> tuple[int, int]:
|
||||
|
||||
def shift_pitch(pitch: dict, steps: int, semitones: int) -> dict:
|
||||
output = copy.deepcopy(pitch)
|
||||
if 'unpitched' in pitch:
|
||||
return output
|
||||
original_octave = pitch.get('octave', 4)
|
||||
diatonic = original_octave * 7 + LETTERS.index(pitch['letter']) + steps
|
||||
octave, index = divmod(diatonic, 7)
|
||||
output['letter'] = LETTERS[index]
|
||||
output['octave'] = octave
|
||||
output['alter'] = pitch_number({**pitch, 'octave': original_octave}) + semitones - (12 * (octave + 1) + NATURAL[output['letter']])
|
||||
output['alter'] = rational(pitch_number({**pitch, 'octave': original_octave}) + semitones - (12 * (octave + 1) + NATURAL[output['letter']]))
|
||||
if 'octave' not in pitch:
|
||||
output.pop('octave')
|
||||
if 'accidental_display' in output:
|
||||
output['accidental_display']['name'] = {-3: 'triple-flat', -2: 'flat-flat', -1: 'flat', 0: 'natural', 1: 'sharp', 2: 'double-sharp', 3: 'triple-sharp'}.get(output['alter'], 'compound')
|
||||
from generator.model import accidental_name
|
||||
output['accidental_display']['name'] = accidental_name(alteration(output['alter']))
|
||||
if 'tablature' in output and semitones:
|
||||
fret = output['tablature']['fret'] + semitones
|
||||
if fret < 0 or fret.denominator != 1:
|
||||
raise ValueError('transposition cannot preserve the authored tablature string/fret')
|
||||
output['tablature']['fret'] = int(fret)
|
||||
return output
|
||||
|
||||
def shift_key(key: dict, steps: int, semitones: int) -> dict:
|
||||
@@ -60,6 +68,8 @@ def shift_key(key: dict, steps: int, semitones: int) -> dict:
|
||||
p = shift_pitch({'letter': letter, 'alter': key['alterations'][index]}, steps, semitones)
|
||||
alterations[LETTERS.index(p['letter'])] = p['alter']
|
||||
result = {**copy.deepcopy(key), 'tonic': tonic, 'alterations': alterations}
|
||||
if 'steps' in key:
|
||||
result['steps'] = [shift_pitch(step, steps, semitones) for step in key['steps']]
|
||||
if not key.get('custom') and tonic:
|
||||
for fifths in range(-14, 15):
|
||||
candidate = key_from_fifths(fifths, key['mode'])
|
||||
@@ -119,7 +129,7 @@ def concert_document(document: dict) -> dict:
|
||||
from generator.model import fraction
|
||||
measure['key'] = shift_key(measure['key'], *interval(0))
|
||||
measure['key_signature'] = key_label(measure['key'])
|
||||
for n in measure['voices'][0]['notes']:
|
||||
for n in (n for v in measure['voices'] for n in v['notes']):
|
||||
n['pitches'] = [shift_pitch(p, *interval(fraction(n['onset']))) for p in n['pitches']]
|
||||
for h in measure['harmonies']:
|
||||
for field in ('root', 'bass'):
|
||||
|
||||
Reference in New Issue
Block a user