@ircam/sc-scheduling
v1.0.0
Published
Set of abstractions for advanced audio scheduling in Node.js and the browser.
Downloads
69
Readme
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 parentposition
refers to the timeline of the childrenTransport
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 schedulerprocessorTime
number Current time in the timeline of the processor seeScheduler#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, 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
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
processor
SchedulerProcessor Processor to test.
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 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
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 rescheduletime
number Time at which the processor must be rescheduled (optional, defaultundefined
)
remove
Remove a processor from the scheduler.
Parameters
processor
SchedulerProcessor The processor to reschedule
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 seeScheduler#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 runinitialState
object Initial state of the transport, to synchronize it from another transport state (seeTransport#dumpState()
). (optional, defaultnull
)
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, defaultthis.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, defaultthis.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, defaultthis.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 positiontime
number Time to execute the command (optional, defaultthis.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 statetime
number Time to execute the command (optional, defaultthis.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 pointtime
number Time to execute the command (optional, defaultthis.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 pointtime
number Time to execute the command (optional, defaultthis.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, defaultthis.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, defaultthis.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
processor
TransportProcessor Engine to add to the transport
- 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
processor
TransportProcessor Engine to check
Returns boolean
remove
Remove a processor from the transport.
Parameters
processor
TransportProcessor Engine to remove from the transport
- 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