Source: widgets/crossover.js

/*
 * 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 invalidate_bands() {
    this.invalid.bands = true;
    this.trigger_draw();
}

function sort_bands() {
    this.bands.sort(function (a, b) {
        return a.options.freq - b.options.freq;
    });
}

function limit_bands () {
    if (this.options.leap) return;
    sort_bands.call(this);
    for (var i = 0; i < this.bands.length; i++)
        _set_freq.call(this, i, this.bands[i]);
}

function set_freq (band) {
    if (this.options.leap) return;
    var i = this.bands.indexOf(band);
    if (i < 0) {
        TK.error("Band no member of crossover");
        return;
    }
    _set_freq.call(this, i, band);
}

function _set_freq (i, band) {
    var freq = band.options.freq;
    var dist = freq * this.get("distance")
    if (i)
        this.bands[i-1].set("x_max", freq - dist);
    if (i < this.bands.length-1)
        this.bands[i+1].set("x_min", freq + dist);
}

TK.Crossover = TK.class({
    /**
     * TK.Crossover is a {@link TK.Equalizer} displaying the response
     * of a multi-band crossover filter. TK.Crossover  uses {@link TK.CrossoverBand}
     * as response handles.
     * 
     * @class TK.Crossover
     * 
     * @extends TK.Equalizer
     * 
     * @param {Object} [options={ }] - An object containing initial options.
     * 
     * @property {Boolean} [options.leap=true] - Define if bands are allowed to leap over each other.
     * @property {Number} [options.distance=0] - Set a minimal distance between bands if leaping is not allowed.
     *   Value is a factor of x. Example: if distance=0.2 a band cannot be moved beyond 800Hz if the upper next
     *   band is at 1kHz.
     */
    _class: "Crossover",
    Extends: TK.Equalizer,
    _options: Object.assign(Object.create(TK.Equalizer.prototype._options), {
        leap: "boolean",
        distance: "number",
    }),
    options: {
        range_y: {min:-60, max: 12, scale: "linear"},
        leap: true,
        distance: 0,
    },
    initialize: function (options) {
        TK.Equalizer.prototype.initialize.call(this, options);
        /**
         * @member {HTMLDivElement} TK.Equalizer#element - The main DIV container.
         *   Has class <code>toolkit-response-handler</code>.
         */
        TK.add_class(this.element, "toolkit-crossover");
    },
    resize: function () {
        invalidate_bands.call(this);
        TK.Equalizer.prototype.resize.call(this);
    },
    redraw: function () {
        var I = this.invalid;
        var O = this.options;
        var lastb = this.bands.length - 1;
        var lastg = this.graphs.length - 1;
        if (I.validate("bands", "accuracy")) {
            I.bands = false;
            I.accuracy = false;
            sort_bands.call(this);
            for (var i = 0; i < this.bands.length; i++) {
                var f = [this.bands[i].lower.get_freq2gain()];
                if (i)
                    f.push(this.bands[i-1].upper.get_freq2gain());
                this._draw_graph(this.graphs[i], f);
            }
            this._draw_graph(this.graphs[lastg], [this.bands[lastb].upper.get_freq2gain()]);
        }
        TK.Equalizer.prototype.redraw.call(this);
    },
    add_band: function (options, type) {
        type = type || TK.CrossoverBand;
        this.add_graph();
        var r = TK.Equalizer.prototype.add_band.call(this, options, type);
        var that = this;
        r.add_event("set_freq", function (f) {
            set_freq.call(that, this);
        });
        limit_bands.call(this);
        return r;
    },
    remove_band: function (band) {
        this.remove_graph(this.graphs[this.graphs.length-1]);
        var r = TK.Equalizer.prototype.remove_band.call(this, options);
        limit_bands.call(this);
        return r;
    },
});

TK.CrossoverBand = TK.class({
    /**
     * TK.CrossoverBand is a {@link TK.EqBand} with an additional filter.
     * 
     * @param {Object} [options={ }] - An object containing additional options.
     * 
     * @property {String|Function} [lower="lowpass3"] - The type of filter for the range below cutoff frequency. See {@link TK.EqBand} for more information.
     * @property {String|Function} [upper="highpass3"] - The type of filter for the range above cutoff frequency. See {@link TK.EqBand} for more information.
     * @property {Function} [label=function (t, x, y, z) { return TK.sprintf("%.2f Hz", x); }] - The function formatting the handles label.
     * 
     * @class TK.CrossoverBand
     * 
     * @extends TK.EqBand
     */
    _class: "CrossoverBand",
    Extends: TK.EqBand,
    _options: Object.assign(Object.create(TK.EqBand.prototype._options), {
        lower: "string",
        upper: "string",
    }),
    options: {
        lower: "lowpass3",
        upper: "highpass3",
        label: function (t, x, y, z) { return TK.sprintf("%.2f Hz", x); },
        mode: "line-vertical", // undocumented, just a default differing from TK.ResponseHanlde
        preferences: [ "top-right", "right", "bottom-right", "top-left", "left", "bottom-left"], // undocumented, just a default differing from TK.ResponseHanlde
    },
    initialize: function (options) {
        /**
         * @member {TK.Filter} TK.CrossoverBand#upper - The filter providing the graphical calculations for the upper graph. 
         */
        this.upper = new TK.Filter();
        /**
         * @member {TK.Filter} TK.CrossoverBand#lower - The filter providing the graphical calculations for the lower graph. 
         */
        this.lower = new TK.Filter();
        TK.EqBand.prototype.initialize.call(this, options);
        /** 
         * @member {HTMLDivElement} TK.CrossoverBand#element - The main SVG group.
         *   Has class <code>toolkit-crossoverband</code>.
         */
        TK.add_class(this.element, "toolkit-crossoverband");
        
        this.set("lower", this.options.lower);
        this.set("upper", this.options.upper);
    },
    set: function (key, val) {
        switch (key) {
            case"lower":
                this.filter = this.lower;
                var r = TK.EqBand.prototype.set.call(this, "type", val);
                this.set("mode", "line-vertical");
                return r;
            case "upper":
                this.filter = this.upper;
                var r = TK.EqBand.prototype.set.call(this, "type", val);
                this.set("mode", "line-vertical");
                return r;
            case "freq":
            case "gain":
            case "q":
                if (this.lower)
                    this.filter = this.lower;
                val = TK.EqBand.prototype.set.call(this, key, val);
                this.filter = this.upper;
                return TK.EqBand.prototype.set.call(this, key, val);
        }
        return TK.EqBand.prototype.set.call(this, key, val);
    },
});

})(this, this.TK);