File: src/filters/TriggerPoint.js
/**
* Based on SetPoint.as originally written in as3.
* Copyright (c) the Funnel development team
* http://www.funnel.cc
*
* Ported to JavaScript by Jeff Hoefs
* Copyright (c) 2011-2012 Jeff Hoefs <soundanalogous@gmail.com>
*
* Released under the MIT license. See LICENSE file for details.
*/
JSUTILS.namespace('BO.filters.TriggerPoint');
BO.filters.TriggerPoint = (function() {
"use strict";
var TriggerPoint;
// dependencies
var FilterBase = BO.filters.FilterBase;
/**
* Divides an input to 0 or 1 based on the threshold and hysteresis. You can
* also use multiple points by providing a nested array such as `[[0.4, 0.1],
* [0.7, 0.05]]`.
* See [Breakout/examples/filters/triggerpoint.html](https://github.com/soundanalogous/Breakout/blob/master/examples/filters/triggerpoint.html) for an example application.
*
* @class TriggerPoint
* @constructor
* @extends BO.filters.FilterBase
* @param {Number[]} points An array of threshold and hysteresis values
* operations for input buffers.
*/
TriggerPoint = function(points) {
this.name = "TriggerPoint";
this._points = {};
this._range = [];
this._lastStatus = 0;
if (points === undefined) {
points = [
[0.5, 0]
];
}
if (points[0] instanceof Array) {
var len = points.length;
for (var i = 0; i < len; i++) {
this._points[points[i][0]] = points[i][1];
}
} else if (typeof points[0] === "number") {
this._points[points[0]] = points[1];
}
this.updateRange();
this._lastStatus = 0;
};
TriggerPoint.prototype = JSUTILS.inherit(FilterBase.prototype);
TriggerPoint.prototype.constructor = TriggerPoint;
/**
* Override FilterBase.processSample
*/
TriggerPoint.prototype.processSample = function(val) {
var status = this._lastStatus;
var len = this._range.length;
for (var i = 0; i < len; i++) {
var range = this._range[i];
if (range[0] <= val && val <= range[1]) {
status = i;
break;
}
}
this._lastStatus = status;
return status;
};
/**
* @method addPoint
*/
TriggerPoint.prototype.addPoint = function(threshold, hysteresis) {
this._points[threshold] = hysteresis;
this.updateRange();
};
/**
* @method removePoint
*/
TriggerPoint.prototype.removePoint = function(threshold) {
// to do: verify that this works in javascript
delete this._points[threshold];
this.updateRange();
};
/**
* @method removeAllPoints
*/
TriggerPoint.prototype.removeAllPoints = function() {
this._points = {};
this.updateRange();
};
/**
* @private
* @method updateRange
*/
TriggerPoint.prototype.updateRange = function() {
this._range = [];
var keys = this.getKeys(this._points);
var firstKey = keys[0];
this._range.push([Number.NEGATIVE_INFINITY, firstKey - this._points[firstKey]]);
var len = keys.length - 1;
for (var i = 0; i < len; i++) {
var t0 = keys[i];
var t1 = keys[i + 1];
var p0 = (t0 * 1) + this._points[t0]; // multiply by 1 to force type to number
var p1 = t1 - this._points[t1];
if (p0 >= p1) {
throw new Error("The specified range overlaps...");
}
this._range.push([p0, p1]);
}
var lastKey = keys[keys.length - 1];
var positiveThresh = (lastKey * 1) + this._points[lastKey];
this._range.push([positiveThresh, Number.POSITIVE_INFINITY]);
};
/**
* @private
* @method getKeys
*/
TriggerPoint.prototype.getKeys = function(obj) {
var keys = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
keys.push(key);
}
}
return keys.sort();
};
return TriggerPoint;
}());