UNPKG

wavefile

Version:

Create, read and write wav files according to the specs.

143 lines (134 loc) 4.24 kB
/* * Copyright (c) 2019 Rafael da Silva Rocha. * Copyright (c) 2014 Florian Markert * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** * @fileoverview Butterworth LPF. Based on the Butterworth LPF from Fili.js. * @see https://github.com/rochars/wavefile * @see https://github.com/markert/fili.js */ /** * Butterworth LPF. */ export class ButterworthLPF { /** * @param {number} order The order of the filter. * @param {number} sampleRate The sample rate. * @param {number} cutOff The cut off frequency. */ constructor(order, sampleRate, cutOff) { /** @type {!Array} */ let filters = []; for (let i = 0; i < order; i++) { filters.push(this.getCoeffs_({ Fs: sampleRate, Fc: cutOff, Q: 0.5 / (Math.sin((Math.PI / (order * 2)) * (i + 0.5))) })); } this.stages = []; for (let i = 0; i < filters.length; i++) { this.stages[i] = { b0 : filters[i].b[0], b1 : filters[i].b[1], b2 : filters[i].b[2], a1 : filters[i].a[0], a2 : filters[i].a[1], k : filters[i].k, z : [0, 0] }; } } /** * @param {number} sample A sample of a sequence. * @return {number} */ filter(sample) { /** @type {number} */ let out = sample; for (let i = 0, len = this.stages.length; i < len; i++) { out = this.runStage_(i, out); } return out; } /** * @param {!Object} params The filter params. * @return {!Object} */ getCoeffs_(params) { /** @type {!Object} */ let coeffs = {}; coeffs.a = []; coeffs.b = []; /** @type {!Object} */ let p = this.preCalc_(params, coeffs); coeffs.k = 1; coeffs.b.push((1 - p.cw) / (2 * p.a0)); coeffs.b.push(2 * coeffs.b[0]); coeffs.b.push(coeffs.b[0]); return coeffs; } /** * @param {!Object} params The filter params. * @param {!Object} coeffs The coefficients template. * @return {!Object} */ preCalc_(params, coeffs) { /** @type {!Object} */ let pre = {}; /** @type {number} */ let w = 2 * Math.PI * params.Fc / params.Fs; pre.alpha = Math.sin(w) / (2 * params.Q); pre.cw = Math.cos(w); pre.a0 = 1 + pre.alpha; coeffs.a0 = pre.a0; coeffs.a.push((-2 * pre.cw) / pre.a0); coeffs.k = 1; coeffs.a.push((1 - pre.alpha) / pre.a0); return pre; } /** * @param {number} i The stage index. * @param {number} sample The sample. * @return {number} */ runStage_(i, sample) { /** @type {number} */ let temp = sample * this.stages[i].k - this.stages[i].a1 * this.stages[i].z[0] - this.stages[i].a2 * this.stages[i].z[1]; /** @type {number} */ let out = this.stages[i].b0 * temp + this.stages[i].b1 * this.stages[i].z[0] + this.stages[i].b2 * this.stages[i].z[1]; this.stages[i].z[1] = this.stages[i].z[0]; this.stages[i].z[0] = temp; return out; } /** * Reset the filter. */ reset() { for (let i = 0; i < this.stages.length; i++) { this.stages[i].z = [0, 0]; } } }