Tone.js/Tone/core/Clock.js

283 lines
7.5 KiB
JavaScript
Raw Normal View History

2018-02-27 22:02:41 +00:00
define(["Tone/core/Tone", "Tone/source/TickSource", "Tone/core/TimelineState",
2018-01-02 15:37:27 +00:00
"Tone/core/Emitter", "Tone/core/Context"], function(Tone) {
2015-10-21 16:11:19 +00:00
"use strict";
2014-07-30 17:54:55 +00:00
/**
2017-10-21 23:02:46 +00:00
* @class A sample accurate clock which provides a callback at the given rate.
2015-06-20 19:50:57 +00:00
* While the callback is not sample-accurate (it is still susceptible to
* loose JS timing), the time passed in as the argument to the callback
* is precise. For most applications, it is better to use Tone.Transport
2015-08-18 22:14:26 +00:00
* instead of the Clock by itself since you can synchronize multiple callbacks.
2014-07-30 17:54:55 +00:00
*
* @constructor
2016-09-24 15:26:57 +00:00
* @extends {Tone.Emitter}
2015-06-20 19:50:57 +00:00
* @param {function} callback The callback to be invoked with the time of the audio event
2015-10-09 15:01:03 +00:00
* @param {Frequency} frequency The rate of the callback
2015-06-14 00:20:36 +00:00
* @example
* //the callback will be invoked approximately once a second
* //and will print the time exactly once a second apart.
2015-08-18 22:14:26 +00:00
* var clock = new Tone.Clock(function(time){
2015-06-14 00:20:36 +00:00
* console.log(time);
2015-08-18 22:14:26 +00:00
* }, 1);
2014-07-30 17:54:55 +00:00
*/
2015-08-18 22:14:26 +00:00
Tone.Clock = function(){
2017-04-26 02:23:22 +00:00
var options = Tone.defaults(arguments, ["callback", "frequency"], Tone.Clock);
2016-09-24 15:26:57 +00:00
Tone.Emitter.call(this);
2015-08-18 22:14:26 +00:00
/**
* The callback function to invoke at the scheduled tick.
* @type {Function}
*/
this.callback = options.callback;
2014-07-30 17:54:55 +00:00
/**
2015-08-18 22:14:26 +00:00
* The next time the callback is scheduled.
* @type {Number}
* @private
2014-07-30 17:54:55 +00:00
*/
this._nextTick = 0;
2014-07-30 17:54:55 +00:00
/**
2018-02-27 22:02:41 +00:00
* The tick counter
* @type {Tone.TickSource}
2014-07-30 17:54:55 +00:00
* @private
*/
2018-02-27 22:02:41 +00:00
this._tickSource = new Tone.TickSource(options.frequency);
/**
* The last time the loop callback was invoked
* @private
* @type {Number}
*/
this._lastUpdate = 0;
2015-08-18 22:14:26 +00:00
/**
2017-10-21 23:02:46 +00:00
* The rate the callback function should be invoked.
2015-08-18 22:14:26 +00:00
* @type {BPM}
* @signal
*/
2018-02-27 22:02:41 +00:00
this.frequency = this._tickSource.frequency;
this._readOnly("frequency");
2015-08-18 22:14:26 +00:00
2014-07-30 17:54:55 +00:00
/**
2015-08-18 22:14:26 +00:00
* The state timeline
* @type {Tone.TimelineState}
* @private
2014-07-30 17:54:55 +00:00
*/
2015-08-18 22:14:26 +00:00
this._state = new Tone.TimelineState(Tone.State.Stopped);
2018-02-27 22:02:41 +00:00
//add an initial state
this._state.setStateAtTime(Tone.State.Stopped, 0);
/**
2017-10-21 23:02:46 +00:00
* The loop function bound to its context.
* This is necessary to remove the event in the end.
* @type {Function}
2015-08-18 22:14:26 +00:00
* @private
*/
2015-08-18 22:14:26 +00:00
this._boundLoop = this._loop.bind(this);
//bind a callback to the worker thread
2017-10-21 23:02:46 +00:00
this.context.on("tick", this._boundLoop);
2014-07-30 17:54:55 +00:00
};
2016-09-24 15:26:57 +00:00
Tone.extend(Tone.Clock, Tone.Emitter);
2014-07-30 17:54:55 +00:00
/**
2015-08-18 22:14:26 +00:00
* The defaults
* @const
* @type {Object}
2014-07-30 17:54:55 +00:00
*/
2015-08-18 22:14:26 +00:00
Tone.Clock.defaults = {
"callback" : Tone.noOp,
"frequency" : 1,
};
/**
* Returns the playback state of the source, either "started", "stopped" or "paused".
* @type {Tone.State}
* @readOnly
* @memberOf Tone.Clock#
* @name state
*/
Object.defineProperty(Tone.Clock.prototype, "state", {
get : function(){
2016-12-19 03:14:14 +00:00
return this._state.getValueAtTime(this.now());
}
2015-08-18 22:14:26 +00:00
});
/**
* Start the clock at the given time. Optionally pass in an offset
* of where to start the tick counter from.
2017-06-21 14:21:38 +00:00
* @param {Time=} time The time the clock should start
2015-08-18 22:14:26 +00:00
* @param {Ticks=} offset Where the tick counter starts counting from.
* @return {Tone.Clock} this
*/
Tone.Clock.prototype.start = function(time, offset){
time = this.toSeconds(time);
2016-12-19 03:14:14 +00:00
if (this._state.getValueAtTime(time) !== Tone.State.Started){
2017-05-28 23:39:18 +00:00
this._state.setStateAtTime(Tone.State.Started, time);
2018-02-27 22:02:41 +00:00
this._tickSource.start(time, offset);
2015-08-18 22:14:26 +00:00
}
2017-10-21 23:02:46 +00:00
return this;
2014-07-30 17:54:55 +00:00
};
/**
2015-08-18 22:14:26 +00:00
* Stop the clock. Stopping the clock resets the tick counter to 0.
2015-06-14 00:20:36 +00:00
* @param {Time} [time=now] The time when the clock should stop.
* @returns {Tone.Clock} this
2015-06-14 00:20:36 +00:00
* @example
* clock.stop();
2014-07-30 17:54:55 +00:00
*/
Tone.Clock.prototype.stop = function(time){
2015-08-18 22:14:26 +00:00
time = this.toSeconds(time);
this._state.cancel(time);
this._state.setStateAtTime(Tone.State.Stopped, time);
2018-02-27 22:02:41 +00:00
this._tickSource.stop(time);
2017-10-21 23:02:46 +00:00
return this;
2014-07-30 17:54:55 +00:00
};
/**
2015-08-18 22:14:26 +00:00
* Pause the clock. Pausing does not reset the tick counter.
* @param {Time} [time=now] The time when the clock should stop.
* @returns {Tone.Clock} this
*/
Tone.Clock.prototype.pause = function(time){
time = this.toSeconds(time);
2016-12-19 03:14:14 +00:00
if (this._state.getValueAtTime(time) === Tone.State.Started){
2015-08-18 22:14:26 +00:00
this._state.setStateAtTime(Tone.State.Paused, time);
2018-02-27 22:02:41 +00:00
this._tickSource.pause(time);
2015-08-18 22:14:26 +00:00
}
2017-10-21 23:02:46 +00:00
return this;
2015-08-18 22:14:26 +00:00
};
/**
* The number of times the callback was invoked. Starts counting at 0
* and increments after the callback was invoked.
* @type {Ticks}
*/
Object.defineProperty(Tone.Clock.prototype, "ticks", {
get : function(){
2018-02-27 22:02:41 +00:00
return Math.ceil(this.getTicksAtTime(this.now()));
},
set : function(t){
2018-02-27 22:02:41 +00:00
this._tickSource.ticks = t;
}
});
/**
* The time since ticks=0 that the Clock has been running. Accounts
* for tempo curves
* @type {Seconds}
*/
Object.defineProperty(Tone.Clock.prototype, "seconds", {
get : function(){
2018-02-27 22:02:41 +00:00
return this._tickSource.seconds;
},
set : function(s){
2018-02-27 22:02:41 +00:00
this._tickSource.seconds = s;
}
});
/**
* Set the clock's ticks at the given time.
* @param {Ticks} ticks The tick value to set
* @param {Time} time When to set the tick value
* @return {Tone.Clock} this
*/
Tone.Clock.prototype.setTicksAtTime = function(ticks, time){
2018-02-27 22:02:41 +00:00
this._tickSource.setTicksAtTime(ticks, time);
return this;
};
2018-02-27 22:02:41 +00:00
/**
* Get the clock's ticks at the given time.
* @param {Time} time When to get the tick value
* @return {Ticks} The tick value at the given time.
*/
Tone.Clock.prototype.getTicksAtTime = function(time){
return this._tickSource.getTicksAtTime(time);
};
/**
* Get the time of the next tick
* @param {Ticks} ticks The tick number.
* @param {Time} before
* @return {Tone.Clock} this
*/
Tone.Clock.prototype.nextTickTime = function(offset, when){
when = this.toSeconds(when);
var currentTick = this.getTicksAtTime(when);
return this._tickSource.getTimeOfTick(currentTick+offset, when);
};
2015-08-18 22:14:26 +00:00
/**
* The scheduling loop.
2014-07-30 17:54:55 +00:00
* @private
*/
Tone.Clock.prototype._loop = function(){
2018-02-27 22:02:41 +00:00
var startTime = this._lastUpdate;
var endTime = this.now() + this.context.lookAhead + this.context.updateInterval;
this._lastUpdate = endTime;
2017-05-28 23:39:18 +00:00
2018-02-27 22:02:41 +00:00
if (startTime !== endTime){
//the state change events
this._state.forEachBetween(startTime, endTime, function(e){
switch (e.state){
case Tone.State.Started :
var offset = this._tickSource.getTicksAtTime(e.time);
this.emit("start", e.time, offset);
2017-05-28 23:39:18 +00:00
break;
2018-02-27 22:02:41 +00:00
case Tone.State.Stopped :
if (e.time !== 0){
this.emit("stop", e.time);
}
2017-05-28 23:39:18 +00:00
break;
2018-02-27 22:02:41 +00:00
case Tone.State.Paused :
this.emit("pause", e.time);
2017-05-28 23:39:18 +00:00
break;
2015-08-18 22:14:26 +00:00
}
2018-02-27 22:02:41 +00:00
}.bind(this));
//the tick callbacks
this._tickSource.forEachTickBetween(startTime, endTime, function(time, ticks){
this.callback(time, ticks);
}.bind(this));
2014-07-30 17:54:55 +00:00
}
};
/**
2015-08-18 22:14:26 +00:00
* Returns the scheduled state at the given time.
* @param {Time} time The time to query.
* @return {String} The name of the state input in setStateAtTime.
* @example
* clock.start("+0.1");
* clock.getStateAtTime("+0.1"); //returns "started"
2015-08-18 22:14:26 +00:00
*/
Tone.Clock.prototype.getStateAtTime = function(time){
time = this.toSeconds(time);
2016-12-19 03:14:14 +00:00
return this._state.getValueAtTime(time);
2015-08-18 22:14:26 +00:00
};
/**
* Clean up
* @returns {Tone.Clock} this
2014-07-30 17:54:55 +00:00
*/
Tone.Clock.prototype.dispose = function(){
2016-09-24 15:26:57 +00:00
Tone.Emitter.prototype.dispose.call(this);
this.context.off("tick", this._boundLoop);
2015-08-18 22:14:26 +00:00
this._writable("frequency");
2018-02-27 22:02:41 +00:00
this._tickSource.dispose();
this._tickSource = null;
2015-02-10 21:33:18 +00:00
this.frequency = null;
this._boundLoop = null;
2015-08-18 22:14:26 +00:00
this._nextTick = Infinity;
this.callback = null;
this._state.dispose();
this._state = null;
2014-07-30 17:54:55 +00:00
};
return Tone.Clock;
2017-10-21 23:02:46 +00:00
});