new core timing primitives

This commit is contained in:
Yotam Mann 2016-04-18 00:34:16 -04:00
parent 3fc2fe90e1
commit 696e84cafb
5 changed files with 1344 additions and 0 deletions

270
Tone/type/Frequency.js Normal file
View file

@ -0,0 +1,270 @@
define(["Tone/core/Tone", "Tone/type/TimeBase"], function (Tone) {
/**
* @param {[type]} val [description]
* @param {[type]} units [description]
* @example
* Tone.Frequency("C3").eval() // 261
* Tone.Frequency(38, "midi").eval() //
* Tone.Frequency("C3").transpose(4).eval();
* Tone.Frequency("440hz").transpose([0, 3, 7]).eval() // ["A4", "C5", "E5"];
*/
Tone.Frequency = function(val, units){
if (this instanceof Tone.Frequency){
Tone.TimeBase.call(this, val, units);
} else {
return new Tone.Frequency(val, units);
}
};
Tone.extend(Tone.Frequency, Tone.TimeBase);
///////////////////////////////////////////////////////////////////////////
// AUGMENT BASE EXPRESSIONS
///////////////////////////////////////////////////////////////////////////
//clone the expressions so that
//we can add more without modifying the original
Tone.Frequency.prototype._primaryExpressions = Object.create(Tone.TimeBase.prototype._primaryExpressions);
/*
* midi type primary expression
* @type {Object}
* @private
*/
Tone.Frequency.prototype._primaryExpressions.midi = {
regexp : /^(\d+(?:\.\d+)?midi)/,
method : function(value){
return this.midiToFrequency(value);
}
};
/*
* note type primary expression
* @type {Object}
* @private
*/
Tone.Frequency.prototype._primaryExpressions.note = {
regexp : /^([a-g]{1}(?:b|#|x|bb)?)(-?[0-9]+)/i,
method : function(pitch, octave){
var index = noteToScaleIndex[pitch.toLowerCase()];
var noteNumber = index + (parseInt(octave) + 1) * 12;
return this.midiToFrequency(noteNumber);
}
};
/*
* BeatsBarsSixteenths type primary expression
* @type {Object}
* @private
*/
Tone.Frequency.prototype._primaryExpressions.tr = {
regexp : /^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,
method : function(m, q, s){
var total = 1;
if (m && m !== "0"){
total *= this._beatsToUnits(this._timeSignature() * parseFloat(m));
}
if (q && q !== "0"){
total *= this._beatsToUnits(parseFloat(q));
}
if (s && s !== "0"){
total *= this._beatsToUnits(parseFloat(s) / 4);
}
return total;
}
};
///////////////////////////////////////////////////////////////////////////
// EXPRESSIONS
///////////////////////////////////////////////////////////////////////////
/**
* Transposes the frequency by the given number of semitones.
* @param {Interval} interval
* @return {Tone.Frequency} this
* @example
* Tone.Frequency("A4").transpose(3); //"C5"
*/
Tone.Frequency.prototype.transpose = function(interval){
this._expr = function(expr, interval){
var val = expr();
return val * this.intervalToFrequencyRatio(interval);
}.bind(this, this._expr, interval);
return this;
};
/**
* Takes an array of semitone intervals and returns
* an array of frequencies transposed by those intervals.
* @param {Array} intervals
* @return {Tone.Frequency} this
* @example
* Tone.Frequency("A4").harmonize([0, 3, 7]); //["A4", "C5", "E5"]
*/
Tone.Frequency.prototype.harmonize = function(intervals){
this._expr = function(expr, intervals){
var val = expr();
var ret = [];
for (var i = 0; i < intervals.length; i++){
ret[i] = val * this.intervalToFrequencyRatio(intervals[i]);
}
return ret;
}.bind(this, this._expr, intervals);
return this;
};
///////////////////////////////////////////////////////////////////////////
// UNIT CONVERSIONS
///////////////////////////////////////////////////////////////////////////
/**
* Return the value of the frequency as a MIDI note
* @return {MIDI}
*/
Tone.Frequency.prototype.toMidi = function(){
return this.frequencyToMidi(this.eval());
};
/**
* Return the value of the frequency in Scientific Pitch Notation
* @return {Note}
*/
Tone.Frequency.prototype.toNote = function(){
var freq = this.eval();
var log = Math.log(freq / Tone.Frequency.A4) / Math.LN2;
var noteNumber = Math.round(12 * log) + 57;
var octave = Math.floor(noteNumber/12);
if(octave < 0){
noteNumber += -12 * octave;
}
var noteName = scaleIndexToNote[noteNumber % 12];
return noteName + octave.toString();
};
/**
* Return the duration of one cycle in seconds.
* @return {Seconds}
*/
Tone.Frequency.prototype.toSeconds = function(){
return 1 / this.eval();
};
/**
* Return the duration of one cycle in ticks
* @return {Ticks}
*/
Tone.Frequency.prototype.toTicks = function(){
var quarterTime = this._beatsToUnits(1);
var quarters = this.eval() / quarterTime;
return Math.floor(quarters * Tone.Transport.PPQ);
};
///////////////////////////////////////////////////////////////////////////
// UNIT CONVERSIONS HELPERS
///////////////////////////////////////////////////////////////////////////
/**
* Returns the value of a frequency in the current units
* @param {Frequency} freq
* @return {Number}
* @private
*/
Tone.Frequency.prototype._frequencyToUnits = function(freq){
return freq;
};
/**
* Returns the value of a tick in the current time units
* @param {Ticks} ticks
* @return {Number}
* @private
*/
Tone.Frequency.prototype._ticksToUnits = function(ticks){
return 1 / ((ticks * 60) / (Tone.Transport.bpm.value * Tone.Transport.PPQ));
};
/**
* Return the value of the beats in the current units
* @param {Number} beats
* @return {Number}
* @private
*/
Tone.Frequency.prototype._beatsToUnits = function(beats){
return 1 / Tone.TimeBase.prototype._beatsToUnits.call(this, beats);
};
/**
* Returns the value of a second in the current units
* @param {Seconds} seconds
* @return {Number}
* @private
*/
Tone.Frequency.prototype._secondsToUnits = function(seconds){
return 1 / seconds;
};
/**
* The default units if none are given.
* @private
*/
Tone.Frequency.prototype._defaultUnits = "hz";
///////////////////////////////////////////////////////////////////////////
// FREQUENCY CONVERSIONS
///////////////////////////////////////////////////////////////////////////
/**
* Note to scale index
* @type {Object}
*/
var noteToScaleIndex = {
"cbb" : -2, "cb" : -1, "c" : 0, "c#" : 1, "cx" : 2,
"dbb" : 0, "db" : 1, "d" : 2, "d#" : 3, "dx" : 4,
"ebb" : 2, "eb" : 3, "e" : 4, "e#" : 5, "ex" : 6,
"fbb" : 3, "fb" : 4, "f" : 5, "f#" : 6, "fx" : 7,
"gbb" : 5, "gb" : 6, "g" : 7, "g#" : 8, "gx" : 9,
"abb" : 7, "ab" : 8, "a" : 9, "a#" : 10, "ax" : 11,
"bbb" : 9, "bb" : 10, "b" : 11, "b#" : 12, "bx" : 13,
};
/**
* scale index to note (sharps)
* @type {Array}
*/
var scaleIndexToNote = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
/**
* The [concert pitch](https://en.wikipedia.org/wiki/Concert_pitch)
* A4's values in Hertz.
* @type {Frequency}
* @static
*/
Tone.Frequency.A4 = 440;
/**
* Convert a MIDI note to frequency value.
* @param {MIDI} midi The midi number to convert.
* @return {Frequency} the corresponding frequency value
* @example
* tone.midiToFrequency(69); // returns 440
*/
Tone.Frequency.prototype.midiToFrequency = function(midi){
return Tone.Frequency.A4 * Math.pow(2, (midi - 69) / 12);
};
/**
* Convert a frequency value to a MIDI note.
* @param {Frequency} frequency The value to frequency value to convert.
* @returns {MIDI}
* @example
* tone.midiToFrequency(440); // returns 69
*/
Tone.Frequency.prototype.frequencyToMidi = function(frequency){
return 69 + 12 * Math.log2(frequency / Tone.Frequency.A4);
};
return Tone.Frequency;
});

227
Tone/type/Time.js Normal file
View file

@ -0,0 +1,227 @@
define(["Tone/core/Tone", "Tone/type/TimeBase"], function (Tone) {
/**
* @param {[type]} val [description]
* @param {[type]} units [description]
*/
Tone.Time = function(val, units){
if (this instanceof Tone.Time){
Tone.TimeBase.call(this, val, units);
} else {
return new Tone.Time(val, units);
}
};
Tone.extend(Tone.Time, Tone.TimeBase);
//clone the expressions so that
//we can add more without modifying the original
Tone.Time.prototype._unaryExpressions = Object.create(Tone.TimeBase.prototype._unaryExpressions);
/*
* Adds an additional unary expression
* which quantizes values to the next subdivision
* @type {Object}
* @private
*/
Tone.Time.prototype._unaryExpressions.quantize = {
regexp : /^@/,
method : function(rh){
return Tone.Transport.nextSubdivision(rh());
}
};
/*
* Adds an additional unary expression
* which adds the current clock time.
* @type {Object}
* @private
*/
Tone.Time.prototype._unaryExpressions.now = {
regexp : /^\+/,
method : function(lh){
return this.now() + lh();
}
};
/**
* Quantize the time by the given subdivision. Optionally add a
* percentage which will move the time value towards the ideal
* quantized value by that percentage.
* @param {Number|Time} val The subdivision to quantize to
* @param {NormalRange} [perc=1] Move the time value
* towards the quantized value by
* a percentage.
* @return {Tone.Time} this
* @example
* Tone.Time(21).quantize(2).eval() //returns 22
* Tone.Time(0.6).quantize("4n", 0.5).eval() //returns 0.55
*/
Tone.Time.prototype.quantize = function(subdiv, perc){
perc = this.defaultArg(perc, 1);
this._expr = function(expr, subdivision, percent){
expr = expr();
subdivision = subdivision.eval();
var multiple = Math.round(expr / subdivision);
var ideal = multiple * subdivision;
var diff = ideal - expr;
return expr + diff * percent;
}.bind(this, this._expr, new this.constructor(subdiv), perc);
return this;
};
/**
* Adds the current clock time to the time expression
* @return {Tone.Time} this
*/
Tone.Time.prototype.addNow = function(){
this._expr = function(expr){
return expr() + this.now();
}.bind(this, this._expr);
return this;
};
/**
* @override
* Override the default value return when no arguments are passed in.
* The default value is 'now'
* @private
*/
Tone.Time.prototype._defaultExpr = function(){
return function(expr){
return expr() + this.now();
}.bind(this, this._expr);
};
//CONVERSIONS//////////////////////////////////////////////////////////////
/**
* Convert a Time to Notation. Values will be thresholded to the nearest 128th note.
* @return {Notation}
*/
Tone.Time.prototype.toNotation = function(){
var time = this.eval();
var testNotations = ["1m", "2n", "4n", "8n", "16n", "32n", "64n", "128n"];
var retNotation = this._toNotationHelper(time, testNotations);
//try the same thing but with tripelets
var testTripletNotations = ["1m", "2n", "2t", "4n", "4t", "8n", "8t", "16n", "16t", "32n", "32t", "64n", "64t", "128n"];
var retTripletNotation = this._toNotationHelper(time, testTripletNotations);
//choose the simpler expression of the two
if (retTripletNotation.split("+").length < retNotation.split("+").length){
return retTripletNotation;
} else {
return retNotation;
}
};
/**
* Helper method for Tone.toNotation
* @param {Number} units
* @param {Array} testNotations
* @return {String}
* @private
*/
Tone.Time.prototype._toNotationHelper = function(units, testNotations){
//the threshold is the last value in the array
var threshold = this._notationToUnits(testNotations[testNotations.length - 1]);
var retNotation = "";
for (var i = 0; i < testNotations.length; i++){
var notationTime = this._notationToUnits(testNotations[i]);
//account for floating point errors (i.e. round up if the value is 0.999999)
var multiple = units / notationTime;
var floatingPointError = 0.000001;
if (1 - multiple % 1 < floatingPointError){
multiple += floatingPointError;
}
multiple = Math.floor(multiple);
if (multiple > 0){
if (multiple === 1){
retNotation += testNotations[i];
} else {
retNotation += multiple.toString() + "*" + testNotations[i];
}
units -= multiple * notationTime;
if (units < threshold){
break;
} else {
retNotation += " + ";
}
}
}
if (retNotation === ""){
retNotation = "0";
}
return retNotation;
};
/**
* Convert a notation value to the current units
* @param {Notation} notation
* @return {Number}
* @private
*/
Tone.Time.prototype._notationToUnits = function(notation){
var primaryExprs = this._primaryExpressions;
var notationExprs = [primaryExprs.n, primaryExprs.t, primaryExprs.m];
for (var i = 0; i < notationExprs.length; i++){
var expr = notationExprs[i];
var match = notation.match(expr.regexp);
if (match){
return expr.method.call(this, match[1]);
}
}
};
/**
* Return the time encoded as Bars:Beats:Sixteenths.
* @return {BarsBeatsSixteenths}
*/
Tone.Time.prototype.toBarsBeatsSixteenths = function(){
var quarterTime = this._beatsToUnits(1);
var quarters = this.eval() / quarterTime;
var measures = Math.floor(quarters / this._timeSignature());
var sixteenths = (quarters % 1) * 4;
quarters = Math.floor(quarters) % this._timeSignature();
var progress = [measures, quarters, sixteenths];
return progress.join(":");
};
/**
* Return the time in ticks.
* @return {Ticks}
*/
Tone.Time.prototype.toTicks = function(){
var quarterTime = this._beatsToUnits(1);
var quarters = this.eval() / quarterTime;
return Math.floor(quarters * Tone.Transport.PPQ);
};
/**
* Return the time in samples
* @return {Samples}
*/
Tone.Time.prototype.toSamples = function(){
return this.eval() * this.context.sampleRate;
};
/**
* Return the time as a frequency value
* @return {Frequency}
*/
Tone.Time.prototype.toFrequency = function(){
return 1/this.eval();
};
/**
* Return the time in seconds.
* @return {Seconds}
*/
Tone.Time.prototype.toSeconds = function(){
return this.eval();
};
return Tone.Time;
});

502
Tone/type/TimeBase.js Normal file
View file

@ -0,0 +1,502 @@
define(["Tone/core/Tone"], function (Tone) {
/**
* @class Tone.TimeBase is a flexible encoding of time
* which can be evaluated to and from a string.
* Parsing code modified from https://code.google.com/p/tapdigit/
* Copyright 2011 2012 Ariya Hidayat, New BSD License
* @extends {Tone}
* @param {Time} val The time value as a number or string
* @param {String=} units Unit values
* @example
* Tone.TimeBase(4, "n")
* Tone.TimeBase(2, "t")
* Tone.TimeBase("2t").add("1m")
* Tone.TimeBase("2t + 1m");
*/
Tone.TimeBase = function(val, units){
//allows it to be constructed with or without 'new'
if (this instanceof Tone.TimeBase) {
/**
* Any expressions parsed from the Time
* @type {Array}
* @private
*/
this._expr = this._noOp;
//default units
units = this.defaultArg(units, this._defaultUnits);
//get the value from the given time
if (this.isString(val)){
this._expr = this._parseExprString(val);
} else if (this.isNumber(val)){
var method = this._primaryExpressions[units].method;
this._expr = method.bind(this, val);
} else if (this.isUndef(val)){
//default expression
this._expr = this._defaultExpr();
}
} else {
return new Tone.TimeBase(val, units);
}
};
Tone.extend(Tone.TimeBase);
/**
* Repalce the current time value with the value
* given by the expression string.
* @param {String} exprString
* @return {Tone.TimeBase} this
*/
Tone.TimeBase.prototype.set = function(exprString){
this._expr = this._parseExprString(exprString);
return this;
};
///////////////////////////////////////////////////////////////////////////
// ABSTRACT SYNTAX TREE PARSER
///////////////////////////////////////////////////////////////////////////
Tone.TimeBase.prototype._primaryExpressions = {
"n" : {
regexp : /^(\d+)n/i,
method : function(value){
value = parseInt(value);
if (value === 1){
return this._beatsToUnits(this._timeSignature());
} else {
return this._beatsToUnits(4 / value);
}
}
},
"t" : {
regexp : /^(\d+)t/i,
method : function(value){
value = parseInt(value);
return this._beatsToUnits(8 / (parseInt(value) * 3));
}
},
"m" : {
regexp : /^(\d+)m/i,
method : function(value){
return this._beatsToUnits(parseInt(value) * this._timeSignature());
}
},
"i" : {
regexp : /^(\d+)i/i,
method : function(value){
return this._ticksToUnits(parseInt(value));
}
},
"hz" : {
regexp : /^(\d+(?:\.\d+)?)hz/i,
method : function(value){
return this._frequencyToUnits(parseFloat(value));
}
},
"tr" : {
regexp : /^(\d+(?:\.\d+)?):(\d+(?:\.\d+)?):?(\d+(?:\.\d+)?)?/,
method : function(m, q, s){
var total = 0;
if (m && m !== "0"){
total += this._beatsToUnits(this._timeSignature() * parseFloat(m));
}
if (q && q !== "0"){
total += this._beatsToUnits(parseFloat(q));
}
if (s && s !== "0"){
total += this._beatsToUnits(parseFloat(s) / 4);
}
return total;
}
},
"s" : {
regexp : /^(\d+(?:\.\d+)?s)/,
method : function(value){
return this._secondsToUnits(parseFloat(value));
}
},
"samples" : {
regexp : /^(\d+)samples/,
method : function(value){
return parseInt(value) / this.context.sampleRate;
}
},
"default" : {
regexp : /^(\d+(?:\.\d+)?)/,
method : function(value){
return this._primaryExpressions[this._defaultUnits].method.call(this, value);
}
}
};
Tone.TimeBase.prototype._binaryExpressions = {
"+" : {
regexp : /^\+/,
precedence : 2,
method : function(lh, rh){
return lh() + rh();
}
},
"-" : {
regexp : /^\-/,
precedence : 2,
method : function(lh, rh){
return lh() - rh();
}
},
"*" : {
regexp : /^\*/,
precedence : 1,
method : function(lh, rh){
return lh() * rh();
}
},
"/" : {
regexp : /^\//,
precedence : 1,
method : function(lh, rh){
return lh() / rh();
}
}
};
Tone.TimeBase.prototype._unaryExpressions = {
"neg" : {
regexp : /^\-/,
method : function(lh){
return -lh();
}
}
};
Tone.TimeBase.prototype._syntaxGlue = {
"(" : {
regexp : /^\(/
},
")" : {
regexp : /^\)/
}
};
/**
* tokenize the expression based on the Expressions object
* @param {string} expr
* @return {Object} returns two methods on the tokenized list, next and peek
* @private
*/
Tone.TimeBase.prototype._tokenize = function(expr){
var position = -1;
var tokens = [];
while(expr.length > 0){
expr = expr.trim();
var token = getNextToken(expr, this);
tokens.push(token);
expr = expr.substr(token.value.length);
}
function getNextToken(expr, context){
var expressions = ["_binaryExpressions", "_unaryExpressions", "_primaryExpressions", "_syntaxGlue"];
for (var i = 0; i < expressions.length; i++){
var group = context[expressions[i]];
for (var opName in group){
var op = group[opName];
var reg = op.regexp;
var match = expr.match(reg);
if (match !== null){
return {
method : op.method,
precedence : op.precedence,
regexp : op.regexp,
value : match[0],
};
}
}
}
throw new SyntaxError("Unexpected token "+expr);
}
return {
next : function(){
return tokens[++position];
},
peek : function(){
return tokens[position + 1];
}
};
};
/**
* Given a token, find the value within the groupName
* @param {Object} token
* @param {String} groupName
* @param {Number} precedence
* @private
*/
Tone.TimeBase.prototype._matchGroup = function(token, group, prec) {
var ret = false;
if (!this.isUndef(token)){
for (var opName in group){
var op = group[opName];
if (op.regexp.test(token.value)){
if (!this.isUndef(prec)){
if(op.precedence === prec){
return op;
}
} else {
return op;
}
}
}
}
return ret;
};
/**
* Match a binary expression given the token and the precedence
* @param {Lexer} lexer
* @param {Number} precedence
* @private
*/
Tone.TimeBase.prototype._parseBinary = function(lexer, precedence){
if (this.isUndef(precedence)){
precedence = 2;
}
var expr;
if (precedence < 0){
expr = this._parseUnary(lexer);
} else {
expr = this._parseBinary(lexer, precedence - 1);
}
var token = lexer.peek();
while (token && this._matchGroup(token, this._binaryExpressions, precedence)){
token = lexer.next();
expr = token.method.bind(this, expr, this._parseBinary(lexer, precedence - 1));
token = lexer.peek();
}
return expr;
};
/**
* Match a unary expression.
* @param {Lexer} lexer
* @private
*/
Tone.TimeBase.prototype._parseUnary = function(lexer){
var token, expr;
token = lexer.peek();
var op = this._matchGroup(token, this._unaryExpressions);
if (op) {
token = lexer.next();
expr = this._parseUnary(lexer);
return op.method.bind(this, expr);
}
return this._parsePrimary(lexer);
};
/**
* Match a primary expression (a value).
* @param {Lexer} lexer
* @private
*/
Tone.TimeBase.prototype._parsePrimary = function(lexer){
var token, expr;
token = lexer.peek();
if (this.isUndef(token)) {
throw new SyntaxError("Unexpected end of expression");
}
if (this._matchGroup(token, this._primaryExpressions)) {
token = lexer.next();
var matching = token.value.match(token.regexp);
return token.method.bind(this, matching[1], matching[2], matching[3]);
}
if (token && token.value === "("){
lexer.next();
expr = this._parseBinary(lexer);
token = lexer.next();
if (!(token && token.value === ")")) {
throw new SyntaxError("Expected )");
}
return expr;
}
throw new SyntaxError("Cannot process token " + token.value);
};
/**
* Recursively parse the string expression into a syntax tree.
* @param {string} expr
* @return {Function} the bound method to be evaluated later
* @private
*/
Tone.TimeBase.prototype._parseExprString = function(exprString){
var lexer = this._tokenize(exprString);
var tree = this._parseBinary(lexer);
return tree;
};
///////////////////////////////////////////////////////////////////////////
// DEFAULTS
///////////////////////////////////////////////////////////////////////////
/**
* The initial expression value
* @return {Number} The initial value 0
* @private
*/
Tone.TimeBase.prototype._noOp = function(){
return 0;
};
/**
* The default expression value if no arguments are given
* @private
*/
Tone.TimeBase.prototype._defaultExpr = function(){
return this._noOp;
};
/**
* The default units if none are given.
* @private
*/
Tone.TimeBase.prototype._defaultUnits = "s";
///////////////////////////////////////////////////////////////////////////
// UNIT CONVERSIONS
///////////////////////////////////////////////////////////////////////////
/**
* Returns the value of a frequency in the current units
* @param {Frequency} freq
* @return {Number}
* @private
*/
Tone.TimeBase.prototype._frequencyToUnits = function(freq){
return 1/freq;
};
/**
* Return the value of the beats in the current units
* @param {Number} beats
* @return {Number}
* @private
*/
Tone.TimeBase.prototype._beatsToUnits = function(beats){
return (60 / Tone.Transport.bpm.value) * beats;
};
/**
* Returns the value of a second in the current units
* @param {Seconds} seconds
* @return {Number}
* @private
*/
Tone.TimeBase.prototype._secondsToUnits = function(seconds){
return seconds;
};
/**
* Returns the value of a tick in the current time units
* @param {Ticks} ticks
* @return {Number}
* @private
*/
Tone.TimeBase.prototype._ticksToUnits = function(ticks){
return ticks * (this._beatsToUnits(1) / Tone.Transport.PPQ);
};
/**
* Return the time signature.
* @return {Number}
* @private
*/
Tone.TimeBase.prototype._timeSignature = function(){
return Tone.Transport.timeSignature;
};
///////////////////////////////////////////////////////////////////////////
// EXPRESSIONS
///////////////////////////////////////////////////////////////////////////
/**
* Push an expression onto the expression list
* @param {Time} val
* @param {String} type
* @param {String} units
* @return {Tone.TimeBase}
* @private
*/
Tone.TimeBase.prototype._pushExpr = function(val, name, units){
//create the expression
if (!(val instanceof Tone.TimeBase)){
val = new Tone.TimeBase(val, units);
}
this._expr = this._binaryExpressions[name].method.bind(this, this._expr, val._expr);
return this;
};
/**
* Subtract the current value by the given time.
* @param {Time} val The value to divide by
* @param {String=} units Optional units to use with the value.
* @return {Tone.TimeBase} this
*/
Tone.TimeBase.prototype.add = function(val, units){
return this._pushExpr(val, "+", units);
};
/**
* Subtract the current value by the given time.
* @param {Time} val The value to divide by
* @param {String=} units Optional units to use with the value.
* @return {Tone.TimeBase} this
*/
Tone.TimeBase.prototype.sub = function(val, units){
return this._pushExpr(val, "-", units);
};
/**
* Multiply the current value by the given time.
* @param {Time} val The value to divide by
* @param {String=} units Optional units to use with the value.
* @return {Tone.TimeBase} this
*/
Tone.TimeBase.prototype.mult = function(val, units){
return this._pushExpr(val, "*", units);
};
/**
* Divide the current value by the given time.
* @param {Time} val The value to divide by
* @param {String=} units Optional units to use with the value.
* @return {Tone.TimeBase} this
*/
Tone.TimeBase.prototype.div = function(val, units){
return this._pushExpr(val, "/", units);
};
/**
* Evaluate the time value. Returns the time
* in seconds.
* @return {Seconds}
*/
Tone.TimeBase.prototype.eval = function(){
return this._expr();
};
/**
* Clean up
* @return {Tone.TimeBase} this
*/
Tone.TimeBase.prototype.dispose = function(){
this._expr = null;
};
return Tone.TimeBase;
});

121
Tone/type/TransportTime.js Normal file
View file

@ -0,0 +1,121 @@
define(["Tone/core/Tone", "Tone/type/Time"], function (Tone) {
/**
* @extends {Tone.Time}
*/
Tone.TransportTime = function(val, units){
if (this instanceof Tone.TransportTime){
Tone.Time.call(this, val, units);
} else {
return new Tone.TransportTime(val, units);
}
};
Tone.extend(Tone.TransportTime, Tone.Time);
//clone the expressions so that
//we can add more without modifying the original
Tone.TransportTime.prototype._unaryExpressions = Object.create(Tone.Time.prototype._unaryExpressions);
/**
* Adds an additional unary expression
* which quantizes values to the next subdivision
* @type {Object}
* @private
*/
Tone.TransportTime.prototype._unaryExpressions.quantize = {
regexp : /^@/,
method : function(rh){
var subdivision = rh();
var multiple = Math.ceil(Tone.Transport.ticks / subdivision);
return multiple * subdivision;
}
};
/**
* @override
* The value of a beat in ticks.
* @param {Number} beats
* @return {Number}
* @private
*/
Tone.TransportTime.prototype._beatsToUnits = function(beats){
return Tone.Transport.PPQ * beats;
};
/**
* @override
* @param {Ticks} ticks
* @return {Number}
* @private
*/
Tone.TransportTime.prototype._ticksToUnits = function(ticks){
return ticks;
};
/**
* Returns the value of a second in the current units
* @param {Seconds} seconds
* @return {Number}
* @private
*/
Tone.TransportTime.prototype._secondsToUnits = function(seconds){
var quarterTime = (60 / Tone.Transport.bpm.value);
var quarters = seconds / quarterTime;
return Math.floor(quarters * Tone.Transport.PPQ);
};
/**
* Evaluate the time expression. Returns values in ticks
* @return {Ticks}
*/
Tone.TransportTime.prototype.eval = function(){
return Math.floor(this._expr());
};
/**
* The current time along the Transport
* @return {Ticks} The Transport's position in ticks.
*/
Tone.TransportTime.prototype.now = function(){
return Tone.Transport.ticks;
};
/**
* Return the time in ticks.
* @return {Ticks}
*/
Tone.TransportTime.prototype.toTicks = function(){
return this.eval();
};
/**
* Return the time in samples
* @return {Samples}
*/
Tone.TransportTime.prototype.toSamples = function(){
return this.toSeconds() * this.context.sampleRate;
};
/**
* Return the time as a frequency value
* @return {Frequency}
*/
Tone.TransportTime.prototype.toFrequency = function(){
return 1/this.toSeconds();
};
/**
* Return the time in seconds.
* @return {Seconds}
*/
Tone.TransportTime.prototype.toSeconds = function(){
var beatTime = 60/Tone.Transport.bpm.value;
var tickTime = beatTime / Tone.Transport.PPQ;
return this.eval() * tickTime;
};
return Tone.TransportTime;
});

224
Tone/type/Type.js Normal file
View file

@ -0,0 +1,224 @@
define(["Tone/core/Tone", "Tone/type/Time", "Tone/type/Frequency", "Tone/type/TransportTime"],
function (Tone) {
///////////////////////////////////////////////////////////////////////////
// TYPES
///////////////////////////////////////////////////////////////////////////
/**
* Units which a value can take on.
* @enum {String}
*/
Tone.Type = {
/**
* Default units
* @typedef {Default}
*/
Default : "number",
/**
* Time can be described in a number of ways. Read more [Time](https://github.com/Tonejs/Tone.js/wiki/Time).
*
* <ul>
* <li>Numbers, which will be taken literally as the time (in seconds).</li>
* <li>Notation, ("4n", "8t") describes time in BPM and time signature relative values.</li>
* <li>TransportTime, ("4:3:2") will also provide tempo and time signature relative times
* in the form BARS:QUARTERS:SIXTEENTHS.</li>
* <li>Frequency, ("8hz") is converted to the length of the cycle in seconds.</li>
* <li>Now-Relative, ("+1") prefix any of the above with "+" and it will be interpreted as
* "the current time plus whatever expression follows".</li>
* <li>Expressions, ("3:0 + 2 - (1m / 7)") any of the above can also be combined
* into a mathematical expression which will be evaluated to compute the desired time.</li>
* <li>No Argument, for methods which accept time, no argument will be interpreted as
* "now" (i.e. the currentTime).</li>
* </ul>
*
* @typedef {Time}
*/
Time : "time",
/**
* Frequency can be described similar to time, except ultimately the
* values are converted to frequency instead of seconds. A number
* is taken literally as the value in hertz. Additionally any of the
* Time encodings can be used. Note names in the form
* of NOTE OCTAVE (i.e. C4) are also accepted and converted to their
* frequency value.
* @typedef {Frequency}
*/
Frequency : "frequency",
/**
* TransportTime describes a position along the Transport's timeline. It is
* similar to Time in that it uses all the same encodings, but TransportTime specifically
* pertains to the Transport's timeline, which is startable, stoppable, loopable, and seekable.
* [Read more](https://github.com/Tonejs/Tone.js/wiki/TransportTime)
* @typedef {TransportTime}
*/
TransportTime : "transportTime",
/**
* Ticks are the basic subunit of the Transport. They are
* the smallest unit of time that the Transport supports.
* @typedef {Ticks}
*/
Ticks : "ticks",
/**
* Normal values are within the range [0, 1].
* @typedef {NormalRange}
*/
NormalRange : "normalRange",
/**
* AudioRange values are between [-1, 1].
* @typedef {AudioRange}
*/
AudioRange : "audioRange",
/**
* Decibels are a logarithmic unit of measurement which is useful for volume
* because of the logarithmic way that we perceive loudness. 0 decibels
* means no change in volume. -10db is approximately half as loud and 10db
* is twice is loud.
* @typedef {Decibels}
*/
Decibels : "db",
/**
* Half-step note increments, i.e. 12 is an octave above the root. and 1 is a half-step up.
* @typedef {Interval}
*/
Interval : "interval",
/**
* Beats per minute.
* @typedef {BPM}
*/
BPM : "bpm",
/**
* The value must be greater than or equal to 0.
* @typedef {Positive}
*/
Positive : "positive",
/**
* A cent is a hundredth of a semitone.
* @typedef {Cents}
*/
Cents : "cents",
/**
* Angle between 0 and 360.
* @typedef {Degrees}
*/
Degrees : "degrees",
/**
* A number representing a midi note.
* @typedef {MIDI}
*/
MIDI : "midi",
/**
* A colon-separated representation of time in the form of
* Bars:Beats:Sixteenths.
* @typedef {BarsBeatsSixteenths}
*/
BarsBeatsSixteenths : "barsBeatsSixteenths",
/**
* Sampling is the reduction of a continuous signal to a discrete signal.
* Audio is typically sampled 44100 times per second.
* @typedef {Samples}
*/
Samples : "samples",
/**
* Hertz are a frequency representation defined as one cycle per second.
* @typedef {Hertz}
*/
Hertz : "hertz",
/**
* A frequency represented by a letter name,
* accidental and octave. This system is known as
* [Scientific Pitch Notation](https://en.wikipedia.org/wiki/Scientific_pitch_notation).
* @typedef {Note}
*/
Note : "note",
/**
* One millisecond is a thousandth of a second.
* @typedef {Milliseconds}
*/
Milliseconds : "milliseconds",
/**
* Seconds are the time unit of the AudioContext. In the end,
* all values need to be evaluated to seconds.
* @typedef {Seconds}
*/
Seconds : "seconds",
/**
* A string representing a duration relative to a measure.
* <ul>
* <li>"4n" = quarter note</li>
* <li>"2m" = two measures</li>
* <li>"8t" = eighth-note triplet</li>
* </ul>
* @typedef {Notation}
*/
Notation : "notation",
};
///////////////////////////////////////////////////////////////////////////
// AUGMENT TONE's PROTOTYPE
///////////////////////////////////////////////////////////////////////////
/**
* Convert Time into seconds.
*
* Unlike the method which it overrides, this takes into account
* transporttime and musical notation.
*
* Time : 1.40
* Notation: 4n|1m|2t
* Now Relative: +3n
* Math: 3n+16n or even complicated expressions ((3n*2)/6 + 1)
*
* @param {Time} time
* @return {Seconds}
*/
Tone.prototype.toSeconds = function(time){
if (this.isNumber(time)){
return time;
} else if (this.isString(time) || this.isUndef(time)){
return (new Tone.Time(time)).eval();
} else if (time instanceof Tone.TransportTime){
return time.toSeconds();
} else if (time instanceof Tone.Time){
return time.eval();
} else if (time instanceof Tone.Frequency){
return time.toSeconds();
}
};
/**
* Convert a frequency representation into a number.
* @param {Frequency} freq
* @return {Hertz} the frequency in hertz
*/
Tone.prototype.toFrequency = function(freq){
if (this.isNumber(freq)){
return freq;
} else if (this.isString(freq) || this.isUndef(freq)){
return (new Tone.Frequency(freq)).eval();
} else if (freq instanceof Tone.Frequency){
return freq.eval();
} else if (freq instanceof Tone.Time){
return freq.toFrequency();
}
};
/**
* Convert a time representation into ticks.
* @param {Time} time
* @return {Ticks} the time in ticks
*/
Tone.prototype.toTicks = function(time){
if (this.isNumber(time) || this.isString(time) || this.isUndef(time)){
return (new Tone.TransportTime(time)).eval();
} else if (time instanceof Tone.Frequency){
return time.toTicks();
} else if (time instanceof Tone.TransportTime){
return time.eval();
} else if (time instanceof Tone.Time){
return time.toTicks();
}
};
return Tone;
});