Tone.js/Tone/core/Transport.js

754 lines
21 KiB
JavaScript
Raw Normal View History

define(["Tone/core/Tone", "Tone/core/Clock", "Tone/core/Type", "Tone/core/Timeline",
2015-10-27 21:46:34 +00:00
"Tone/core/Emitter", "Tone/core/Gain", "Tone/core/IntervalTimeline"],
function(Tone){
"use strict";
/**
* @class Transport for timing musical events.
* Supports tempo curves and time changes. Unlike browser-based timing (setInterval, requestAnimationFrame)
2015-06-14 05:45:12 +00:00
* Tone.Transport timing events pass in the exact time of the scheduled event
* in the argument of the callback function. Pass that time value to the object
2015-06-20 19:50:57 +00:00
* you're scheduling. <br><br>
* A single transport is created for you when the library is initialized.
* <br><br>
* The transport emits the events: "start", "stop", "pause", and "loop" which are
* called with the time of that event as the argument.
*
2015-10-27 21:46:34 +00:00
* @extends {Tone.Emitter}
2015-06-20 19:50:57 +00:00
* @singleton
2015-06-14 05:45:12 +00:00
* @example
* //repeated event every 8th note
2016-03-03 18:08:26 +00:00
* Tone.Transport.scheduleRepeat(function(time){
2015-06-14 05:45:12 +00:00
* //do something with the time
* }, "8n");
* @example
2016-03-03 18:08:26 +00:00
* //schedule an event on the 16th measure
* Tone.Transport.schedule(function(time){
2015-06-14 05:45:12 +00:00
* //do something with the time
* }, "16:0:0");
*/
2014-09-11 02:37:57 +00:00
Tone.Transport = function(){
2015-10-27 21:46:34 +00:00
Tone.Emitter.call(this);
2015-08-17 00:31:11 +00:00
///////////////////////////////////////////////////////////////////////
// LOOPING
//////////////////////////////////////////////////////////////////////
/**
* If the transport loops or not.
* @type {boolean}
*/
this.loop = false;
/**
* The loop start position in ticks
* @type {Ticks}
* @private
*/
this._loopStart = 0;
/**
* The loop end position in ticks
* @type {Ticks}
* @private
*/
this._loopEnd = 0;
///////////////////////////////////////////////////////////////////////
// CLOCK/TEMPO
//////////////////////////////////////////////////////////////////////
/**
* Pulses per quarter is the number of ticks per quarter note.
* @private
* @type {Number}
*/
this._ppq = TransportConstructor.defaults.PPQ;
/**
* watches the main oscillator for timing ticks
2014-07-30 17:54:55 +00:00
* initially starts at 120bpm
* @private
2014-07-30 17:54:55 +00:00
* @type {Tone.Clock}
*/
2015-08-18 20:30:50 +00:00
this._clock = new Tone.Clock({
"callback" : this._processTick.bind(this),
"frequency" : 0,
});
/**
2015-06-20 19:50:57 +00:00
* The Beats Per Minute of the Transport.
2015-06-13 23:50:39 +00:00
* @type {BPM}
* @signal
2015-06-20 19:50:57 +00:00
* @example
* Tone.Transport.bpm.value = 80;
* //ramp the bpm to 120 over 10 seconds
* Tone.Transport.bpm.rampTo(120, 10);
2015-02-21 19:05:12 +00:00
*/
2015-08-18 20:30:50 +00:00
this.bpm = this._clock.frequency;
this.bpm._toUnits = this._toUnits.bind(this);
this.bpm._fromUnits = this._fromUnits.bind(this);
this.bpm.units = Tone.Type.BPM;
this.bpm.value = TransportConstructor.defaults.bpm;
this._readOnly("bpm");
2015-02-21 19:05:12 +00:00
/**
2015-08-17 00:31:11 +00:00
* The time signature, or more accurately the numerator
* of the time signature over a denominator of 4.
* @type {Number}
* @private
*/
2015-08-17 00:31:11 +00:00
this._timeSignature = TransportConstructor.defaults.timeSignature;
2015-02-21 19:05:12 +00:00
2015-08-17 00:31:11 +00:00
///////////////////////////////////////////////////////////////////////
// TIMELINE EVENTS
//////////////////////////////////////////////////////////////////////
/**
2015-08-18 22:15:19 +00:00
* All the events in an object to keep track by ID
* @type {Object}
2015-08-17 00:31:11 +00:00
* @private
*/
this._scheduledEvents = {};
2015-08-17 00:31:11 +00:00
/**
2015-08-18 22:15:19 +00:00
* The event ID counter
2015-08-17 00:31:11 +00:00
* @type {Number}
* @private
*/
2015-08-18 22:15:19 +00:00
this._eventID = 0;
2015-08-17 00:31:11 +00:00
/**
2015-08-18 22:15:19 +00:00
* The scheduled events.
* @type {Tone.Timeline}
2015-08-17 00:31:11 +00:00
* @private
*/
2015-08-18 22:15:19 +00:00
this._timeline = new Tone.Timeline();
2015-08-17 00:31:11 +00:00
/**
2015-08-18 22:15:19 +00:00
* Repeated events
2015-08-17 00:31:11 +00:00
* @type {Array}
* @private
*/
this._repeatedEvents = new Tone.IntervalTimeline();
2015-08-17 00:31:11 +00:00
/**
2015-08-18 22:15:19 +00:00
* Events that occur once
* @type {Array}
2015-08-17 00:31:11 +00:00
* @private
*/
2015-08-18 22:15:19 +00:00
this._onceEvents = new Tone.Timeline();
2015-08-17 00:31:11 +00:00
/**
* All of the synced Signals
* @private
* @type {Array}
*/
this._syncedSignals = [];
2015-08-17 00:31:11 +00:00
///////////////////////////////////////////////////////////////////////
// SWING
//////////////////////////////////////////////////////////////////////
2015-10-27 21:46:34 +00:00
var swingSeconds = this.notationToSeconds(TransportConstructor.defaults.swingSubdivision, TransportConstructor.defaults.bpm, TransportConstructor.defaults.timeSignature);
2015-08-17 00:31:11 +00:00
/**
* The subdivision of the swing
* @type {Ticks}
* @private
*/
2015-10-27 21:46:34 +00:00
this._swingTicks = (swingSeconds / (60 / TransportConstructor.defaults.bpm)) * this._ppq;
2015-08-17 00:31:11 +00:00
/**
* The swing amount
* @type {NormalRange}
* @private
*/
this._swingAmount = 0;
};
2015-10-27 21:46:34 +00:00
Tone.extend(Tone.Transport, Tone.Emitter);
2015-02-24 03:14:22 +00:00
/**
* the defaults
* @type {Object}
* @const
* @static
*/
Tone.Transport.defaults = {
"bpm" : 120,
"swing" : 0,
"swingSubdivision" : "16n",
"timeSignature" : 4,
"loopStart" : 0,
2015-08-17 00:31:11 +00:00
"loopEnd" : "4m",
"PPQ" : 192
2015-02-24 03:14:22 +00:00
};
2014-04-06 00:47:59 +00:00
///////////////////////////////////////////////////////////////////////////////
2014-07-30 17:54:55 +00:00
// TICKS
2014-04-06 00:47:59 +00:00
///////////////////////////////////////////////////////////////////////////////
/**
2014-07-30 17:54:55 +00:00
* called on every tick
* @param {number} tickTime clock relative tick time
* @private
*/
2014-09-11 02:37:57 +00:00
Tone.Transport.prototype._processTick = function(tickTime){
2015-08-17 00:31:11 +00:00
//handle swing
if (this._swingAmount > 0 &&
2015-08-18 20:30:50 +00:00
this._clock.ticks % this._ppq !== 0 && //not on a downbeat
this._clock.ticks % this._swingTicks === 0){
2015-08-17 00:31:11 +00:00
//add some swing
tickTime += this.ticksToSeconds(this._swingTicks) * this._swingAmount;
}
2015-08-18 20:30:50 +00:00
//do the loop test
if (this.loop){
if (this._clock.ticks === this._loopEnd){
2015-08-18 22:15:19 +00:00
this.ticks = this._loopStart;
2015-08-31 19:19:03 +00:00
this.trigger("loop", tickTime);
2015-08-18 20:30:50 +00:00
}
}
var ticks = this._clock.ticks;
//process the single occurrence events
this._onceEvents.forEachBefore(ticks, function(event){
event.callback(tickTime);
});
//and clear the single occurrence timeline
this._onceEvents.cancelBefore(ticks);
2015-08-17 00:31:11 +00:00
//fire the next tick events if their time has come
2015-08-18 22:15:19 +00:00
this._timeline.forEachAtTime(ticks, function(event){
event.callback(tickTime);
});
2015-08-17 00:31:11 +00:00
//process the repeated events
this._repeatedEvents.forEachOverlap(ticks, function(event){
2015-08-18 22:15:19 +00:00
if ((ticks - event.time) % event.interval === 0){
event.callback(tickTime);
2014-04-06 00:47:59 +00:00
}
2015-08-18 22:15:19 +00:00
});
};
2014-03-19 21:25:33 +00:00
2014-04-06 00:47:59 +00:00
///////////////////////////////////////////////////////////////////////////////
2015-08-18 22:15:19 +00:00
// SCHEDULABLE EVENTS
///////////////////////////////////////////////////////////////////////////////
2014-03-19 21:25:33 +00:00
/**
2015-08-17 00:31:11 +00:00
* Schedule an event along the timeline.
2015-12-08 05:07:16 +00:00
* @param {Function} callback The callback to be invoked at the time.
* @param {TimelinePosition} time The time to invoke the callback at.
2015-08-17 00:31:11 +00:00
* @return {Number} The id of the event which can be used for canceling the event.
2015-02-28 04:24:51 +00:00
* @example
2015-12-08 05:07:16 +00:00
* //trigger the callback when the Transport reaches the desired time
2015-11-11 04:02:01 +00:00
* Tone.Transport.schedule(function(time){
* envelope.triggerAttack(time);
* }, "128i");
*/
2015-08-17 00:31:11 +00:00
Tone.Transport.prototype.schedule = function(callback, time){
2015-08-18 22:15:19 +00:00
var event = {
"time" : this.toTicks(time),
"callback" : callback
};
var id = this._eventID++;
this._scheduledEvents[id.toString()] = {
2015-08-18 22:15:19 +00:00
"event" : event,
"timeline" : this._timeline
};
this._timeline.addEvent(event);
return id;
};
2014-04-06 00:47:59 +00:00
2014-06-18 20:45:25 +00:00
/**
2015-11-11 04:02:01 +00:00
* Schedule a repeated event along the timeline. The event will fire
* at the `interval` starting at the `startTime` and for the specified
* `duration`.
2015-08-17 00:31:11 +00:00
* @param {Function} callback The callback to invoke.
* @param {Time} interval The duration between successive
* callbacks.
* @param {TimelinePosition=} startTime When along the timeline the events should
2015-08-17 00:31:11 +00:00
* start being invoked.
* @param {Time} [duration=Infinity] How long the event should repeat.
2015-08-17 00:31:11 +00:00
* @return {Number} The ID of the scheduled event. Use this to cancel
* the event.
2015-11-11 04:02:01 +00:00
* @example
* //a callback invoked every eighth note after the first measure
* Tone.Transport.scheduleRepeat(callback, "8n", "1m");
2014-06-18 20:45:25 +00:00
*/
Tone.Transport.prototype.scheduleRepeat = function(callback, interval, startTime, duration){
if (interval <= 0){
2015-08-18 22:15:19 +00:00
throw new Error("repeat events must have an interval larger than 0");
}
var event = {
"time" : this.toTicks(startTime),
"duration" : this.toTicks(this.defaultArg(duration, Infinity)),
2015-08-18 22:15:19 +00:00
"interval" : this.toTicks(interval),
"callback" : callback
};
var id = this._eventID++;
this._scheduledEvents[id.toString()] = {
2015-08-18 22:15:19 +00:00
"event" : event,
"timeline" : this._repeatedEvents
};
this._repeatedEvents.addEvent(event);
return id;
2014-06-18 20:45:25 +00:00
};
/**
2015-08-18 20:30:50 +00:00
* Schedule an event that will be removed after it is invoked.
* Note that if the given time is less than the current transport time,
* the event will be invoked immediately.
* @param {Function} callback The callback to invoke once.
* @param {TimelinePosition} time The time the callback should be invoked.
2015-08-18 20:30:50 +00:00
* @returns {Number} The ID of the scheduled event.
*/
2015-08-17 00:31:11 +00:00
Tone.Transport.prototype.scheduleOnce = function(callback, time){
2015-08-18 22:15:19 +00:00
var event = {
"time" : this.toTicks(time),
"callback" : callback
};
var id = this._eventID++;
this._scheduledEvents[id.toString()] = {
2015-08-18 22:15:19 +00:00
"event" : event,
"timeline" : this._onceEvents
};
this._onceEvents.addEvent(event);
return id;
};
/**
* Clear the passed in event id from the timeline
2015-08-18 20:30:50 +00:00
* @param {Number} eventId The id of the event.
2015-08-18 22:15:19 +00:00
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.clear = function(eventId){
if (this._scheduledEvents.hasOwnProperty(eventId)){
var item = this._scheduledEvents[eventId.toString()];
2015-12-05 18:10:41 +00:00
item.timeline.removeEvent(item.event);
delete this._scheduledEvents[eventId.toString()];
}
2015-08-18 22:15:19 +00:00
return this;
};
2014-06-18 20:45:25 +00:00
/**
2015-08-17 00:31:11 +00:00
* Remove scheduled events from the timeline after
* the given time. Repeated events will be removed
* if their startTime is after the given time
* @param {TimelinePosition} [after=0] Clear all events after
2015-08-17 00:31:11 +00:00
* this time.
* @returns {Tone.Transport} this
*/
Tone.Transport.prototype.cancel = function(after){
2015-08-17 00:31:11 +00:00
after = this.defaultArg(after, 0);
after = this.toTicks(after);
this._timeline.cancel(after);
this._onceEvents.cancel(after);
this._repeatedEvents.cancel(after);
2015-08-17 00:31:11 +00:00
return this;
};
2014-04-06 00:47:59 +00:00
///////////////////////////////////////////////////////////////////////////////
// START/STOP/PAUSE
///////////////////////////////////////////////////////////////////////////////
2015-08-17 00:31:11 +00:00
/**
* Returns the playback state of the source, either "started", "stopped", or "paused"
2015-10-27 21:46:34 +00:00
* @type {Tone.State}
2015-08-17 00:31:11 +00:00
* @readOnly
2015-10-27 21:46:34 +00:00
* @memberOf Tone.Transport#
2015-08-17 00:31:11 +00:00
* @name state
*/
Object.defineProperty(Tone.Transport.prototype, "state", {
get : function(){
2015-08-18 20:30:50 +00:00
return this._clock.getStateAtTime(this.now());
2015-08-17 00:31:11 +00:00
}
});
/**
2015-06-20 19:50:57 +00:00
* Start the transport and all sources synced to the transport.
* @param {Time} [time=now] The time when the transport should start.
* @param {Time=} offset The timeline offset to start the transport.
* @returns {Tone.Transport} this
2015-06-20 19:50:57 +00:00
* @example
* //start the transport in one second starting at beginning of the 5th measure.
* Tone.Transport.start("+1", "4:0:0");
*/
Tone.Transport.prototype.start = function(time, offset){
2015-08-17 00:31:11 +00:00
time = this.toSeconds(time);
2015-08-18 22:15:19 +00:00
if (!this.isUndef(offset)){
offset = this.toTicks(offset);
} else {
2015-08-18 20:30:50 +00:00
offset = this.defaultArg(offset, this._clock.ticks);
2014-06-18 05:35:34 +00:00
}
2015-08-18 22:15:19 +00:00
//start the clock
this._clock.start(time, offset);
this.trigger("start", time, this.ticksToSeconds(offset));
2015-01-06 04:33:05 +00:00
return this;
};
/**
2015-06-20 19:50:57 +00:00
* Stop the transport and all sources synced to the transport.
* @param {Time} [time=now] The time when the transport should stop.
* @returns {Tone.Transport} this
2015-06-20 19:50:57 +00:00
* @example
* Tone.Transport.stop();
*/
2014-09-11 02:37:57 +00:00
Tone.Transport.prototype.stop = function(time){
time = this.toSeconds(time);
2015-08-18 20:30:50 +00:00
this._clock.stop(time);
this.trigger("stop", time);
2015-01-06 04:33:05 +00:00
return this;
};
/**
2015-06-20 19:50:57 +00:00
* Pause the transport and all sources synced to the transport.
* @param {Time} [time=now]
* @returns {Tone.Transport} this
*/
2014-09-11 02:37:57 +00:00
Tone.Transport.prototype.pause = function(time){
time = this.toSeconds(time);
2015-08-18 20:30:50 +00:00
this._clock.pause(time);
this.trigger("pause", time);
2015-01-06 04:33:05 +00:00
return this;
};
2014-03-19 21:25:33 +00:00
2014-04-06 00:47:59 +00:00
///////////////////////////////////////////////////////////////////////////////
// SETTERS/GETTERS
2014-04-06 00:47:59 +00:00
///////////////////////////////////////////////////////////////////////////////
/**
2015-06-20 19:50:57 +00:00
* The time signature as just the numerator over 4.
2015-02-21 19:05:12 +00:00
* For example 4/4 would be just 4 and 6/8 would be 3.
* @memberOf Tone.Transport#
2015-11-11 04:02:01 +00:00
* @type {Number|Array}
2015-02-21 19:05:12 +00:00
* @name timeSignature
2015-06-20 19:50:57 +00:00
* @example
* //common time
* Tone.Transport.timeSignature = 4;
* // 7/8
2015-11-11 04:02:01 +00:00
* Tone.Transport.timeSignature = [7, 8];
* //this will be reduced to a single number
* Tone.Transport.timeSignature; //returns 3.5
2015-02-21 19:05:12 +00:00
*/
Object.defineProperty(Tone.Transport.prototype, "timeSignature", {
get : function(){
2015-08-17 00:31:11 +00:00
return this._timeSignature;
2015-02-21 19:05:12 +00:00
},
2015-08-17 00:31:11 +00:00
set : function(timeSig){
2015-11-11 04:02:01 +00:00
if (this.isArray(timeSig)){
2015-08-17 00:31:11 +00:00
timeSig = (timeSig[0] / timeSig[1]) * 4;
}
this._timeSignature = timeSig;
2015-02-10 21:33:18 +00:00
}
2015-02-21 19:05:12 +00:00
});
/**
2015-06-20 19:50:57 +00:00
* When the Tone.Transport.loop = true, this is the starting position of the loop.
2015-02-21 19:05:12 +00:00
* @memberOf Tone.Transport#
2015-06-14 00:20:36 +00:00
* @type {Time}
2015-02-21 19:05:12 +00:00
* @name loopStart
*/
Object.defineProperty(Tone.Transport.prototype, "loopStart", {
get : function(){
2015-08-17 00:31:11 +00:00
return this.ticksToSeconds(this._loopStart);
2015-02-21 19:05:12 +00:00
},
set : function(startPosition){
2015-08-17 00:31:11 +00:00
this._loopStart = this.toTicks(startPosition);
2015-02-21 19:05:12 +00:00
}
});
/**
2015-06-20 19:50:57 +00:00
* When the Tone.Transport.loop = true, this is the ending position of the loop.
2015-02-21 19:05:12 +00:00
* @memberOf Tone.Transport#
2015-06-14 00:20:36 +00:00
* @type {Time}
2015-02-21 19:05:12 +00:00
* @name loopEnd
*/
Object.defineProperty(Tone.Transport.prototype, "loopEnd", {
get : function(){
2015-08-17 00:31:11 +00:00
return this.ticksToSeconds(this._loopEnd);
2015-02-21 19:05:12 +00:00
},
set : function(endPosition){
2015-08-17 00:31:11 +00:00
this._loopEnd = this.toTicks(endPosition);
2015-02-21 19:05:12 +00:00
}
});
/**
2015-06-20 19:50:57 +00:00
* Set the loop start and stop at the same time.
* @param {TimelinePosition} startPosition
* @param {TimelinePosition} endPosition
* @returns {Tone.Transport} this
2015-06-20 19:50:57 +00:00
* @example
* //loop over the first measure
* Tone.Transport.setLoopPoints(0, "1m");
* Tone.Transport.loop = true;
*/
2014-09-11 02:37:57 +00:00
Tone.Transport.prototype.setLoopPoints = function(startPosition, endPosition){
2015-02-21 19:05:12 +00:00
this.loopStart = startPosition;
this.loopEnd = endPosition;
2015-01-06 04:33:05 +00:00
return this;
};
2014-09-25 03:46:57 +00:00
/**
2015-02-21 19:05:12 +00:00
* The swing value. Between 0-1 where 1 equal to
* the note + half the subdivision.
* @memberOf Tone.Transport#
2015-06-14 00:20:36 +00:00
* @type {NormalRange}
2015-02-21 19:05:12 +00:00
* @name swing
*/
Object.defineProperty(Tone.Transport.prototype, "swing", {
get : function(){
2015-08-17 00:31:11 +00:00
return this._swingAmount * 2;
2015-02-21 19:05:12 +00:00
},
set : function(amount){
//scale the values to a normal range
2015-08-17 00:31:11 +00:00
this._swingAmount = amount * 0.5;
2015-02-21 19:05:12 +00:00
}
});
2014-09-25 03:46:57 +00:00
/**
2015-02-21 19:05:12 +00:00
* Set the subdivision which the swing will be applied to.
* The default values is a 16th note. Value must be less
* than a quarter note.
2014-09-30 03:42:56 +00:00
*
2015-02-21 19:05:12 +00:00
* @memberOf Tone.Transport#
2015-06-14 00:20:36 +00:00
* @type {Time}
2015-02-21 19:05:12 +00:00
* @name swingSubdivision
*/
Object.defineProperty(Tone.Transport.prototype, "swingSubdivision", {
get : function(){
2015-08-17 00:31:11 +00:00
return this.toNotation(this._swingTicks + "i");
2015-02-21 19:05:12 +00:00
},
set : function(subdivision){
2015-08-17 00:31:11 +00:00
this._swingTicks = this.toTicks(subdivision);
2015-02-21 19:05:12 +00:00
}
});
/**
* The Transport's position in MEASURES:BEATS:SIXTEENTHS.
* Setting the value will jump to that position right away.
*
* @memberOf Tone.Transport#
2015-06-20 19:50:57 +00:00
* @type {TransportTime}
2015-02-21 19:05:12 +00:00
* @name position
*/
Object.defineProperty(Tone.Transport.prototype, "position", {
get : function(){
2015-08-17 00:31:11 +00:00
var quarters = this.ticks / this._ppq;
var measures = Math.floor(quarters / this._timeSignature);
2015-08-18 20:30:50 +00:00
var sixteenths = ((quarters % 1) * 4);
//if the sixteenths aren't a whole number, fix their length
if (sixteenths % 1 > 0){
sixteenths = sixteenths.toFixed(3);
}
2015-08-17 00:31:11 +00:00
quarters = Math.floor(quarters) % this._timeSignature;
2015-02-21 19:05:12 +00:00
var progress = [measures, quarters, sixteenths];
return progress.join(":");
},
set : function(progress){
2015-07-14 01:58:20 +00:00
var ticks = this.toTicks(progress);
2015-08-18 22:15:19 +00:00
this.ticks = ticks;
2015-02-21 19:05:12 +00:00
}
});
2014-09-25 03:46:57 +00:00
2015-10-09 23:09:02 +00:00
/**
* The Transport's loop position as a normalized value. Always
* returns 0 if the transport if loop is not true.
* @memberOf Tone.Transport#
* @name progress
* @type {NormalRange}
*/
Object.defineProperty(Tone.Transport.prototype, "progress", {
get : function(){
if (this.loop){
return (this.ticks - this._loopStart) / (this._loopEnd - this._loopStart);
} else {
return 0;
}
}
});
2015-08-17 00:31:11 +00:00
/**
* The transports current tick position.
*
* @memberOf Tone.Transport#
* @type {Ticks}
* @name ticks
*/
Object.defineProperty(Tone.Transport.prototype, "ticks", {
get : function(){
2015-08-18 20:30:50 +00:00
return this._clock.ticks;
2015-08-17 00:31:11 +00:00
},
set : function(t){
2015-08-18 22:15:19 +00:00
this._clock.ticks = t;
2015-08-17 00:31:11 +00:00
}
});
/**
* Pulses Per Quarter note. This is the smallest resolution
* the Transport timing supports. This should be set once
* on initialization and not set again. Changing this value
* after other objects have been created can cause problems.
*
* @memberOf Tone.Transport#
* @type {Number}
* @name PPQ
*/
Object.defineProperty(Tone.Transport.prototype, "PPQ", {
get : function(){
return this._ppq;
},
set : function(ppq){
this._ppq = ppq;
2015-08-18 20:30:50 +00:00
this.bpm.value = this.bpm.value;
2015-08-17 00:31:11 +00:00
}
});
2015-08-18 20:30:50 +00:00
/**
* Convert from BPM to frequency (factoring in PPQ)
* @param {BPM} bpm The BPM value to convert to frequency
* @return {Frequency} The BPM as a frequency with PPQ factored in.
* @private
*/
Tone.Transport.prototype._fromUnits = function(bpm){
return 1 / (60 / bpm / this.PPQ);
};
2015-08-17 00:31:11 +00:00
/**
2015-08-18 20:30:50 +00:00
* Convert from frequency (with PPQ) into BPM
* @param {Frequency} freq The clocks frequency to convert to BPM
* @return {BPM} The frequency value as BPM.
* @private
2015-08-17 00:31:11 +00:00
*/
2015-08-18 20:30:50 +00:00
Tone.Transport.prototype._toUnits = function(freq){
return (freq / this.PPQ) * 60;
2015-08-17 00:31:11 +00:00
};
2015-08-18 20:30:50 +00:00
///////////////////////////////////////////////////////////////////////////////
// SYNCING
///////////////////////////////////////////////////////////////////////////////
/**
* Returns the time aligned to the next subdivision
* of the Transport. If the Transport is not started,
* it will return 0.
* Note: this will not work precisely during tempo ramps.
* @param {Time} subdivision The subdivision to quantize to
* @return {Number} The context time of the next subdivision.
* @example
* Tone.Transport.start(); //the transport must be started
* Tone.Transport.nextSubdivision("4n");
*/
Tone.Transport.prototype.nextSubdivision = function(subdivision){
subdivision = this.toSeconds(subdivision);
//if the transport's not started, return 0
var now;
if (this.state === Tone.State.Started){
now = this._clock._nextTick;
} else {
return 0;
}
var transportPos = this.ticksToSeconds(this.ticks);
var remainingTime = subdivision - (transportPos % subdivision);
if (remainingTime === subdivision){
remainingTime = 0;
}
return now + remainingTime;
};
2014-07-30 17:54:55 +00:00
/**
2015-06-20 19:50:57 +00:00
* Attaches the signal to the tempo control signal so that
2014-07-30 17:54:55 +00:00
* any changes in the tempo will change the signal in the same
2015-02-21 19:05:12 +00:00
* ratio.
2014-07-30 17:54:55 +00:00
*
* @param {Tone.Signal} signal
2015-02-21 19:05:12 +00:00
* @param {number=} ratio Optionally pass in the ratio between
* the two signals. Otherwise it will be computed
* based on their current values.
* @returns {Tone.Transport} this
2014-07-30 17:54:55 +00:00
*/
2015-02-21 19:05:12 +00:00
Tone.Transport.prototype.syncSignal = function(signal, ratio){
if (!ratio){
//get the sync ratio
2015-10-21 14:29:29 +00:00
if (signal._param.value !== 0){
ratio = signal._param.value / this.bpm._param.value;
2015-02-21 19:05:12 +00:00
} else {
ratio = 0;
}
}
var ratioSignal = new Tone.Gain(ratio);
2015-10-21 14:29:29 +00:00
this.bpm.chain(ratioSignal, signal._param);
this._syncedSignals.push({
2015-02-21 19:05:12 +00:00
"ratio" : ratioSignal,
"signal" : signal,
2015-10-21 14:29:29 +00:00
"initial" : signal._param.value
2015-02-21 19:05:12 +00:00
});
2015-10-21 14:29:29 +00:00
signal._param.value = 0;
2015-02-21 19:05:12 +00:00
return this;
};
/**
2015-06-20 19:50:57 +00:00
* Unsyncs a previously synced signal from the transport's control.
* See Tone.Transport.syncSignal.
2015-02-21 19:05:12 +00:00
* @param {Tone.Signal} signal
* @returns {Tone.Transport} this
2015-02-21 19:05:12 +00:00
*/
Tone.Transport.prototype.unsyncSignal = function(signal){
for (var i = this._syncedSignals.length - 1; i >= 0; i--){
var syncedSignal = this._syncedSignals[i];
2015-02-21 19:05:12 +00:00
if (syncedSignal.signal === signal){
syncedSignal.ratio.dispose();
2015-10-21 14:29:29 +00:00
syncedSignal.signal._param.value = syncedSignal.initial;
this._syncedSignals.splice(i, 1);
2015-02-21 19:05:12 +00:00
}
}
2015-01-06 04:33:05 +00:00
return this;
2014-07-30 17:54:55 +00:00
};
2014-03-19 21:25:33 +00:00
2014-09-11 17:38:41 +00:00
/**
2015-06-20 19:50:57 +00:00
* Clean up.
* @returns {Tone.Transport} this
2015-06-20 19:50:57 +00:00
* @private
2014-09-11 17:38:41 +00:00
*/
Tone.Transport.prototype.dispose = function(){
2015-10-27 21:46:34 +00:00
Tone.Emitter.prototype.dispose.call(this);
2014-09-11 17:38:41 +00:00
this._clock.dispose();
this._clock = null;
2015-08-28 22:33:20 +00:00
this._writable("bpm");
2015-02-21 19:05:12 +00:00
this.bpm = null;
2015-08-18 22:15:19 +00:00
this._timeline.dispose();
this._timeline = null;
this._onceEvents.dispose();
this._onceEvents = null;
this._repeatedEvents.dispose();
this._repeatedEvents = null;
2015-01-06 04:33:05 +00:00
return this;
2014-09-11 17:38:41 +00:00
};
2015-08-17 00:31:11 +00:00
///////////////////////////////////////////////////////////////////////////////
// INITIALIZATION
///////////////////////////////////////////////////////////////////////////////
2014-06-16 05:44:00 +00:00
var TransportConstructor = Tone.Transport;
2014-08-29 20:36:31 +00:00
2014-07-30 17:54:55 +00:00
Tone._initAudioContext(function(){
2014-10-16 04:49:31 +00:00
if (typeof Tone.Transport === "function"){
//a single transport object
Tone.Transport = new Tone.Transport();
} else {
//stop the clock
Tone.Transport.stop();
2015-08-17 00:31:11 +00:00
//get the previous values
var prevSettings = Tone.Transport.get();
//destory the old transport
Tone.Transport.dispose();
2014-10-16 04:49:31 +00:00
//make new Transport insides
TransportConstructor.call(Tone.Transport);
2015-08-17 00:31:11 +00:00
//set the previous config
Tone.Transport.set(prevSettings);
2014-10-16 04:49:31 +00:00
}
2014-07-30 17:54:55 +00:00
});
2014-04-06 00:47:59 +00:00
return Tone.Transport;
});