Rhythm Patterns
This commit is contained in:
@@ -25,28 +25,30 @@ def harmonic_key(measure, 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):
|
||||
def audit(source, documents, *, role_documents=False):
|
||||
"""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':
|
||||
for name, document in documents.items():
|
||||
part = name.split('_')[0] if role_documents else name
|
||||
wanted = {'solo': 'slash', 'melody': 'vocal' if part == 'vocal' else 'melody'}.get(name.split('_')[-1], 'accompaniment') if role_documents else 'accompaniment'
|
||||
if part == 'vocal' and not role_documents:
|
||||
continue
|
||||
sections = generator.arrange.form.full_arrangement_sections(authored, part)
|
||||
entry_positions = {s.source_measure_index for s in sections if s.role == 'accompaniment'}
|
||||
entry_positions = {s.source_measure_index for s in sections if s.role == wanted}
|
||||
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':
|
||||
if section.role != wanted:
|
||||
continue
|
||||
m = measures[offset + j]
|
||||
if j == section.measure_count - 1 and following and following.role == 'melody' and generator.arrange.pickup.enters(following, authored):
|
||||
if wanted == 'accompaniment' and 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
|
||||
if j == section.measure_count - 1 and following and following.role == 'slash' and generator.score.body_start_measure_index(authored):
|
||||
if wanted == 'accompaniment' and j == section.measure_count - 1 and following and following.role == 'slash' and generator.score.body_start_measure_index(authored):
|
||||
exceptions.append({'measure': m['number'], 'reason': 'source-defined solo pickup allocation'})
|
||||
continue
|
||||
position = section.source_measure_index + j
|
||||
@@ -60,16 +62,16 @@ def audit(source, documents):
|
||||
boundary = following.role if following and block_start + 2 >= section.source_measure_index + section.measure_count and generator.score.body_start_measure_index(authored) and following.role in ('slash', 'melody') else ''
|
||||
key = (position, context, boundary)
|
||||
signature = tuple((v['id'], generator.model.fraction(n['onset']), generator.model.fraction(n['duration']),
|
||||
tuple((generator.model.pitch_number(p), generator.model.fraction(p['duration']), p['tablature']['string'], p['tablature']['fret'], bool(p.get('tie_from')) if position not in entry_positions or generator.model.fraction(n['onset']) else False) for p in n['pitches']))
|
||||
for v in m['voices'] for n in v['notes'] if n['pitches'])
|
||||
tuple((generator.model.pitch_number(p), generator.model.fraction(p['duration']), p.get('tablature', {}).get('string'), p.get('tablature', {}).get('fret'), bool(p.get('tie_from')) if position not in entry_positions or generator.model.fraction(n['onset']) else False, bool(p.get('tie_to'))) for p in n['pitches']))
|
||||
for v in m['voices'] for n in v['notes'])
|
||||
if key in seen:
|
||||
if signature != seen[key]:
|
||||
raise ValueError(f'{part}, arrangement measure {m["number"]}: canonical accompaniment changed')
|
||||
raise ValueError(f'{name}, arrangement measure {m["number"]}: canonical {wanted} changed')
|
||||
reused += 1
|
||||
else:
|
||||
if any(k[0] == key[0] for k in seen):
|
||||
exceptions.append({'measure': m['number'], 'reason': 'changed two-bar harmony, duration, or protected participation'})
|
||||
seen[key] = signature
|
||||
offset += section.measure_count
|
||||
result[part] = {'reused_bars': reused, 'distinct_form_bars': len(seen), 'exceptions': exceptions}
|
||||
result[name] = {'reused_bars': reused, 'distinct_form_bars': len(seen), 'exceptions': exceptions}
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user