285 lines
9.9 KiB
JavaScript
285 lines
9.9 KiB
JavaScript
|
/*
|
||
|
* This file is part of Toolkit.
|
||
|
*
|
||
|
* Toolkit is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 3 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* Toolkit is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Lesser General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General
|
||
|
* Public License along with this program; if not, write to the
|
||
|
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
"use strict";
|
||
|
(function(w, TK){
|
||
|
function range_set(value, key) {
|
||
|
this.range_x.set(key, value);
|
||
|
this.range_y.set(key, value);
|
||
|
}
|
||
|
TK.Dynamics = TK.class({
|
||
|
/**
|
||
|
* TK.Dynamics are based on {@link TK.Chart} and display the characteristics of dynamic
|
||
|
* processors. They are square widgets drawing a {@link TK.Grid} automatically based on
|
||
|
* the range.
|
||
|
*
|
||
|
* @class TK.Dynamics
|
||
|
*
|
||
|
* @extends TK.Chart
|
||
|
*
|
||
|
* @property {Object} options
|
||
|
*
|
||
|
* @param {Number} [options.min=-96] - Minimum decibels to display.
|
||
|
* @param {Number} [options.max=24] - Maximum decibels to display.
|
||
|
* @param {String} [options.scale="linear"] - Scale of the display, see {@link TK.Range} for details.
|
||
|
* @param {String} [options.type=false] - Type of the dynamics: <code>compressor</code>, <code>expander</code>, <code>gate</code>, <code>limiter</code> or <code>false</code> to draw your own graph.
|
||
|
* @param {Number} [options.threshold=0] - Threshold of the dynamics.
|
||
|
* @param {Number} [options.ratio=1] - Ratio of the dynamics.
|
||
|
* @param {Number} [options.makeup=0] - Makeup of the dynamics. This raises the whole graph after all other parameters are applied.
|
||
|
* @param {Number} [options.range=0] - Range of the dynamics. Only used in type <code>expander</code>. The maximum gain reduction.
|
||
|
* @param {Number} [options.gain=0] - Input gain of the dynamics.
|
||
|
* @param {Number} [options.reference=0] - Input reference of the dynamics.
|
||
|
* @param {Function} [options.grid_labels=function (val) { return val + (!val ? "dB":""); }] - Callback to format the labels of the {@link TK.Grid}.
|
||
|
* @param {Number} [options.db_grid=12] - Draw a grid line every [n] decibels.
|
||
|
*/
|
||
|
_class: "Dynamics",
|
||
|
Extends: TK.Chart,
|
||
|
_options: Object.assign(Object.create(TK.Chart.prototype._options), {
|
||
|
size: "number", // deprecated, undocumented. Is set via CSS.
|
||
|
min: "number",
|
||
|
max: "number",
|
||
|
scale: "string",
|
||
|
type: "string",
|
||
|
threshold: "number",
|
||
|
ratio: "number",
|
||
|
makeup: "number",
|
||
|
range: "number",
|
||
|
gain: "number",
|
||
|
reference: "number",
|
||
|
grid_labels: "function",
|
||
|
db_grid: "number",
|
||
|
}),
|
||
|
options: {
|
||
|
db_grid: 12,
|
||
|
min: -96,
|
||
|
max: 24,
|
||
|
scale: "linear",
|
||
|
type: false,
|
||
|
threshold: 0,
|
||
|
ratio: 1,
|
||
|
makeup: 0,
|
||
|
range: 0,
|
||
|
gain: 0,
|
||
|
reference: 0,
|
||
|
grid_labels: function (val) { return val + (!val ? "dB":""); }
|
||
|
},
|
||
|
static_events: {
|
||
|
set_size: function(value) {
|
||
|
TK.warn("using deprecated 'size' option");
|
||
|
this.set("width", value);
|
||
|
this.set("height", value);
|
||
|
},
|
||
|
set_min: range_set,
|
||
|
set_max: range_set,
|
||
|
set_scale: range_set,
|
||
|
},
|
||
|
initialize: function (options) {
|
||
|
TK.Chart.prototype.initialize.call(this, options, true);
|
||
|
var O = this.options;
|
||
|
/**
|
||
|
* @member {HTMLDivElement} TK.Dynamics#element - The main DIV container.
|
||
|
* Has class <code>toolkit-dynamics</code>.
|
||
|
*/
|
||
|
TK.add_class(this.element, "toolkit-dynamics");
|
||
|
this.set("scale", O.scale);
|
||
|
if (O.size) this.set("size", O.size);
|
||
|
this.set("min", O.min);
|
||
|
this.set("max", O.max);
|
||
|
/**
|
||
|
* @member {TK.Graph} TK.Dynamics#steady - The graph drawing the zero line. Has class <code>toolkit-steady</code>
|
||
|
*/
|
||
|
this.steady = this.add_graph({
|
||
|
dots: [{x:O.min, y:O.min},
|
||
|
{x:O.max, y:O.max}],
|
||
|
"class": "toolkit-steady",
|
||
|
mode: "line"
|
||
|
});
|
||
|
},
|
||
|
|
||
|
redraw: function () {
|
||
|
var O = this.options;
|
||
|
var I = this.invalid;
|
||
|
|
||
|
TK.Chart.prototype.redraw.call(this);
|
||
|
|
||
|
if (I.validate("size", "min", "max", "scale")) {
|
||
|
var grid_x = [];
|
||
|
var grid_y = [];
|
||
|
var min = this.range_x.get("min");
|
||
|
var max = this.range_x.get("max");
|
||
|
var step = O.db_grid;
|
||
|
var cls;
|
||
|
for (var i = min; i <= max; i += step) {
|
||
|
cls = i ? "" : "toolkit-highlight";
|
||
|
grid_x.push({
|
||
|
pos: i,
|
||
|
label: i === min ? "" : O.grid_labels(i),
|
||
|
"class": cls
|
||
|
});
|
||
|
grid_y.push({
|
||
|
pos: i,
|
||
|
label: i === min ? "" : O.grid_labels(i),
|
||
|
"class": cls
|
||
|
});
|
||
|
}
|
||
|
if (this.grid) {
|
||
|
this.grid.set("grid_x", grid_x);
|
||
|
this.grid.set("grid_y", grid_y);
|
||
|
}
|
||
|
|
||
|
if (this.steady)
|
||
|
this.steady.set("dots", [{x:O.min, y:O.min}, {x:O.max, y:O.max}]);
|
||
|
}
|
||
|
|
||
|
if (I.type) {
|
||
|
if (O._last_type)
|
||
|
TK.remove_class(this.element, "toolkit-" + O._last_type);
|
||
|
TK.add_class(this.element, "toolkit-" + O.type);
|
||
|
}
|
||
|
|
||
|
if (I.validate("ratio", "threshold", "range", "makeup", "gain", "reference")) {
|
||
|
this.draw_graph();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
resize: function() {
|
||
|
var O = this.options;
|
||
|
var E = this.element;
|
||
|
var S = this.svg;
|
||
|
|
||
|
/* bypass the Chart resize logic here */
|
||
|
TK.Widget.prototype.resize.call(this);
|
||
|
|
||
|
var tmp = TK.css_space(S, "border", "padding");
|
||
|
var w = TK.inner_width(E) - tmp.left - tmp.right;
|
||
|
var h = TK.inner_height(E) - tmp.top - tmp.bottom;
|
||
|
|
||
|
var s = Math.min(h, w);
|
||
|
|
||
|
if (s > 0 && s !== O._width) {
|
||
|
this.set("_width", s);
|
||
|
this.set("_height", s);
|
||
|
this.range_x.set("basis", s);
|
||
|
this.range_y.set("basis", s);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
draw_graph: function () {
|
||
|
var O = this.options;
|
||
|
if (O.type === false) return;
|
||
|
if (!this.graph) {
|
||
|
this.graph = this.add_graph({
|
||
|
dots: [{x: O.min, y: O.min},
|
||
|
{x: O.max, y: O.max}]
|
||
|
});
|
||
|
}
|
||
|
var curve = [];
|
||
|
var range = O.range;
|
||
|
var ratio = O.ratio;
|
||
|
var thres = O.threshold;
|
||
|
var gain = O.gain;
|
||
|
var ref = O.reference;
|
||
|
var makeup = O.makeup;
|
||
|
var min = O.min;
|
||
|
var max = O.max;
|
||
|
var s;
|
||
|
if (ref == 0)
|
||
|
{
|
||
|
s = 0;
|
||
|
}
|
||
|
else if (!isFinite(ratio))
|
||
|
{
|
||
|
s = ref;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
s = (1 / (Math.max(ratio, 1.001) - 1)) * ratio * ref;
|
||
|
}
|
||
|
var l = 5; // estimated width of line. dirty workaround for
|
||
|
// keeping the line end out of sight in case
|
||
|
// salient point is outside the visible are
|
||
|
switch (O.type) {
|
||
|
case "compressor":
|
||
|
// entry point
|
||
|
curve.push({x: min - l,
|
||
|
y: min + makeup - gain + ref - l});
|
||
|
// salient point
|
||
|
curve.push({x: thres + gain - s,
|
||
|
y: thres + makeup - s + ref});
|
||
|
// exit point
|
||
|
if (isFinite(ratio) && ratio > 0) {
|
||
|
curve.push({x: max,
|
||
|
y: thres + makeup + (max - thres - gain) / ratio
|
||
|
});
|
||
|
} else if (ratio === 0) {
|
||
|
curve.push({x: thres,
|
||
|
y: max
|
||
|
});
|
||
|
} else {
|
||
|
curve.push({x: max,
|
||
|
y: thres + makeup
|
||
|
});
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
case "limiter":
|
||
|
curve.push({x: min,
|
||
|
y: min + makeup - gain});
|
||
|
curve.push({x: thres + gain,
|
||
|
y: thres + makeup});
|
||
|
curve.push({x: max,
|
||
|
y: thres + makeup});
|
||
|
break;
|
||
|
case "gate":
|
||
|
curve.push({x: thres,
|
||
|
y: min});
|
||
|
curve.push({x: thres,
|
||
|
y: thres + makeup});
|
||
|
curve.push({x: max,
|
||
|
y: max + makeup});
|
||
|
break;
|
||
|
case "expander":
|
||
|
if (O.ratio !== 1) {
|
||
|
curve.push({x: min,
|
||
|
y: min + makeup + range});
|
||
|
|
||
|
var y = (ratio * range + (ratio - 1) * thres) / (ratio - 1);
|
||
|
curve.push({x: y - range,
|
||
|
y: y + makeup});
|
||
|
curve.push({x: thres,
|
||
|
y: thres + makeup});
|
||
|
}
|
||
|
else
|
||
|
curve.push({x: min,
|
||
|
y: min + makeup});
|
||
|
curve.push({x: max,
|
||
|
y: max + makeup});
|
||
|
break;
|
||
|
default:
|
||
|
TK.warn("Unsupported type", O.type);
|
||
|
}
|
||
|
this.graph.set("dots", curve);
|
||
|
},
|
||
|
set: function (key, val) {
|
||
|
if (key == "type")
|
||
|
this.options._last_type = this.options.type;
|
||
|
return TK.Chart.prototype.set.call(this, key, val);
|
||
|
},
|
||
|
});
|
||
|
})(this, this.TK);
|