63 lines
2.8 KiB
Python
63 lines
2.8 KiB
Python
"""One learned accompaniment per instrument, reused at authored form positions."""
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import fractions
|
|
|
|
import generator.accompaniment
|
|
import generator.acoustic
|
|
import generator.acoustic_voicing
|
|
import generator.electric
|
|
import generator.guitar
|
|
import generator.fretboard
|
|
import generator.model
|
|
import generator.parts
|
|
import generator.score
|
|
import generator.arrange.form
|
|
import generator.arrange.pickup
|
|
|
|
def harmonic_key(measure, previous):
|
|
|
|
def key(chord):
|
|
return repr({k: chord.get(k) for k in ('root', 'kind', 'degrees', 'bass', 'inversion')}) if chord else ''
|
|
|
|
active = generator.accompaniment.active_chord(measure, fractions.Fraction(), previous)
|
|
return (key(active), tuple((generator.model.fraction(h['onset']), key(h)) for h in measure['harmonies']
|
|
if generator.model.fraction(h['onset']) > 0), measure['duration'], measure['time_signature'])
|
|
|
|
def audit(source, documents):
|
|
"""Compare music, not generated IDs, at every reused form position."""
|
|
authored = generator.score.source_staff(source)['measures']
|
|
result = {}
|
|
for part, document in documents.items():
|
|
if part == 'vocal':
|
|
continue
|
|
sections = generator.arrange.form.full_arrangement_sections(authored, part)
|
|
measures = document['staves'][0]['measures']
|
|
carried = generator.accompaniment.carried_chords(measures)
|
|
seen, exceptions, offset, reused = {}, [], 0, 0
|
|
for si, section in enumerate(sections):
|
|
following = sections[si + 1] if si + 1 < len(sections) else None
|
|
for j in range(section.measure_count):
|
|
if section.role != 'accompaniment':
|
|
continue
|
|
m = measures[offset + j]
|
|
if j == section.measure_count - 1 and following and following.role == 'melody' and generator.arrange.pickup.enters(following, authored):
|
|
exceptions.append({'measure': m['number'], 'reason': 'protected melody pickup'})
|
|
continue
|
|
key = (section.source_measure_index + j, repr(harmonic_key(m, carried[offset + j])))
|
|
signature = tuple((generator.model.fraction(n['onset']), generator.model.fraction(n['duration']),
|
|
tuple((generator.model.pitch_number(p), p['tablature']['string'], p['tablature']['fret']) for p in n['pitches']))
|
|
for n in generator.score.measure_notes(m))
|
|
if key in seen:
|
|
if signature != seen[key]:
|
|
raise ValueError(f'{part}, arrangement measure {m["number"]}: canonical accompaniment changed')
|
|
reused += 1
|
|
else:
|
|
if any(k[0] == key[0] for k in seen):
|
|
exceptions.append({'measure': m['number'], 'reason': 'changed effective harmony or duration'})
|
|
seen[key] = signature
|
|
offset += section.measure_count
|
|
result[part] = {'reused_bars': reused, 'distinct_form_bars': len(seen), 'exceptions': exceptions}
|
|
return result
|