Tone.js/Tone/core/Clock.js

158 lines
3.8 KiB
JavaScript
Raw Normal View History

2014-07-30 17:54:55 +00:00
define(["Tone/core/Tone", "Tone/signal/Signal"], function(Tone){
"use strict";
2014-07-30 17:54:55 +00:00
/**
* @class a sample accurate clock built on an oscillator.
* Invokes the tick method at the set rate
2014-07-30 17:54:55 +00:00
*
* @constructor
* @extends {Tone}
2015-02-10 21:33:18 +00:00
* @param {Tone.Frequency} frequency the rate of the callback
* @param {function} callback the callback to be invoked with the time of the audio event
2014-07-30 17:54:55 +00:00
*/
2015-02-10 21:33:18 +00:00
Tone.Clock = function(frequency, callback){
2014-07-30 17:54:55 +00:00
/**
* the oscillator
* @type {OscillatorNode}
2014-09-05 15:32:33 +00:00
* @private
2014-07-30 17:54:55 +00:00
*/
this._oscillator = null;
/**
* the script processor which listens to the oscillator
2014-09-04 19:22:25 +00:00
* @type {ScriptProcessorNode}
2014-07-30 17:54:55 +00:00
* @private
*/
this._jsNode = this.context.createScriptProcessor(this.bufferSize, 1, 1);
this._jsNode.onaudioprocess = this._processBuffer.bind(this);
/**
* the rate control signal
* @type {Tone.Signal}
*/
this.frequency = new Tone.Signal(frequency, Tone.Signal.Units.Frequency);
2014-07-30 17:54:55 +00:00
/**
* whether the tick is on the up or down
* @type {boolean}
* @private
*/
this._upTick = false;
/**
* the callback which is invoked on every tick
* with the time of that tick as the argument
* @type {function(number)}
*/
2015-02-10 21:33:18 +00:00
this.tick = callback;
2014-07-30 17:54:55 +00:00
/**
* Callback is invoked when the clock is stopped.
* @type {function}
* @example
* clock.onended = function(){
* console.log("the clock is stopped");
* }
*/
this.onended = function(){};
2014-07-30 17:54:55 +00:00
//setup
this._jsNode.noGC();
};
Tone.extend(Tone.Clock);
/**
* start the clock
* @param {Tone.Time} [time=now] the time when the clock should start
2015-01-06 04:33:05 +00:00
* @returns {Tone.Clock} `this`
2014-07-30 17:54:55 +00:00
*/
Tone.Clock.prototype.start = function(time){
if (!this._oscillator){
this._oscillator = this.context.createOscillator();
this._oscillator.type = "square";
this._oscillator.connect(this._jsNode);
//connect it up
2015-02-10 21:33:18 +00:00
this.frequency.connect(this._oscillator.frequency);
this._upTick = false;
var startTime = this.toSeconds(time);
this._oscillator.start(startTime);
}
2015-01-06 04:33:05 +00:00
return this;
2014-07-30 17:54:55 +00:00
};
/**
* stop the clock
* @param {Tone.Time} [time=now] The time when the clock should stop.
2015-01-06 04:33:05 +00:00
* @returns {Tone.Clock} `this`
2014-07-30 17:54:55 +00:00
*/
Tone.Clock.prototype.stop = function(time){
if (this._oscillator){
var now = this.now();
var stopTime = this.toSeconds(time, now);
this._oscillator.stop(stopTime);
this._oscillator = null;
if (time){
//set a timeout for when it stops
setTimeout(this.onended.bind(this), (stopTime - now) * 1000);
} else {
this.onended();
}
}
2015-01-06 04:33:05 +00:00
return this;
2014-07-30 17:54:55 +00:00
};
/**
* @private
* @param {AudioProcessingEvent} event
*/
Tone.Clock.prototype._processBuffer = function(event){
var now = this.defaultArg(event.playbackTime, this.now());
var bufferSize = this._jsNode.bufferSize;
var incomingBuffer = event.inputBuffer.getChannelData(0);
var upTick = this._upTick;
var self = this;
2014-07-30 17:54:55 +00:00
for (var i = 0; i < bufferSize; i++){
var sample = incomingBuffer[i];
if (sample > 0 && !upTick){
upTick = true;
//get the callback out of audio thread
setTimeout(function(){
//to account for the double buffering
var tickTime = now + self.samplesToSeconds(i + bufferSize * 2);
return function(){
2015-02-10 21:33:18 +00:00
if (self.tick){
self.tick(tickTime);
}
};
}(), 0); // jshint ignore:line
2014-07-30 17:54:55 +00:00
} else if (sample < 0 && upTick){
upTick = false;
}
}
this._upTick = upTick;
};
/**
* clean up
2015-01-06 04:33:05 +00:00
* @returns {Tone.Clock} `this`
2014-07-30 17:54:55 +00:00
*/
Tone.Clock.prototype.dispose = function(){
this._jsNode.disconnect();
2015-02-10 21:33:18 +00:00
this.frequency.dispose();
this.frequency = null;
2014-07-30 17:54:55 +00:00
if (this._oscillator){
this._oscillator.disconnect();
2015-02-10 21:33:18 +00:00
this._oscillator = null;
2014-07-30 17:54:55 +00:00
}
this._jsNode.onaudioprocess = function(){};
this._jsNode = null;
2015-02-10 21:33:18 +00:00
this.tick = null;
this.onended = function(){};
2015-01-06 04:33:05 +00:00
return this;
2014-07-30 17:54:55 +00:00
};
return Tone.Clock;
});