supriya-project/supriya

Implement completion messages and prioritization for Providers

josiah-wolf-oberholtzer opened this issue · 6 comments

Prioritization via:

  • Provider.ordered() context manager: each message gets a new monotonically higher priority
  • Provider.unordered() context manager
  • priority: Optional[int] = None kwarg for all methods

Completion via:

  • Completion context manager, returned from all methods that could take a completion message
  • Completion contexts should (maybe) share the same stack as moments
  • Completion contexts require a current moment
with provider.at(...):
    buffer_ = provider.add_buffer(...)
    with buffer_:
        with provider.ordered():
            provider.add_synth(...)
            bus.set(...)
            buffer_.normalize(...)
    with other_buffer.free():
        provider.free_synth(synth...)

Prioritization is straightforward for real-time, but tricky for NRT: we need to maintain the issued commands and their orders, rather than compute what we think the state of the server would be at each time step. This inverts the current relationship where we modify the intended state of the server and that state generates the OSC commands. In some ways, this makes things a little simpler, treating Sessions more like sclang's Score. It gets tricky again if we want to perform any checks (e.g. node parentage) or do any other kind of inspection: the state of the server needs to be computed from the commands (including completion messages 😅). May also need to implement a "simplify" algorithm that rolls-up related commands (e.g. multiple node moves).

More implementation thoughts:

  • Change all provider proxies to always use integers for their identifiers: simplifies the provider side, if we can look up NRT entities by session ID
  • Collapse the concrete providers into the abstract: just one Provider class
  • All provider methods create request objects and store them on the moment, a dictionary of priorities to dictionaries of requests: Dict[int, Dict[Type[Request], List[Request]]]
  • The exception for Provider methods are the entity creation methods (add_*) where we need to grab new IDs via the RT allocators / new session IDs. That's simple for RT, but for NRT we need a (private?) way to create NRT entities with pre-computed IDs. Maybe a session_id: Optional[int] = None kwarg is fine - it can error if the entity was already added
  • The __exit__ method to ProviderMoment does 99% of the work, handling both RT and NRT applications

Change of thought:

  • No prioritization, because all requests are ordered by when they've been added to a given moment
    • Why? We don't want to re-order node additions (groups, parallel groups, synths)
    • Simplest to just order everything by when the methods are called
  • (maybe) No implicit SynthDef loading - just use completions

In progress via #307