UNPKG

tone

Version:

A Web Audio framework for making interactive music in the browser.

341 lines (310 loc) 8.05 kB
define(["Tone/core/Tone"], function (Tone) { /** * @class Tone.TimeBase is a flexible encoding of time * which can be evaluated to and from a string. * @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") * Tone.TimeBase("2t") + Tone.TimeBase("4n"); */ Tone.TimeBase = function(val, units){ //allows it to be constructed with or without 'new' if (this instanceof Tone.TimeBase) { /** * The value * @type {Number|String|Tone.TimeBase} * @private */ this._val = val; /** * The units * @type {String?} */ this._units = units; //test if the value is a string representation of a number if (Tone.isUndef(this._units) && Tone.isString(this._val) && // eslint-disable-next-line eqeqeq parseFloat(this._val) == this._val && this._val.charAt(0) !== "+"){ this._val = parseFloat(this._val); this._units = this._defaultUnits; } else if (val && val.constructor === this.constructor){ //if they're the same type, just copy values over this._val = val._val; this._units = val._units; } else if (val instanceof Tone.TimeBase){ switch(this._defaultUnits){ case "s" : this._val = val.toSeconds(); break; case "i" : this._val = val.toTicks(); break; case "hz" : this._val = val.toFrequency(); break; case "midi" : this._val = val.toMidi(); break; default : throw new Error("Unrecognized default units "+this._defaultUnits); } } } else { return new Tone.TimeBase(val, units); } }; Tone.extend(Tone.TimeBase); /////////////////////////////////////////////////////////////////////////// // ABSTRACT SYNTAX TREE PARSER /////////////////////////////////////////////////////////////////////////// /** * All the primary expressions. * @private * @type {Object} */ Tone.TimeBase.prototype._expressions = { "n" : { regexp : /^(\d+)n(\.?)$/i, method : function(value, dot){ value = parseInt(value); var scalar = dot === "." ? 1.5 : 1; if (value === 1){ return this._beatsToUnits(this._getTimeSignature())*scalar; } else { return this._beatsToUnits(4 / value)*scalar; } } }, "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._getTimeSignature()); } }, "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._getTimeSignature() * 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._expressions[this._defaultUnits].method.call(this, value); } } }; /** * The default units if none are given. * @type {String} * @private */ Tone.TimeBase.prototype._defaultUnits = "s"; /////////////////////////////////////////////////////////////////////////// // TRANSPORT FALLBACKS /////////////////////////////////////////////////////////////////////////// /** * Return the bpm, or 120 if Transport is not available * @type {Number} * @private */ Tone.TimeBase.prototype._getBpm = function(){ if (Tone.Transport){ return Tone.Transport.bpm.value; } else { return 120; } }; /** * Return the timeSignature or 4 if Transport is not available * @type {Number} * @private */ Tone.TimeBase.prototype._getTimeSignature = function(){ if (Tone.Transport){ return Tone.Transport.timeSignature; } else { return 4; } }; /** * Return the PPQ or 192 if Transport is not available * @type {Number} * @private */ Tone.TimeBase.prototype._getPPQ = function(){ if (Tone.Transport){ return Tone.Transport.PPQ; } else { return 192; } }; /** * Return the current time in whichever context is relevant * @type {Number} * @private */ Tone.TimeBase.prototype._now = function(){ return this.now(); }; /////////////////////////////////////////////////////////////////////////// // 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 / this._getBpm()) * 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) / this._getPPQ()); }; /** * With no arguments, return 'now' * @return {Number} * @private */ Tone.TimeBase.prototype._noArg = function(){ return this._now(); }; /////////////////////////////////////////////////////////////////////////// // EXPRESSIONS /////////////////////////////////////////////////////////////////////////// /** * Evaluate the time value. Returns the time * in seconds. * @return {Seconds} */ Tone.TimeBase.prototype.valueOf = function(){ if (Tone.isUndef(this._val)){ return this._noArg(); } else if (Tone.isString(this._val) && Tone.isUndef(this._units)){ for (var units in this._expressions){ if (this._expressions[units].regexp.test(this._val.trim())){ this._units = units; break; } } } if (!Tone.isUndef(this._units)){ var expr = this._expressions[this._units]; var matching = this._val.toString().trim().match(expr.regexp); if (matching){ return expr.method.apply(this, matching.slice(1)); } else { return expr.method.call(this, parseFloat(this._val)); } } else { return this._val; } }; /** * Return the value in seconds * @return {Seconds} */ Tone.TimeBase.prototype.toSeconds = function(){ return this.valueOf(); }; /** * Return the value in hertz * @return {Frequency} */ Tone.TimeBase.prototype.toFrequency = function(){ return 1 / this.toSeconds(); }; /** * Return the time in samples * @return {Samples} */ Tone.TimeBase.prototype.toSamples = function(){ return this.toSeconds() * this.context.sampleRate; }; /** * Return the time in milliseconds. * @return {Milliseconds} */ Tone.TimeBase.prototype.toMilliseconds = function(){ return this.toSeconds() * 1000; }; /** * Clean up * @return {Tone.TimeBase} this */ Tone.TimeBase.prototype.dispose = function(){ this._val = null; this._units = null; }; return Tone.TimeBase; });