Problem
When NoteGeneratorThread follows an Ableton Link session, _set_generator_tempos() walks the full generator tree and sets every rhythm-notetype Itemstream to the same BPM. Any per-generator tempo differences set at construction time are overwritten on the first Link tempo change.
This means you can't have a child generator running at half tempo (or any other ratio) relative to the Link session — all voices are locked to the same absolute BPM.
Proposed solution
Add a tempo_ratio attribute to NoteGenerator (default 1.0). When _set_generator_tempos updates a generator's rhythm streams, it applies new_bpm * generator.tempo_ratio instead of new_bpm directly.
Example usage:
parent = Line().with_rhythm('e').with_pitches('c4 d e f')
child = Line().with_rhythm('q').with_pitches('a2 b')
child.tempo_ratio = 0.5 # half the Link tempo
parent.add_generator(child)
t = kickoff(parent, 'sine.orc', link_follower=lf, streaming=True)
# Link at 120 bpm → parent streams at 120, child streams at 60
# Link changes to 100 → parent at 100, child at 50
Implementation notes
tempo_ratio on NoteGenerator, default 1.0 (no change to existing behavior)
_set_generator_tempos changes from stream.tempo = bpm to stream.tempo = bpm * generator.tempo_ratio
- Ratios compose: if parent has ratio 1.0 and child has ratio 0.5, child gets
bpm * 0.5. If a grandchild has ratio 2.0, it gets bpm * 0.5 * 2.0 = bpm * 1.0. Whether ratios should compose or be absolute is a design choice — composing feels more natural for hierarchical structures.
- No impact on
latency_offset_secs or sync model — those operate in wall time, not beat time
Problem
When
NoteGeneratorThreadfollows an Ableton Link session,_set_generator_tempos()walks the full generator tree and sets every rhythm-notetype Itemstream to the same BPM. Any per-generator tempo differences set at construction time are overwritten on the first Link tempo change.This means you can't have a child generator running at half tempo (or any other ratio) relative to the Link session — all voices are locked to the same absolute BPM.
Proposed solution
Add a
tempo_ratioattribute toNoteGenerator(default1.0). When_set_generator_temposupdates a generator's rhythm streams, it appliesnew_bpm * generator.tempo_ratioinstead ofnew_bpmdirectly.Example usage:
Implementation notes
tempo_ratioonNoteGenerator, default1.0(no change to existing behavior)_set_generator_temposchanges fromstream.tempo = bpmtostream.tempo = bpm * generator.tempo_ratiobpm * 0.5. If a grandchild has ratio 2.0, it getsbpm * 0.5 * 2.0 = bpm * 1.0. Whether ratios should compose or be absolute is a design choice — composing feels more natural for hierarchical structures.latency_offset_secsor sync model — those operate in wall time, not beat time