39 lines
2.0 KiB
Python
39 lines
2.0 KiB
Python
"""Exact notated attacks used to compare arrangements; no audio rendering."""
|
|
import fractions
|
|
import generator.model
|
|
import generator.pitch
|
|
|
|
def timeline(document):
|
|
events, linked = [], {}
|
|
absolute = fractions.Fraction()
|
|
for measure in document['staves'][0]['measures']:
|
|
for voice in measure['voices']:
|
|
state = {key: measure[key] for key in ('transpose', 'clef', 'staff_details')}
|
|
changes = iter(sorted(measure.get('attributes', []), key=lambda a: generator.model.fraction(a['onset'])))
|
|
change = next(changes, None)
|
|
for note in voice['notes']:
|
|
onset = generator.model.fraction(note['onset'])
|
|
while change is not None and generator.model.fraction(change['onset']) <= onset:
|
|
state.update({key: change[key] for key in state if key in change})
|
|
change = next(changes, None)
|
|
if note.get('cue') or note.get('grace'):
|
|
continue
|
|
for pitch in note['pitches']:
|
|
if pitch.get('cue'):
|
|
continue
|
|
length = generator.model.fraction(pitch['duration'])
|
|
value = generator.pitch.sounding_pitch(pitch, state)
|
|
if pitch.get('tie_from'):
|
|
previous = linked[pitch['tie_from']]
|
|
assert generator.model.fraction(previous['onset']) + generator.model.fraction(previous['duration']) == absolute + onset
|
|
assert generator.model.fraction(previous['pitch']) == value
|
|
previous['duration'] = generator.model.rational(generator.model.fraction(previous['duration']) + length)
|
|
linked[pitch['id']] = previous
|
|
else:
|
|
event = dict(id=pitch['id'], pitch=generator.model.rational(value), onset=generator.model.rational(absolute + onset), duration=generator.model.rational(length))
|
|
events.append(event)
|
|
linked[pitch['id']] = event
|
|
absolute += generator.model.fraction(measure['duration'])
|
|
events.sort(key=lambda e: (generator.model.fraction(e['onset']), e['id']))
|
|
return dict(duration=generator.model.rational(absolute), events=events)
|