2014-07-30 17:54:55 +00:00
|
|
|
define(["Tone/core/Tone", "Tone/signal/Signal"], function(Tone){
|
2014-09-04 04:41:40 +00:00
|
|
|
|
|
|
|
"use strict";
|
2014-07-30 17:54:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @class a sample accurate clock built on an oscillator.
|
2015-01-05 03:19:33 +00:00
|
|
|
* Invokes the tick method at the set rate
|
2014-07-30 17:54:55 +00:00
|
|
|
*
|
|
|
|
* @internal
|
|
|
|
* @constructor
|
|
|
|
* @extends {Tone}
|
2014-09-16 01:45:31 +00:00
|
|
|
* @param {number} rate 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
|
|
|
*/
|
|
|
|
Tone.Clock = function(rate, callback){
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._controlSignal = new Tone.Signal(1);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)}
|
|
|
|
*/
|
|
|
|
this.tick = this.defaultArg(callback, function(){});
|
|
|
|
|
|
|
|
//setup
|
|
|
|
this._jsNode.noGC();
|
|
|
|
this.setRate(rate);
|
|
|
|
};
|
|
|
|
|
|
|
|
Tone.extend(Tone.Clock);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* set the rate of the clock
|
|
|
|
* optionally ramp to the rate over the rampTime
|
|
|
|
* @param {Tone.Time} rate
|
|
|
|
* @param {Tone.Time=} rampTime
|
2015-01-06 04:33:05 +00:00
|
|
|
* @returns {Tone.Clock} `this`
|
2014-07-30 17:54:55 +00:00
|
|
|
*/
|
|
|
|
Tone.Clock.prototype.setRate = function(rate, rampTime){
|
|
|
|
//convert the time to a to frequency
|
|
|
|
var freqVal = this.secondsToFrequency(this.toSeconds(rate));
|
|
|
|
if (!rampTime){
|
|
|
|
this._controlSignal.cancelScheduledValues(0);
|
|
|
|
this._controlSignal.setValue(freqVal);
|
|
|
|
} else {
|
|
|
|
this._controlSignal.exponentialRampToValueNow(freqVal, rampTime);
|
|
|
|
}
|
2015-01-06 04:33:05 +00:00
|
|
|
return this;
|
2014-07-30 17:54:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return the current rate
|
|
|
|
*
|
|
|
|
* @return {number}
|
|
|
|
*/
|
|
|
|
Tone.Clock.prototype.getRate = function(){
|
|
|
|
return this._controlSignal.getValue();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* start the clock
|
|
|
|
* @param {Tone.Time} time 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){
|
2015-01-05 03:19:33 +00:00
|
|
|
if (!this._oscillator){
|
|
|
|
this._oscillator = this.context.createOscillator();
|
|
|
|
this._oscillator.type = "square";
|
|
|
|
this._oscillator.connect(this._jsNode);
|
|
|
|
//connect it up
|
|
|
|
this._controlSignal.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 the time when the clock should stop
|
2014-09-14 19:33:32 +00:00
|
|
|
* @param {function} onend called when the oscilator stops
|
2015-01-06 04:33:05 +00:00
|
|
|
* @returns {Tone.Clock} `this`
|
2014-07-30 17:54:55 +00:00
|
|
|
*/
|
2014-09-14 19:33:32 +00:00
|
|
|
Tone.Clock.prototype.stop = function(time, onend){
|
2015-01-05 03:19:33 +00:00
|
|
|
if (this._oscillator){
|
|
|
|
var now = this.now();
|
|
|
|
var stopTime = this.toSeconds(time, now);
|
|
|
|
this._oscillator.stop(stopTime);
|
|
|
|
this._oscillator = null;
|
|
|
|
//set a timeout for when it stops
|
|
|
|
if (time){
|
|
|
|
setTimeout(onend, (stopTime - now) * 1000);
|
|
|
|
} else {
|
|
|
|
onend();
|
|
|
|
}
|
|
|
|
}
|
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;
|
2014-09-16 01:45:31 +00:00
|
|
|
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;
|
2014-09-16 01:45:31 +00:00
|
|
|
//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(){
|
|
|
|
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();
|
|
|
|
this._controlSignal.dispose();
|
|
|
|
if (this._oscillator){
|
|
|
|
this._oscillator.disconnect();
|
|
|
|
}
|
|
|
|
this._jsNode.onaudioprocess = function(){};
|
|
|
|
this._jsNode = null;
|
|
|
|
this._controlSignal = null;
|
|
|
|
this._oscillator = null;
|
2015-01-06 04:33:05 +00:00
|
|
|
return this;
|
2014-07-30 17:54:55 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
return Tone.Clock;
|
|
|
|
});
|