@@ -0,0 +1,44 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import generator.harmony
|
||||
import generator.model
|
||||
import generator.pitch
|
||||
|
||||
def chord_notes(chord: dict) -> tuple[tuple[int, str], ...]:
|
||||
return tuple((degree, generator.model.pitch_name(generator.pitch.shift_pitch({**chord['root'], 'octave': 4}, degree - 1, generator.harmony.degree_semitones(degree, alter)))) for degree, alter in generator.harmony.degrees(chord).items())
|
||||
|
||||
def passage(part: str, solo, choices, library) -> str:
|
||||
lines = [f'{part.capitalize()}: {solo.label}', 'Positions use source measure numbers and quarter-note beats; arranged measure numbers are also shown.', '']
|
||||
lines.append('Scale map:')
|
||||
previous = None
|
||||
for choice in choices:
|
||||
label = choice.scale.label() if choice.scale else 'No exact match within the permitted catalog.' if choice.span.chord else 'N.C.'
|
||||
if label != previous:
|
||||
lines.append(f' Source measure {choice.span.source_measure}, beat {choice.span.beat}: {label}')
|
||||
lines.append(' ' + choice.reason)
|
||||
previous = label
|
||||
lines.extend(('', 'Chord detail:'))
|
||||
for choice in choices:
|
||||
span, scale = choice.span, choice.scale
|
||||
if span.chord is None:
|
||||
continue
|
||||
chord = span.chord
|
||||
notes = chord_notes(chord)
|
||||
required = [name for _, name in notes]
|
||||
if bass := chord.get('bass'):
|
||||
required.append(generator.model.pitch_name(bass))
|
||||
targets = [name for degree, name in notes if degree in (3, 7)] or [name for _, name in notes]
|
||||
lines.append(f'Source measure {span.source_measure}, beat {span.beat}; arranged measure {span.measure}; offset {span.start} through {span.end}')
|
||||
lines.append(' Chord: ' + generator.harmony.symbol(chord) + '; required pitches: ' + ', '.join(dict.fromkeys(required)))
|
||||
if scale:
|
||||
root = generator.model.pitch_number({**chord['root'], 'octave': 4}) % 12
|
||||
degree, mode = library.chord_mode(scale, generator.model.pitch_name(chord['root'])) if root in scale.pitches else (None, None)
|
||||
lines.append(' Parent: ' + scale.label() + ' [' + ', '.join(scale.notes) + ']')
|
||||
lines.append(f' Parent degree: {degree or "omitted root"}; chord-root mode: {mode or "not assigned"}')
|
||||
lines.append(' Chord-tone targets: ' + ', '.join(targets))
|
||||
if generator.model.pitch_name(chord['root']) == scale.root:
|
||||
lines.append(' Tonic resolution target: ' + scale.root)
|
||||
else:
|
||||
lines.append(' No exact match within the permitted catalog.')
|
||||
lines.extend((' Reason: ' + choice.reason, ''))
|
||||
return '\n'.join(lines)
|
||||
Reference in New Issue
Block a user