mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-15 08:17:07 +00:00
Transport component
This commit is contained in:
parent
fc4e910ebf
commit
6b59fe3a4d
2 changed files with 347 additions and 0 deletions
50
examples/transport.html
Normal file
50
examples/transport.html
Normal file
|
@ -0,0 +1,50 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Transport</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../style/GUI.css">
|
||||
|
||||
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
|
||||
<script type="text/javascript" src="../src/core/Tone.js"></script>
|
||||
<script type="text/javascript" src="../src/components/Transport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="Transport">0:0:0</div>
|
||||
<script type="text/javascript">
|
||||
var transport = new Tone.Transport(60, 4);
|
||||
|
||||
transport.setInterval(function(time){
|
||||
$("#Transport").html(transport.getProgress());
|
||||
}, transport.notationToBeat("16n"));
|
||||
|
||||
transport.setTimeout(function(time){
|
||||
console.log("a");
|
||||
}, "0:0:1");
|
||||
|
||||
transport.setTimeout(function(time){
|
||||
console.log("series");
|
||||
}, "0:1:0");
|
||||
|
||||
transport.setTimeout(function(time){
|
||||
console.log("of");
|
||||
}, "0:3:0");
|
||||
|
||||
transport.setTimeout(function(time){
|
||||
console.log("timed");
|
||||
}, "1:0:0");
|
||||
|
||||
transport.setTimeout(function(time){
|
||||
console.log("events");
|
||||
}, "1:0:3");
|
||||
|
||||
transport.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
297
src/components/Transport.js
Normal file
297
src/components/Transport.js
Normal file
|
@ -0,0 +1,297 @@
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TRANSPORT
|
||||
//
|
||||
// oscillator-based transport allows for simple musical timing
|
||||
// supports tempo curves and time changes
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//@param {number=} bpm
|
||||
//@param {number=} timeSignature (over 4);
|
||||
Tone.Transport = function(bpm, timeSignature){
|
||||
|
||||
//components
|
||||
this.oscillator = null;
|
||||
this.jsNode = this.context.createScriptProcessor(this.bufferSize, 1, 1);
|
||||
this.jsNode.onaudioprocess = this._processBuffer.bind(this);
|
||||
this.timeSignature = this.defaultArg(timeSignature, 4);
|
||||
|
||||
//privates
|
||||
this._tatum = 12; //subdivisions of the quarter note
|
||||
this._ticks = 0; //the number of tatums
|
||||
this._upTick = false; // if the wave is on the rise or fall
|
||||
this._bpm = bpm;
|
||||
|
||||
//@type {Array.<Tone.Transport.Interval>}
|
||||
this._intervals = [];
|
||||
//@type {Array.<Tone.Transport.Timeout>}
|
||||
this._timeouts = [];
|
||||
this._timeoutProgress = 0;
|
||||
|
||||
|
||||
//so it doesn't get garbage collected
|
||||
this.jsNode.connect(Tone.Master);
|
||||
}
|
||||
|
||||
Tone.extend(Tone.Transport, Tone);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// INTERNAL METHODS
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Tone.Transport.prototype._processBuffer = function(event){
|
||||
var now = this.defaultArg(event.playbackTime, this.now());
|
||||
var bufferSize = this.jsNode.bufferSize;
|
||||
var endTime = now + this.samplesToSeconds(bufferSize);
|
||||
var incomingBuffer = event.inputBuffer.getChannelData(0);
|
||||
var upTick = this._upTick;
|
||||
for (var i = 0; i < bufferSize; i++){
|
||||
var sample = incomingBuffer[i];
|
||||
if (sample > 0 && !upTick){
|
||||
upTick = true;
|
||||
this._processTick(now + this.samplesToSeconds(i));
|
||||
} else if (sample < 0 && upTick){
|
||||
upTick = false;
|
||||
}
|
||||
}
|
||||
this._upTick = upTick;
|
||||
}
|
||||
|
||||
//@param {number} tickTime
|
||||
Tone.Transport.prototype._processTick = function(tickTime){
|
||||
this._ticks++;
|
||||
//do the intervals
|
||||
this._processIntervals(this._ticks, tickTime);
|
||||
this._processTimeouts(this._ticks, tickTime);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TIMING
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//processes and invokes the intervals
|
||||
Tone.Transport.prototype._processIntervals = function(ticks, time){
|
||||
for (var i = 0, len = this._intervals.length; i<len; i++){
|
||||
var interval = this._intervals[i];
|
||||
if (interval.callbackTick() === ticks){
|
||||
interval.doCallback(time);
|
||||
interval.start = ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//processes and invokes the timeouts
|
||||
Tone.Transport.prototype._processTimeouts = function(ticks, time){
|
||||
for (var i = this._timeoutProgress, len = this._timeouts.length; i<len; i++){
|
||||
var timeout = this._timeouts[i];
|
||||
var callbackTick = timeout.callbackTick();
|
||||
if (callbackTick === ticks){
|
||||
timeout.doCallback(time);
|
||||
//increment the timeoutprogress
|
||||
this._timeoutProgress = i + 1;
|
||||
} else if (callbackTick > ticks){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//@param {function(number)} callback
|
||||
//@param {string} interval (01:02:0.2)
|
||||
//@param {Object=} ctx the 'this' object which the
|
||||
//@returns {Tone.Transport.Event} the event
|
||||
Tone.Transport.prototype.setInterval = function(callback, interval, ctx){
|
||||
var ticks = this.progressToTicks(interval);
|
||||
ctx = this.defaultArg(ctx, window);
|
||||
var timeout = new Tone.Transport.Timeout(callback, ctx, ticks, this._ticks);
|
||||
this._intervals.push(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
//@param {number} intervalId
|
||||
//@param {}
|
||||
//@returns {boolean} true if the interval was removed
|
||||
Tone.Transport.prototype.clearInterval = function(rmInterval){
|
||||
for (var i = 0; i < this._intervals.length; i++){
|
||||
var interval = this._intervals[i];
|
||||
if (interval === rmInterval){
|
||||
this._intervals.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//@param {function(number)} callback
|
||||
//@param {string} timeout colon seperated (bars:beats)
|
||||
//@param {Object=} ctx the 'this' object which the
|
||||
//@returns {number} the timeoutID
|
||||
Tone.Transport.prototype.setTimeout = function(callback, timeout, ctx){
|
||||
var ticks = this.progressToTicks(timeout);
|
||||
ctx = this.defaultArg(ctx, window);
|
||||
var timeout = new Tone.Transport.Timeout(callback, ctx, ticks, this._ticks);
|
||||
//put it in the right spot
|
||||
this._addTimeout(timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
//add an event in the correct position
|
||||
Tone.Transport.prototype._addTimeout = function(event){
|
||||
for (var i = this._timeoutProgress, len = this._timeouts.length; i<len; i++){
|
||||
var testEvnt = this._timeouts[i];
|
||||
if (testEvnt.callbackTick() > event.callbackTick()){
|
||||
this._timeouts.splice(i, 0, event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//otherwise push it on the end
|
||||
this._timeouts.push(event);
|
||||
}
|
||||
|
||||
//@param {string} timeoutID returned by setTimeout
|
||||
Tone.Transport.prototype.clearTimeout = function(timeoutID){
|
||||
for (var i = 0; i < this._timeouts.length; i++){
|
||||
var timeout = this._timeouts[i];
|
||||
if (timeout.id === timeoutID){
|
||||
this._timeouts.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//@param {string} measures (measures:beats:sixteenths)
|
||||
//@returns {number} the the conversion to ticks
|
||||
Tone.Transport.prototype.progressToTicks = function(progress){
|
||||
var measures = 0;
|
||||
var quarters = 0;
|
||||
var sixteenths = 0;
|
||||
if (typeof progress === "number"){
|
||||
quarters = progress;
|
||||
} else if (typeof progress === "string"){
|
||||
var split = progress.split(":");
|
||||
if (split.length === 2){
|
||||
measures = parseFloat(split[0]);
|
||||
quarters = parseFloat(split[1]);
|
||||
} else if (split.length === 1){
|
||||
quarters = parseFloat(split[0]);
|
||||
} else if (split.length === 3){
|
||||
measures = parseFloat(split[0]);
|
||||
quarters = parseFloat(split[1]);
|
||||
sixteenths = parseFloat(split[2]);
|
||||
}
|
||||
}
|
||||
var ticks = (measures * this.timeSignature + quarters + sixteenths / 4) * this._tatum;
|
||||
//quantize to tick value
|
||||
return Math.round(ticks);
|
||||
}
|
||||
|
||||
//@param {number} ticks
|
||||
//@returns {string} progress (measures:beats:sixteenths)
|
||||
Tone.Transport.prototype.ticksToProgress = function(ticks){
|
||||
var quarters = ticks / this._tatum;
|
||||
var measures = parseInt(quarters / this.timeSignature, 10);
|
||||
var sixteenths = parseInt((quarters % 1) * 4, 10);
|
||||
quarters = parseInt(quarters, 10) % this.timeSignature;
|
||||
var progress = [measures, quarters, sixteenths];
|
||||
return progress.join(":");
|
||||
}
|
||||
|
||||
//@returns {string} progress (measures:beats:sixteenths)
|
||||
Tone.Transport.prototype.getProgress = function(){
|
||||
return this.ticksToProgress(this._ticks);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// START/STOP/PAUSE
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Tone.Transport.prototype.start = function(time){
|
||||
this.upTick = false;
|
||||
time = this.defaultArg(time, this.now());
|
||||
this.oscillator = this.context.createOscillator();
|
||||
this.oscillator.type = "square";
|
||||
this.setTempo(this._bpm);
|
||||
this.oscillator.connect(this.jsNode);
|
||||
this.oscillator.start(time);
|
||||
}
|
||||
|
||||
Tone.Transport.prototype.stop = function(time){
|
||||
time = this.defaultArg(time, this.now());
|
||||
this.oscillator.stop(time);
|
||||
this._ticks = 0;
|
||||
//go through and return all of the intervals to their original start value
|
||||
for (var i = 0; i < this._intervals.length; i++){
|
||||
var interval = this._intervals[i];
|
||||
interval.start = interval._originalStart;
|
||||
}
|
||||
//move the timeout progress back to 0
|
||||
this._timeoutProgress = 0;
|
||||
}
|
||||
|
||||
Tone.Transport.prototype.pause = function(time){
|
||||
time = this.defaultArg(time, this.now());
|
||||
this.oscillator.stop(time);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TEMPO CONTROLS
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//@param {number} bpm
|
||||
//@param {number=} rampTime Optionally speed the tempo up over time
|
||||
Tone.Transport.prototype.setTempo = function(bpm, rampTime){
|
||||
this._bpm = bpm;
|
||||
//convert the bpm to frequency
|
||||
this.oscillator.frequency.value = 4 / this.notationTime(this._tatum.toString() + "n", this._bpm);
|
||||
}
|
||||
|
||||
//@returns {number} the current bpm
|
||||
Tone.Transport.prototype.getTempo = function(){
|
||||
//convert the current frequency of the oscillator to bpm
|
||||
//if the oscillator isn't running, return _bpm
|
||||
}
|
||||
|
||||
//@param {Array.<number>} noteValues
|
||||
//@param {string} subdivision
|
||||
//@returns {Array.<number>} the
|
||||
Tone.Transport.prototype.quantize = function(noteValues, subdivision, percentage){
|
||||
|
||||
}
|
||||
|
||||
Tone.Transport.prototype.setTimeSignature = function(denominator){
|
||||
this.timeSignature = denominator
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TRANSPORT EVENT
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//@constructor
|
||||
//@param {function(number)} callback
|
||||
//@param {object} context
|
||||
//@param {number} interval (in ticks)
|
||||
//@param {number} startTicks
|
||||
//@param {boolean} repeat
|
||||
Tone.Transport.Timeout = function(callback, context, interval, startTicks){
|
||||
this.interval = interval;
|
||||
this.start = startTicks;
|
||||
this.callback = callback;
|
||||
this.context = context;
|
||||
this._originalStart = startTicks;
|
||||
}
|
||||
|
||||
Tone.Transport.Timeout.prototype.doCallback = function(playbackTime){
|
||||
this.callback.call(this.context, playbackTime);
|
||||
}
|
||||
|
||||
Tone.Transport.Timeout.prototype.callbackTick = function(){
|
||||
return this.start + this.interval;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue