@@ -91,8 +91,11 @@ def validate(notes,contracts,harmony):
|
||||
return {},c.id+': missing or changed expression event/target'
|
||||
if c.preparation and not same(c.preparation):return {},c.id+': changed preparation'
|
||||
if any(n.event_id in approved for n in c.events):return {},c.id+': overlapping expression permissions'
|
||||
target=c.target; span=harmony.at(c.boundary if c.boundary is not None else target.start)
|
||||
target=c.target; span=harmony.at(c.boundary if c.boundary is not None else c.events[0].start if c.kind=='color' else target.start)
|
||||
if not span or span.chord=='N.C.' or span.chord!=c.chord:return {},c.id+': wrong target harmony'
|
||||
if c.kind in ('anticipation','anticipated_color'):
|
||||
previous=next((s for s in harmony.spans if s.end==c.boundary),None)
|
||||
if not previous or previous.chord.strip('()')==span.chord.strip('()'):return {},c.id+': anticipation requires a harmonic change'
|
||||
if c.kind in ('approach','enclosure','connection'):
|
||||
rule=next((r for r in vocabulary.data('lead_ins')['patterns'] if r['id']==c.rule and r['kind']==c.kind),None)
|
||||
if not rule or len(c.events)!=len(rule['semitones']):return {},c.id+': unknown approach formula'
|
||||
@@ -112,6 +115,16 @@ def validate(notes,contracts,harmony):
|
||||
if n.pitch.number()%12 not in harmony.permitted(n.start,n.end):return {},c.id+': declared diatonic neighbor is outside source-key/chord motion'
|
||||
if any(n.start<c.target.start and n.end>c.events[0].start and n.event_id not in {e.event_id for e in c.events} for n in notes):return {},c.id+': interrupted approach'
|
||||
if c.kind=='connection' and (not c.preparation or c.preparation.end!=c.events[0].start or abs(c.preparation.pitch.number()-c.events[0].pitch.number())>2):return {},c.id+': chromatic connection lacks its incoming motion'
|
||||
elif c.kind=='passing':
|
||||
rule=vocabulary.data('lead_ins')['passing']
|
||||
if c.rule!=rule['id'] or len(c.events)!=1 or not c.preparation:return {},c.id+': unsupported passing figure'
|
||||
n,=c.events;before=c.preparation
|
||||
left=n.pitch.number()-before.pitch.number();right=target.pitch.number()-n.pitch.number()
|
||||
if before.end!=n.start or n.end!=target.start or n.end-n.start>F(rule['max_note']) or left!=right or left not in rule['steps']:
|
||||
return {},c.id+': interrupted/nonstepwise passing figure'
|
||||
def supported(endpoint):
|
||||
return harmony.ordinary(endpoint) or any(a.kind in ('color','anticipated_color') and endpoint in a.events for a in contracts)
|
||||
if not supported(before) or not supported(target):return {},c.id+': unsupported passing endpoints'
|
||||
elif c.kind=='delayed_approach':
|
||||
rule=vocabulary.data('lead_ins').get('delayed_approach',{})
|
||||
if c.rule!=rule.get('id') or len(c.events)!=1:return {},c.id+': unknown delayed approach'
|
||||
@@ -126,7 +139,7 @@ def validate(notes,contracts,harmony):
|
||||
for n in c.events:
|
||||
if n.end-n.start>F(rule['max_beats']):return {},c.id+': excessive color duration'
|
||||
for region,a,b in harmony.segments(n):
|
||||
if n.pitch.number()%12 in region.roles.basic:continue
|
||||
if n.pitch.number()%12 in region.roles.basic|region.roles.specified:continue
|
||||
if region.chord=='N.C.' or not eligible_color(rule,region,harmony):return {},c.id+': incompatible color harmony/function'
|
||||
p=degree_pitch(parsed_chord(region.chord),rule['degree'],rule['alter'],n.pitch.number())
|
||||
# The rule retains the functional degree/spelling. Encoded
|
||||
|
||||
Reference in New Issue
Block a user