LinkClock:
Filter:
Classes | Scheduling > Clocks

LinkClock

Scheduler synchronized with Ableton Link
Location: NOT INSTALLED!

Description

LinkClock is a TempoClock based on Ableton Link, a protocol that synchronizes phase (beat-within-bar) and tempo over a local network.

Link behavior

Link attempts, as much as possible, to allow applications to move in their own time while synchronizing:

Link does not synchronize exact beat numbers. Two SuperCollider peers joining the same Link session, in 4/4 time, may be respectively at beats 4 and 16 (which are both barlines), but they should not be at beats 4.0 and 19.0.

Many Link-enabled applications (DAWs) can start and stop the entire timeline. SuperCollider clocks do not stop. A DAW, when starting to play, should lock into SuperCollider's phase within the bar. You can register functions to respond to other peers starting or stopping. (SuperCollider does not know which other peer started or stopped.)

Latency

Link coordinates the sounding time of given beats.

All audio applications must prepare events, and audio processing, in advance, so that the signal hits the audio interface at the right time. SuperCollider uses OSC timestamps for this purpose, calculating timestamps from the current time in seconds + a latency offset.

To coordinate with other applications, then, the LinkClock must run earlier than the desired sounding time, by the same fixed offset. That is, it should have the same (positive) latency offset as the server:

Server messages should then be sent with the same amount of latency. Events generated by patterns automatically apply the server object's latency. For other messages, use Server: -makeBundle.

With a positive latency, if you examine t.beats, it will appear to be later than the beats shown in other applications. This is normal. The clock in SuperCollider must run early for the events to sound on time.

(Because the clock and server have separate latency settings, it is possible to adjust timing relative to other applications. If you find, for instance, that SuperCollider is sounding slightly late, you can increase the LinkClock's latency slightly, moving the clock earlier, without affecting messaging latency. Ideally, this should not be necessary, but inter-application coordination can be fiddly.)

This does have an impact on tempo changes. SuperCollider receives tempo changes from other peers only after they are sent, which is probably slightly late for our clock. In practice, it means that events falling within the latency gap may be slightly out of sync, but sync should recover very quickly. (So, Link is not ideal for music involving frequent tempo or meter changes.)

beatsPerBar and quantum

For every moment in time, Ableton Link knows the beat, time in seconds, and phase. Phase is measured relative to a quantum. Link peers sharing the same quantum will synchronize barlines.

For instance, assume quantum = 4. Peer A is currently at beat 33. Peer B joins the network. Peer A's last barline was beat 32, so phase = 1. Peer B's beat counter should arrange to have the same phase: in practice, it will start with -3.

PhasePeer A beatsPeer B beatsNotes
032--
133-3Link calculates -3 for B
234-2
335-1
0360Barline (synced)

If you change the quantum, Link does not guarantee that beats will keep the same duration. Beats may be shorter or longer, to try to preserve phase sync.

Therefore, you should not change the quantum in the middle of a performance. Ableton recommends to set the quantum just before beginning. Then, you can change SuperCollider's beatsPerBar as needed, without changing the quantum, so that Link's phase reference remains consistent.

SuperCollider peers may optionally synchronize barlines and all meter changes by using LinkClock: -enableMeterSync. This is independent of Link's quantum, and is safe to use mid-performance. See MeterSync.

Notifications

A LinkClock's state may be changed by other connected Link peers. LinkClock uses dependant notifications to relay the new state to other interested objects.

NotificationEvent
\tempoTempo changed
\meterThe clock's beatsPerBar changed
\linkStartAn external peer started playback
\linkStopAn external peer stopped playback
\numPeersThe number of connected peers changed
\resyncedAfter -enableMeterSync, a \resynced notification is sent whenever a MeterSync: -resyncMeter call finishes successfully. One Boolean argument is passed: true if other SuperCollider peers were found, false if not.

(Note that Link does not synchronize meter across peers; this notification is inherited from TempoClock. LinkClock: -isSyncingMeter is a SuperCollider-specific way to synchronize meter; it is not part of the Link protocol.)

The most convenient way to register to receive a notification is SimpleController. See the examples below.

Class Methods

.new

Creates a new instance of LinkClock.

Arguments:

tempo

The initial tempo. Defaults to 1.

beats

The time in beats, corresponding to the reference time. Default to 0.

seconds

The reference time in seconds. See TempoClock: *new.

queueSize

The storage size of the scheduling queue. See TempoClock: *new.

Discussion:

If an existing Link session is found on the local network, the object connects to it and use its properties: the tempo argument is discarded in favor of the session tempo, and the beat argument will be adjusted to ensure proper beat and phase synchronization across all peers. If SuperCollider is the first to join, a new Link session is locally created and initialized with the constructor arguments.

.newFromTempoClock

Creates a new instance of LinkClock derived from a TempoClock.

Arguments:

clock

The TempoClock used to create the LinkClock.

Discussion:

The LinkClock tempo, beats, seconds and beatsPerBar values are set to those of the TempoClock. The TempoClock is then stopped and all of its tasks are rescheduled by the newly created LinkClock, so that they are now synchronized with the Link session.

Inherited class methods

Instance Methods

.latency

Gets or sets the number of seconds of OSC messaging latency for which the LinkClock should account. In general, this should be set to match the server object's latency.

Arguments:

lat

A Float.

.tempo

Sets or gets the current session tempo at the current logical time. Note that the tempo may be changed at any time by another peer; the LinkClock broadcasts a \tempo notification in this case (see LinkClock: Notifications).

.beats

Sets or gets the current logical time in beats. If you are trying to set the beats, Link may adjust your given value to maintain sync with other peers.

.numPeers

Gets the number of peers connected to the current Link session. When peers join or leave the session, the LinkClock broadcasts a \numPeers notification (see LinkClock: Notifications).

.quantum

Gets or sets Link's internal quantum (see above, beatsPerBar and quantum). Normally this should be done at the beginning of a performance, or not at all. It is risky to change quantum during a performance. Changing meter locally is the same as in TempoClock: TempoClock: -beatsPerBar.

Arguments:

quantum

An Integer or Float.

.enableMeterSync

Activates SuperCollider-barline sync by creating a MeterSync object internally. (If already enabled, no new object will be created.)

Arguments:

id

Optional: An integer ID, uniquely identifying this instance. If not provided, one will be chosen randomly.

ports

Optional: An array of port numbers, to which barline-sync messages will be sent.

Returns:

The LinkClock instance (to support chaining configuration methods, e.g. l = LinkClock.new.latency_(s.latency).enableMeterSync, in which case you want l to be the clock object). To get access to the MeterSync object, use -getMeterSync.

.disableMeterSync

Remove all barline-sync objects.

.isSyncingMeter

Returns:

A Boolean, true if barline sync is active, false if not. (If you create the barline sync object independently, this answer is likely to be incorrect.)

.getMeterSync

Returns:

The MeterSync object previously created by -enableMeterSync.

NOTE: It is possible, though redundant and not recommended, to create multiple barline-sync objects by doing clock.enableMeterSync and m = SCClockMeterSync(clock). getMeterSync has access only to the object created by clock.enableMeterSync. Therefore, it is recommended to use clock.enableMeterSync in all cases.

Inherited instance methods

Examples