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.
npm install --save @ircam/sc-scheduling
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;
});
-
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
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
currentTime
number Current time in the timeline of the schedulerprocessorTime
number Current time in the timeline of the processor seeScheduler#options.currentTimeToProcessorTimeFunction
.event
SchedulerEvent Event that holds informations about the current scheduler call.
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/
-
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, default0.02
)$1.lookahead
(optional, default0.05
)$1.queueSize
(optional, default1e3
)$1.currentTimeToProcessorTimeFunction
(optional, defaultidentity
)$1.currentTimeToAudioTimeFunction
(optional, defaultnull
)$1.maxRecursions
(optional, default100
)$1.verbose
(optional, defaultfalse
)
-
options
object Options of the scheduler
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 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 duration, in seconds. Throws if negative or lower than period.
Type: number
Current time in the scheduler timeline, in seconds.
Basically an accessor for getTimeFunction
parameter given in constructor.
Type: number
[deprecated] Scheduler current audio time according to currentTime
Type: number
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
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
.
deferedProcessor
SchedulerProcessor Callback function to schedule.time
number Time at which the callback should be scheduled.
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;
});
Check whether a given processor has been added to this scheduler
processor
SchedulerProcessor Processor to test.
Returns boolean
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.
processor
SchedulerProcessor Processor to add to the schedulertime
number Time at which the processor should be launched. (optional, defaultthis.currentTime
)priority
Number Additional priority in case of equal time between two processor. Higher priority means the processor will processed first. (optional, default0
)
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.
processor
SchedulerProcessor The processor to rescheduletime
number Time at which the processor must be rescheduled (optional, defaultundefined
)
Remove a processor from the scheduler.
processor
SchedulerProcessor The processor to reschedule
Clear the scheduler.
Scheduler information provided as third argument of a callback registered in the scheduler
Delta time between tick time and current time, in seconds
Type: Number
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
currentPosition
number Current position in the timeline of the transport.processorTime
number Current time in the timeline of the processor seeScheduler#options.currentTimeToProcessorTimeFunction
.event
(TransportEvent | SchedulerEvent) Event that holds informations about the current transport or scheduler call.
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.
scheduler
Scheduler Instance of scheduler into which the transport should runinitialState
object Initial state of the transport, to synchronize it from another transport state (seeTransport#dumpState()
). (optional, defaultnull
)
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);
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
Pointer to the underlying scheduler.
Type: Scheduler
Current time from scheduler timeline, in seconds.
Type: number
Current processor time, in seconds.
Type: number
Current transport position, in seconds.
Type: number
Estimated position at given time according to the transport current state.
time
number Time to convert to position
Returns number
Start the transport at a given time.
time
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Stop the transport at a given time, position will be reset to zero.
time
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Pause the transport at a given time, position will remain untouched.
time
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Seek to a new position in the timeline at a given time.
position
number New transport positiontime
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Set the transport loop state at a given time.
value
boolean Loop statetime
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Set the transport loop start point at a given time.
position
number Position of loop start pointtime
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Set the transport loop end point at a given time.
position
number Position of loop end pointtime
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
Set transport speed at a given time.
Note that speed must be strictly positive. Experimental
value
time
number Time to execute the command (optional, defaultthis.currentTime
)speed
number Speed of the transport, must be strictly > 0
Returns (object | null) Raw event or null
if event discarded
Cancel all currently scheduled event after the given time.
time
number Time to execute the command (optional, defaultthis.currentTime
)
Returns (object | null) Raw event or null
if event discarded
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)
event
object Raw event as returned by the transport control methods
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);
Add a list of raw events to the transport queue.
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.
processor
TransportProcessor Engine to add to the transport
- Throws any Throw if the processor has already been added to this or another transport
Define if a given processor has been added to the transport.
processor
TransportProcessor Engine to check
Returns boolean
Remove a processor from the transport.
processor
TransportProcessor Engine to remove from the transport
- Throws any Throw if the processor has not been added to the transport
Remove all processors, cancel all registered transport event and pause transport
Event emitted by the Transport when a change occurs
transportState
tickLookahead
Type of the event
Type: string
Time of the event
Type: number
Position of the event in timeline
Type: number
Current speed of the transport (0 is stopped or paused, 1 if started)
Type: number
Wether the transport is looping
Type: boolean
Start position of the loop
Type: number
Stop position of the loop
Type: number
Delta time between tick time and event time, in seconds
Type: number