mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 08:38:00 +00:00
removing Tone.Expr
no more use for this class
This commit is contained in:
parent
7b35eee3ed
commit
27b1513ddf
2 changed files with 0 additions and 598 deletions
|
@ -1,457 +0,0 @@
|
|||
define(["Tone/core/Tone", "Tone/signal/Add", "Tone/signal/Subtract", "Tone/signal/Multiply",
|
||||
"Tone/signal/GreaterThan", "Tone/signal/GreaterThanZero", "Tone/signal/Abs", "Tone/signal/Negate",
|
||||
"Tone/signal/Modulo", "Tone/signal/Pow", "Tone/signal/AudioToGain"], function(Tone){
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @class Evaluate an expression at audio rate. <br><br>
|
||||
* Parsing code modified from https://code.google.com/p/tapdigit/
|
||||
* Copyright 2011 2012 Ariya Hidayat, New BSD License
|
||||
*
|
||||
* @extends {Tone.SignalBase}
|
||||
* @constructor
|
||||
* @param {string} expr the expression to generate
|
||||
* @example
|
||||
* //adds the signals from input[0] and input[1].
|
||||
* var expr = new Tone.Expr("$0 + $1");
|
||||
*/
|
||||
Tone.Expr = function(){
|
||||
|
||||
Tone.SignalBase.call(this);
|
||||
var expr = this._replacements(Array.prototype.slice.call(arguments));
|
||||
var inputCount = this._parseInputs(expr);
|
||||
|
||||
/**
|
||||
* hold onto all of the nodes for disposal
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this._nodes = [];
|
||||
|
||||
/**
|
||||
* The inputs. The length is determined by the expression.
|
||||
* @type {Array}
|
||||
*/
|
||||
this.input = new Array(inputCount);
|
||||
|
||||
//create a gain for each input
|
||||
for (var i = 0; i < inputCount; i++){
|
||||
this.input[i] = this.context.createGain();
|
||||
}
|
||||
|
||||
//parse the syntax tree
|
||||
var tree = this._parseTree(expr);
|
||||
//evaluate the results
|
||||
var result;
|
||||
try {
|
||||
result = this._eval(tree);
|
||||
} catch (e){
|
||||
this._disposeNodes();
|
||||
throw new Error("Tone.Expr: Could evaluate expression: "+expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* The output node is the result of the expression
|
||||
* @type {Tone}
|
||||
*/
|
||||
this.output = result;
|
||||
};
|
||||
|
||||
Tone.extend(Tone.Expr, Tone.SignalBase);
|
||||
|
||||
//some helpers to cut down the amount of code
|
||||
function applyBinary(Constructor, args, self){
|
||||
var op = new Constructor();
|
||||
self._eval(args[0]).connect(op, 0, 0);
|
||||
self._eval(args[1]).connect(op, 0, 1);
|
||||
return op;
|
||||
}
|
||||
function applyUnary(Constructor, args, self){
|
||||
var op = new Constructor();
|
||||
self._eval(args[0]).connect(op, 0, 0);
|
||||
return op;
|
||||
}
|
||||
function getNumber(arg){
|
||||
return arg ? parseFloat(arg) : undefined;
|
||||
}
|
||||
function literalNumber(arg){
|
||||
return arg && arg.args ? parseFloat(arg.args) : undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* the Expressions that Tone.Expr can parse.
|
||||
*
|
||||
* each expression belongs to a group and contains a regexp
|
||||
* for selecting the operator as well as that operators method
|
||||
*
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
Tone.Expr._Expressions = {
|
||||
//values
|
||||
"value" : {
|
||||
"signal" : {
|
||||
regexp : /^\d+\.\d+|^\d+/,
|
||||
method : function(arg){
|
||||
var sig = new Tone.Signal(getNumber(arg));
|
||||
return sig;
|
||||
}
|
||||
},
|
||||
"input" : {
|
||||
regexp : /^\$\d/,
|
||||
method : function(arg, self){
|
||||
return self.input[getNumber(arg.substr(1))];
|
||||
}
|
||||
}
|
||||
},
|
||||
//syntactic glue
|
||||
"glue" : {
|
||||
"(" : {
|
||||
regexp : /^\(/,
|
||||
},
|
||||
")" : {
|
||||
regexp : /^\)/,
|
||||
},
|
||||
"," : {
|
||||
regexp : /^,/,
|
||||
}
|
||||
},
|
||||
//functions
|
||||
"func" : {
|
||||
"abs" : {
|
||||
regexp : /^abs/,
|
||||
method : applyUnary.bind(this, Tone.Abs)
|
||||
},
|
||||
"mod" : {
|
||||
regexp : /^mod/,
|
||||
method : function(args, self){
|
||||
var modulus = literalNumber(args[1]);
|
||||
var op = new Tone.Modulo(modulus);
|
||||
self._eval(args[0]).connect(op);
|
||||
return op;
|
||||
}
|
||||
},
|
||||
"pow" : {
|
||||
regexp : /^pow/,
|
||||
method : function(args, self){
|
||||
var exp = literalNumber(args[1]);
|
||||
var op = new Tone.Pow(exp);
|
||||
self._eval(args[0]).connect(op);
|
||||
return op;
|
||||
}
|
||||
},
|
||||
"a2g" : {
|
||||
regexp : /^a2g/,
|
||||
method : function(args, self){
|
||||
var op = new Tone.AudioToGain();
|
||||
self._eval(args[0]).connect(op);
|
||||
return op;
|
||||
}
|
||||
},
|
||||
},
|
||||
//binary expressions
|
||||
"binary" : {
|
||||
"+" : {
|
||||
regexp : /^\+/,
|
||||
precedence : 1,
|
||||
method : applyBinary.bind(this, Tone.Add)
|
||||
},
|
||||
"-" : {
|
||||
regexp : /^-/,
|
||||
precedence : 1,
|
||||
method : function(args, self){
|
||||
//both unary and binary op
|
||||
if (args.length === 1){
|
||||
return applyUnary(Tone.Negate, args, self);
|
||||
} else {
|
||||
return applyBinary(Tone.Subtract, args, self);
|
||||
}
|
||||
}
|
||||
},
|
||||
"*" : {
|
||||
regexp : /^\*/,
|
||||
precedence : 0,
|
||||
method : applyBinary.bind(this, Tone.Multiply)
|
||||
}
|
||||
},
|
||||
//unary expressions
|
||||
"unary" : {
|
||||
"-" : {
|
||||
regexp : /^-/,
|
||||
method : applyUnary.bind(this, Tone.Negate)
|
||||
},
|
||||
"!" : {
|
||||
regexp : /^!/,
|
||||
method : applyUnary.bind(this, Tone.NOT)
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} expr the expression string
|
||||
* @return {number} the input count
|
||||
* @private
|
||||
*/
|
||||
Tone.Expr.prototype._parseInputs = function(expr){
|
||||
var inputArray = expr.match(/\$\d/g);
|
||||
var inputMax = 0;
|
||||
if (inputArray !== null){
|
||||
for (var i = 0; i < inputArray.length; i++){
|
||||
var inputNum = parseInt(inputArray[i].substr(1)) + 1;
|
||||
inputMax = Math.max(inputMax, inputNum);
|
||||
}
|
||||
}
|
||||
return inputMax;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Array} args an array of arguments
|
||||
* @return {string} the results of the replacements being replaced
|
||||
* @private
|
||||
*/
|
||||
Tone.Expr.prototype._replacements = function(args){
|
||||
var expr = args.shift();
|
||||
for (var i = 0; i < args.length; i++){
|
||||
expr = expr.replace(/%/i, args[i]);
|
||||
}
|
||||
return expr;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.Expr.prototype._tokenize = function(expr){
|
||||
var position = -1;
|
||||
var tokens = [];
|
||||
|
||||
while(expr.length > 0){
|
||||
expr = expr.trim();
|
||||
var token = getNextToken(expr);
|
||||
tokens.push(token);
|
||||
expr = expr.substr(token.value.length);
|
||||
}
|
||||
|
||||
function getNextToken(expr){
|
||||
for (var type in Tone.Expr._Expressions){
|
||||
var group = Tone.Expr._Expressions[type];
|
||||
for (var opName in group){
|
||||
var op = group[opName];
|
||||
var reg = op.regexp;
|
||||
var match = expr.match(reg);
|
||||
if (match !== null){
|
||||
return {
|
||||
type : type,
|
||||
value : match[0],
|
||||
method : op.method
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new SyntaxError("Tone.Expr: Unexpected token "+expr);
|
||||
}
|
||||
|
||||
return {
|
||||
next : function(){
|
||||
return tokens[++position];
|
||||
},
|
||||
peek : function(){
|
||||
return tokens[position + 1];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* recursively parse the string expression into a syntax tree
|
||||
*
|
||||
* @param {string} expr
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
Tone.Expr.prototype._parseTree = function(expr){
|
||||
var lexer = this._tokenize(expr);
|
||||
var isUndef = Tone.isUndef.bind(this);
|
||||
|
||||
function matchSyntax(token, syn) {
|
||||
return !isUndef(token) &&
|
||||
token.type === "glue" &&
|
||||
token.value === syn;
|
||||
}
|
||||
|
||||
function matchGroup(token, groupName, prec) {
|
||||
var ret = false;
|
||||
var group = Tone.Expr._Expressions[groupName];
|
||||
if (!isUndef(token)){
|
||||
for (var opName in group){
|
||||
var op = group[opName];
|
||||
if (op.regexp.test(token.value)){
|
||||
if (!isUndef(prec)){
|
||||
if(op.precedence === prec){
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function parseExpression(precedence) {
|
||||
if (isUndef(precedence)){
|
||||
precedence = 5;
|
||||
}
|
||||
var expr;
|
||||
if (precedence < 0){
|
||||
expr = parseUnary();
|
||||
} else {
|
||||
expr = parseExpression(precedence-1);
|
||||
}
|
||||
var token = lexer.peek();
|
||||
while (matchGroup(token, "binary", precedence)) {
|
||||
token = lexer.next();
|
||||
expr = {
|
||||
operator : token.value,
|
||||
method : token.method,
|
||||
args : [
|
||||
expr,
|
||||
parseExpression(precedence-1)
|
||||
]
|
||||
};
|
||||
token = lexer.peek();
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
function parseUnary() {
|
||||
var token, expr;
|
||||
token = lexer.peek();
|
||||
if (matchGroup(token, "unary")) {
|
||||
token = lexer.next();
|
||||
expr = parseUnary();
|
||||
return {
|
||||
operator : token.value,
|
||||
method : token.method,
|
||||
args : [expr]
|
||||
};
|
||||
}
|
||||
return parsePrimary();
|
||||
}
|
||||
|
||||
function parsePrimary() {
|
||||
var token, expr;
|
||||
token = lexer.peek();
|
||||
if (isUndef(token)) {
|
||||
throw new SyntaxError("Tone.Expr: Unexpected termination of expression");
|
||||
}
|
||||
if (token.type === "func") {
|
||||
token = lexer.next();
|
||||
return parseFunctionCall(token);
|
||||
}
|
||||
if (token.type === "value") {
|
||||
token = lexer.next();
|
||||
return {
|
||||
method : token.method,
|
||||
args : token.value
|
||||
};
|
||||
}
|
||||
if (matchSyntax(token, "(")) {
|
||||
lexer.next();
|
||||
expr = parseExpression();
|
||||
token = lexer.next();
|
||||
if (!matchSyntax(token, ")")) {
|
||||
throw new SyntaxError("Expected )");
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
throw new SyntaxError("Tone.Expr: Parse error, cannot process token " + token.value);
|
||||
}
|
||||
|
||||
function parseFunctionCall(func) {
|
||||
var token, args = [];
|
||||
token = lexer.next();
|
||||
if (!matchSyntax(token, "(")) {
|
||||
throw new SyntaxError("Tone.Expr: Expected ( in a function call \"" + func.value + "\"");
|
||||
}
|
||||
token = lexer.peek();
|
||||
if (!matchSyntax(token, ")")) {
|
||||
args = parseArgumentList();
|
||||
}
|
||||
token = lexer.next();
|
||||
if (!matchSyntax(token, ")")) {
|
||||
throw new SyntaxError("Tone.Expr: Expected ) in a function call \"" + func.value + "\"");
|
||||
}
|
||||
return {
|
||||
method : func.method,
|
||||
args : args,
|
||||
name : name
|
||||
};
|
||||
}
|
||||
|
||||
function parseArgumentList() {
|
||||
var token, expr, args = [];
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
expr = parseExpression();
|
||||
if (isUndef(expr)) {
|
||||
break;
|
||||
}
|
||||
args.push(expr);
|
||||
token = lexer.peek();
|
||||
if (!matchSyntax(token, ",")) {
|
||||
break;
|
||||
}
|
||||
lexer.next();
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
return parseExpression();
|
||||
};
|
||||
|
||||
/**
|
||||
* recursively evaluate the expression tree
|
||||
* @param {Object} tree
|
||||
* @return {AudioNode} the resulting audio node from the expression
|
||||
* @private
|
||||
*/
|
||||
Tone.Expr.prototype._eval = function(tree){
|
||||
if (!Tone.isUndef(tree)){
|
||||
var node = tree.method(tree.args, this);
|
||||
this._nodes.push(node);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* dispose all the nodes
|
||||
* @private
|
||||
*/
|
||||
Tone.Expr.prototype._disposeNodes = function(){
|
||||
for (var i = 0; i < this._nodes.length; i++){
|
||||
var node = this._nodes[i];
|
||||
if (Tone.isFunction(node.dispose)) {
|
||||
node.dispose();
|
||||
} else if (Tone.isFunction(node.disconnect)) {
|
||||
node.disconnect();
|
||||
}
|
||||
node = null;
|
||||
this._nodes[i] = null;
|
||||
}
|
||||
this._nodes = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* clean up
|
||||
*/
|
||||
Tone.Expr.prototype.dispose = function(){
|
||||
Tone.SignalBase.prototype.dispose.call(this);
|
||||
this._disposeNodes();
|
||||
};
|
||||
|
||||
return Tone.Expr;
|
||||
});
|
|
@ -1,141 +0,0 @@
|
|||
|
||||
define(["Tone/signal/Signal", "Tone/signal/Expr", "Test", "helper/Basic",
|
||||
"helper/OutputAudio", "helper/PassAudio", "helper/Offline", "helper/ConstantOutput"],
|
||||
function(Signal, Expr, Test, Basic, OutputAudio, PassAudio, Offline, ConstantOutput){
|
||||
|
||||
describe("Expr", function(){
|
||||
|
||||
Basic(Expr, "1");
|
||||
|
||||
context("I/O", function(){
|
||||
|
||||
it("can create inputs", function(){
|
||||
var exp = new Expr("$0 + $1");
|
||||
Test.connect(exp, 0);
|
||||
Test.connect(exp, 1);
|
||||
exp.dispose();
|
||||
});
|
||||
|
||||
it("has an output", function(){
|
||||
var exp = new Expr("0 + 0");
|
||||
exp.connect(Test);
|
||||
exp.dispose();
|
||||
});
|
||||
|
||||
it("outputs audio", function(){
|
||||
return OutputAudio(function(){
|
||||
new Expr("1.1").toMaster();
|
||||
});
|
||||
});
|
||||
|
||||
it("passes input", function(){
|
||||
return PassAudio(function(input){
|
||||
var exp = new Expr("$0");
|
||||
input.connect(exp);
|
||||
exp.toMaster();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
context("Parsing", function(){
|
||||
|
||||
it("can do string replacements", function(){
|
||||
return ConstantOutput(function(){
|
||||
var exp = new Expr("% + %", 0.2, 0.8);
|
||||
exp.toMaster();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
it("can do string replacements with strings", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("%", "1 + 2").toMaster();
|
||||
}, 3);
|
||||
});
|
||||
|
||||
it("handles precendence", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("8 + 16 * 4 * (2 - 1)").toMaster();
|
||||
}, 72);
|
||||
});
|
||||
|
||||
it("tolerates inconsistent spacing", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("2 * 3-2 *4 ").toMaster();
|
||||
}, -2);
|
||||
});
|
||||
|
||||
it("handles parens", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("(8 + 16) * (4 - 1)").toMaster();
|
||||
}, 72);
|
||||
});
|
||||
});
|
||||
|
||||
context("Math", function(){
|
||||
|
||||
it("does signal addition", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("1 + 3").toMaster();
|
||||
}, 4);
|
||||
});
|
||||
|
||||
it("does signal multiplication", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("1.5 * 6").toMaster();
|
||||
}, 9);
|
||||
});
|
||||
|
||||
it("does signal subtraction", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("8 - 16").toMaster();
|
||||
}, -8);
|
||||
});
|
||||
});
|
||||
|
||||
context("Unary Operators", function(){
|
||||
|
||||
it("correctly outputs negative", function(){
|
||||
return ConstantOutput(function(){
|
||||
var sig = new Signal(1);
|
||||
var exp = new Expr("-$0");
|
||||
sig.connect(exp);
|
||||
exp.toMaster();
|
||||
}, -1);
|
||||
});
|
||||
});
|
||||
|
||||
context("Functions", function(){
|
||||
|
||||
it("handles abs(-1)", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("abs(-1)").toMaster();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
it("handles abs(0.11)", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("abs(0.11)").toMaster();
|
||||
}, 0.11);
|
||||
});
|
||||
|
||||
it("handles mod(0.2, 0.9)", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("mod(0.2, 0.9)").toMaster();
|
||||
}, 0.2);
|
||||
});
|
||||
|
||||
it("handles mod(0.5, 0.25)", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("mod(0.6, 0.25)").toMaster();
|
||||
}, 0.1);
|
||||
});
|
||||
|
||||
it("computes pow(0.2, 3)", function(){
|
||||
return ConstantOutput(function(){
|
||||
new Expr("pow(0.2, 3)").toMaster();
|
||||
}, 0.008, 0.001);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in a new issue