Skip to content

ircam-ismm/sc-scheduling

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

  • time refers to the timeline of the parent

  • position refers to the timeline of the children

  • Transport is the thing that control timelines or engines with play / pause / etc.

  • Timeline is an abstraction that can host abstractions and place them in time according to each others

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

  • currentTime number Current time in the timeline of the scheduler
  • processorTime number Current time in the timeline of the processor see Scheduler#options.currentTimeToProcessorTimeFunction.
  • event SchedulerEvent Event that holds informations about the current scheduler call.

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

  • getTimeFunction function Function that returns a time in seconds, defining the timeline in which the scheduler is running.

  • $1 Object (optional, default {})

    • $1.period (optional, default 0.025)
    • $1.lookahead (optional, default 0.1)
    • $1.queueSize (optional, default 1e3)
    • $1.currentTimeToProcessorTimeFunction (optional, default identity)
    • $1.currentTimeToAudioTimeFunction (optional, default null)
    • $1.maxRecursions (optional, default 100)
    • $1.verbose (optional, default false)
  • options object Options of the scheduler

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

  • deferedProcessor SchedulerProcessor Callback function to schedule.
  • time number Time at which the callback should be scheduled.

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

  • processor SchedulerProcessor Processor to add to the scheduler
  • time number Time at which the processor should be launched. (optional, default this.currentTime)
  • priority Number Additional priority in case of equal time between two processor. Higher priority means the processor will processed first. (optional, default 0)

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

  • processor SchedulerProcessor The processor to reschedule
  • time number Time at which the processor must be rescheduled (optional, default undefined)

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

  • currentPosition number Current position in the timeline of the transport.
  • processorTime number Current time in the timeline of the processor see Scheduler#options.currentTimeToProcessorTimeFunction.
  • event (TransportEvent | SchedulerEvent) Event that holds informations about the current transport or scheduler call.

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

  • scheduler Scheduler Instance of scheduler into which the transport should run
  • initialState object Initial state of the transport, to synchronize it from another transport state (see Transport#dumpState()). (optional, default null)

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

  • time number Time to convert to position

Returns number

start

Start the transport at a given time.

Parameters

  • time number Time to execute the command (optional, default this.currentTime)

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

  • time number Time to execute the command (optional, default this.currentTime)

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

pause

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

Parameters

  • time number Time to execute the command (optional, default this.currentTime)

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

seek

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

Parameters

  • position number New transport position
  • time number Time to execute the command (optional, default this.currentTime)

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

loop

Set the transport loop state at a given time.

Parameters

  • value boolean Loop state
  • time number Time to execute the command (optional, default this.currentTime)

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

loopStart

Set the transport loop start point at a given time.

Parameters

  • position number Position of loop start point
  • time number Time to execute the command (optional, default this.currentTime)

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

loopEnd

Set the transport loop end point at a given time.

Parameters

  • position number Position of loop end point
  • time number Time to execute the command (optional, default this.currentTime)

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

  • value
  • time number Time to execute the command (optional, default this.currentTime)
  • speed number Speed of the transport, must be strictly > 0

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

cancel

Cancel all currently scheduled event after the given time.

Parameters

  • time number Time to execute the command (optional, default this.currentTime)

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

  • event object Raw event as returned by the transport control methods

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

  • Throws any Throw if the processor has already been added to this or another transport

has

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

Parameters

Returns boolean

remove

Remove a processor from the transport.

Parameters

  • Throws any Throw if the processor has not been added to the transport

clear

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

TransportEvent

Event emitted by the Transport when a change occurs

Parameters

  • transportState
  • tickLookahead

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