Clocks

Supriya provides a collection of musical-time-aware clocks which permit scheduling callbacks relative to both seconds, beats and measures, and which - by extension - understand tempo, time signatures and downbeats.

These clocks come in both threaded and asynchronous flavors, as well as online and offline.

Threaded clocks handle callbacks in their own thread, while asynchronous clocks hook into an asyncio event loop. Use threaded clocks when experimenting interactively in the terminal or building simple applications. Use asynchronous clocks when building more complex applications which integrate with other asyncio-aware libraries like aiohttp, python-prompt-toolkit or pymonome.

Offline clocks implement the same interface as their online counterparts, but process their callbacks as fast as possible, regardless of the amount of real time consumed. This makes them suitable for unit-testing logic expecting online clocks, or for implementing non-realtime rendering of usually-online musical patterns.

Lifecycle

Creating

Instantiate a threaded clock with:

>>> clock = supriya.Clock()

Starting and stopping

Start a threaded clock with:

>>> clock.start()

Stop a threaded clock with:

>>> clock.stop()

Scheduling

Let’s consider a simple callback function:

>>> def callback(context):
...     print(f"The current offset is: {context.current_moment.offset}")
... 

All clock callbacks need to accept at least a context argument, to which the clock will pass a ClockContext object.

Scheduling

We can schedule that procedure with:

>>> clock.schedule(callback)
0

Scheduling callbacks returns an event ID.

Canceling

We can use the event ID to cancel the callback if it hasn’t already occurred:

>>> clock.cancel(0)
CallbackCommand(args=None, event_id=0, event_type=<EventType.SCHEDULE: 1>, kwargs=None, procedure=<function callback at 0x7fa7a44f9ee0>, quantization=None, schedule_at=0.0, time_unit=<TimeUnit.BEATS: 0>)

Cueing

We can also cue callbacks, scheduling them to occur at some quantized offset in the future, e.g. on the next beat or downbeat:

>>> clock.cue(callback)
1

Callbacks

Let’s consider a more complicated callback function:

>>> def callback(
...     context,
...     salutation,
...     delta=0.25,
...     max_repeats=0,
...     time_unit=supriya.clocks.TimeUnit.SECONDS,
... ):
...     print(f"{salutation}: {context.current_moment.offset}")
...     if max_repeats and context.event.invocations < max_repeats:
...         print("Re-scheduling")
...         return delta, time_unit
...     else:
...         print("Bailing!")
... 

Arguments

  • positional arguments

  • keyword arguments

Deltas

  • none

  • single float

  • pair of float and time unit or int

Contexts, moments, events

  • contexts

  • moments, current and expected

  • events, invocations

Musical time

Like callbacks, changes to tempo and time signature can be triggered “immediately”, scheduled at an absolute point in the future, or cued at some quantization relative the current time.

Tempo

Time signatures

Async clocks

Async clocks have identical APIs to threaded clocks with three differences:

  • their start() method is async,

  • their stop() method is async, and

  • scheduled callbacks may also be async.

Instantiate an async clock with:

>>> clock = supriya.AsyncClock()

Offline clocks

Debugging