ircam-ismm / sc-scheduling

scheduler, transport and timeline
https://ircam-ismm.github.io/sc-scheduling/
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

sc-scheduling

Simple library to schedule events in Node.js and the browser.

The abstractions provided by the library are built in such way that they can be used in distributed contexts, allowing to synchronize events and controls among multiple devices and clients.

Install

npm install --save @ircam/sc-scheduling

Example Use

import { Scheduler } from '@ircam/sc-scheduling';

const audioContext = new AudioContext();
// create a scheduler in the timeline of the audio context
const scheduler = new Scheduler(() => audioContext.currentTime);
// schedule an audio event to be scheduler every seconds
schedule.add(currentTime => {
  doSomethingAtCurrentTime(currentTime);
  // ask to be called back in 1 second from currentTime
  return currentTime + 1;
});

Terminology

API

Table of Contents

SchedulerProcessor

Processor to add into a Scheduler.

The processor will be called back by the Scheduler at the time it request, do some processing and return the next time at which it wants to be called back.

Note that the APIs of the SchedulerProcessor and of a TransportProcessor are made in such way that it is possible to implement generic processors that can be added both to a Scheduler and to a Transport.

Type: function

Parameters

Scheduler

The Scheduler interface implements a lookahead scheduler that can be used to schedule events in an arbitrary timelines.

It aims at finding a tradeoff between time precision, real-time responsiveness and the weaknesses of the native timers (i.e. setTimeout and setInterval)

For an in-depth explaination of the pattern, see https://web.dev/audio-scheduling/

Parameters

Examples

import { Scheduler } from '@ircam/sc-scheduling';
import { getTime } from '@ircam/sc-utils';

const scheduler = new Scheduler(getTime);

const processor = (currentTime, processorTime, infos) => {
  console.log(currentTime);
  return currentTime + 0.1; // ask to be called back every 100ms
}

// start processor in 1 second
scheduler.add(processor, getTime() + 1);

period

Period of the scheduler, in seconds.

Minimum time span between the scheduler checks for events, in seconds. Throws if negative or greater than lookahead.

Type: number

lookahead

Lookahead duration, in seconds. Throws if negative or lower than period.

Type: number

currentTime

Current time in the scheduler timeline, in seconds.

Basically an accessor for getTimeFunction parameter given in constructor.

Type: number

audioTime

[deprecated] Scheduler current audio time according to currentTime

Type: number

processorTime

Processor time, in seconds, according to currentTime and the transfert function provided in options.currentTimeToProcessorTimeFunction.

If options.currentTimeToProcessorTimeFunction has not been set, is equal to currentTime.

Type: number

defer

Execute a function once at a given time.

Calling defer compensates for the tick lookahead introduced by the scheduling with a setTimeout. Can be usefull for example to synchronize audio events which natively scheduled with visuals which have no internal timing/scheduling ability.

Be aware that this method will introduce small timing error of 1-2 ms order of magnitude due to the setTimeout.

Parameters

Examples

const scheduler = new Scheduler(getTime);

scheduler.add((currentTime, processorTime) => {
  // schedule some audio event
  playSomeSoundAt(processorTime);
  // defer execution of visual display to compensate the tickLookahead
  scheduler.defer(displaySomeSynchronizedStuff, currentTime);
  // ask the scheduler to call back in 1 second
  return currentTime + 1;
});

has

Check whether a given processor has been added to this scheduler

Parameters

Returns boolean

add

Add a processor to the scheduler.

Note that given time is considered a logical time and that no particular checks are made on it as it might break synchronization between several processors. So if the given time is in the past, the processor will be called in a recursive loop until it reaches current time. This is the responsibility of the consumer code to handle such possible issues.

Parameters

reset

Reset next time of a given processor.

If time is not a number, the processor is removed from the scheduler.

Note that given time is considered a logical time and that no particular checks are made on it as it might break synchronization between several processors. So if the given time is in the past, the processor will be called in a recursive loop until it reaches current time. This is the responsibility of the consumer code to handle such possible issues.

Be aware that calling this method within a processor callback function won't work, because the reset will always be overriden by the processor return value.

Parameters

remove

Remove a processor from the scheduler.

Parameters

clear

Clear the scheduler.

SchedulerEvent

Scheduler information provided as third argument of a callback registered in the scheduler

tickLookahead

Delta time between tick time and current time, in seconds

Type: Number

TransportProcessor

Processor to add into a Transport.

The processor will be called back by the Transport on each transport event to define its behavior according to event. Between these events, it can be called as a regular SchedulerProcessor to do some processing.

Note that the APIs of the SchedulerProcessor and of a TransportProcessor are made in such way that it is possible to implement generic processors that can be added both to a Scheduler and to a Transport.

Type: function

Parameters

Transport

The Transport abstraction allows to define and manipulate a timeline.

All provided Transport commands (e.g. start, stop, etc) can be scheduled in the underlying scheduler timeline which makes it usable in distributed and synchronized contexts.

Parameters

Examples

import { Scheduler, Transport, TransportEvent } from '@ircam/sc-scheduling';
import { getTime } from '@ircam/sc-utils';

const scheduler = new Scheduler(getTime);
const transport = new Transport(scheduler);

const processor = (position, time, infos) => {
  if (infos instanceof TransportEvent) {
     // ask to be called back only when the transport is running
     return infos.speed > 0 ? position : Infinity;
  }

  console.log(position);
  return position + 0.1; // ask to be called back every 100ms
}

transport.add(processor);
// start transport in 1 second
transport.start(getTime() + 1);

serialize

Retrieves the current state and event queue for the transport as a raw object.

The returned value can be used to initialize the state of another synchronized transport, cf. initialValue argument from constructor.

Returns object

scheduler

Pointer to the underlying scheduler.

Type: Scheduler

currentTime

Current time from scheduler timeline, in seconds.

Type: number

processorTime

Current processor time, in seconds.

Type: number

currentPosition

Current transport position, in seconds.

Type: number

getPositionAtTime

Estimated position at given time according to the transport current state.

Parameters

Returns number

start

Start the transport at a given time.

Parameters

Returns (object | null) Raw event or null if event discarded

stop

Stop the transport at a given time, position will be reset to zero.

Parameters

Returns (object | null) Raw event or null if event discarded

pause

Pause the transport at a given time, position will remain untouched.

Parameters

Returns (object | null) Raw event or null if event discarded

seek

Seek to a new position in the timeline at a given time.

Parameters

Returns (object | null) Raw event or null if event discarded

loop

Set the transport loop state at a given time.

Parameters

Returns (object | null) Raw event or null if event discarded

loopStart

Set the transport loop start point at a given time.

Parameters

Returns (object | null) Raw event or null if event discarded

loopEnd

Set the transport loop end point at a given time.

Parameters

Returns (object | null) Raw event or null if event discarded

speed

Set transport speed at a given time.

Note that speed must be strictly positive. Experimental

Parameters

Returns (object | null) Raw event or null if event discarded

cancel

Cancel all currently scheduled event after the given time.

Parameters

Returns (object | null) Raw event or null if event discarded

addEvent

Add raw event to the transport queue.

Most of the time, you should use the dedicated higher level methods. However this is useful to control several transports from a central event producer. In particular this can be used to synchronize several transport on the network according you have access to a synchronized timeline in which the schedulers are running, cf. e.g. https://github.com/ircam-ismm/sync)

Parameters

Examples

const scheduler = new Scheduler(getTime);
const primary = new Transport(scheduler);
// create a "copy" of the primary transport
const secondary = new Transport(scheduler, primary.serialize());
// perform some control command and share it with the secondary transport
const event = primary.start(getTime() + 1);
// `event` (as well as `primary.serialize()`) could e.g. be sent over the network
secondary.addEvent(event);

addEvents

Add a list of raw events to the transport queue.

Parameters

add

Add an processor to the transport.

When a processor is added to the transport, it called with an 'init' event to allow it to respond properly to the current state of the transport. For example, if the transport has already been started.

Parameters

has

Define if a given processor has been added to the transport.

Parameters

Returns boolean

remove

Remove a processor from the transport.

Parameters

clear

Remove all processors, cancel all registered transport event and pause transport

TransportEvent

Event emitted by the Transport when a change occurs

Parameters

type

Type of the event

Type: string

time

Time of the event

Type: number

position

Position of the event in timeline

Type: number

speed

Current speed of the transport (0 is stopped or paused, 1 if started)

Type: number

loop

Wether the transport is looping

Type: boolean

loopStart

Start position of the loop

Type: number

loopEnd

Stop position of the loop

Type: number

tickLookahead

Delta time between tick time and event time, in seconds

Type: number

License

BSD-3-Clause