Skip to content

Mapping streams in context are not updated by Link tempo following #48

@benmca

Description

@benmca

Problem

When NoteGeneratorThread follows an Ableton Link session, _set_generator_tempos() walks the generator tree and updates .tempo on every rhythm-notetype Itemstream found in generator.streams. However, mapping streams stored in generator.context are invisible to this walk — their .tempo is never updated.

This is a common pattern in csound-pieces (especially index-based granular pieces): a mapping Itemstream with mapping_keys=[keys.rhythm, keys.index] lives in context['tuplestream'], and a post_process callable pulls from it to set note.rhythm, note.pfields[keys.duration], and other pfields. The mapping stream's .tempo controls the actual rhythmic timing, not the generator's own rhythm stream (which is effectively a dummy).

Example from thuja-ep/olallan/jams4.py:

g.context['tuplestream'] = Itemstream(
    mapping_keys=[keys.rhythm, keys.index],
    mapping_lists=[rhythms, indexes],
    tempo=160,
    streammode=streammodes.random
)

def post_process(note, context):
    item = context['tuplestream'].get_next_value()
    note.rhythm = utils.rhythm_to_duration(item[keys.rhythm], context['tuplestream'].tempo)
    note.pfields[keys.index] = item[keys.index]
    note.pfields[keys.duration] = note.rhythm

When Link changes tempo to 100 bpm, _set_generator_tempos updates the generator's rhythm stream to 100, but context['tuplestream'].tempo stays at 160. The audible rhythm doesn't change.

Scope

This pattern appears in many pieces in csound-pieces, particularly:

  • thuja-ep/books-style/ — index-based granular pieces
  • thuja-ep/olallan/ — live-coding pieces with mapping streams
  • thuja-ep/live_coding/ — similar patterns

The freq_to_file post_process (copy-pasted across 30+ files) is a related pattern that also relies on context-stored state.

Options to consider

  1. Convention-based: _set_generator_tempos also walks generator.context looking for Itemstream values and updates their .tempo. Simple but implicit — any Itemstream in context gets its tempo changed, which may not always be desired.

  2. Opt-in registration: Add a method like generator.register_tempo_stream(stream) that adds the stream to a list. _set_generator_tempos updates both streams and the registered list. Explicit but requires user action.

  3. Callback hook: Fire a user-defined callback on tempo change (e.g., generator.on_tempo_change(new_bpm)), letting the post_process author decide what to update. Most flexible, least magic.

  4. Tempo indirection: Instead of storing a tempo value on each stream, streams could reference a shared tempo source (the generator's rhythm stream tempo, or the Link BPM directly). The mapping stream's .tempo would be a property that reads from the source. Cleanest long-term but larger refactor.

Interaction with #46 (tempo ratios)

If per-generator tempo ratios are implemented, the mapping stream's tempo would need to respect the same ratio. Options 1 and 2 handle this naturally (the walk applies the ratio). Options 3 and 4 would need the ratio passed to the callback or shared source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions