UNPKG

webpd

Version:

WebPd is a compiler for audio programming language Pure Data allowing to run .pd patches on web pages.

183 lines (172 loc) 5.71 kB
import { Interpolator } from './interpolator.js'; import { FIRLPF } from './fir-lpf.js'; import { ButterworthLPF } from './butterworth-lpf.js'; /* * Copyright (c) 2019 Rafael da Silva Rocha. * * 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. * */ /** * Default use of LPF for each resampling method. * @readonly * @enum {boolean} * @private */ const DEFAULT_LPF_USE = { 'point': false, 'linear': false, 'cubic': true, 'sinc': true }; /** * Default LPF order for each type of LPF. * @readonly * @enum {number} * @private */ const DEFAULT_LPF_ORDER = { 'IIR': 16, 'FIR': 71 }; /** * Default LPF class for each type of LPF. * @readonly * @enum {!Function} * @private */ const DEFAULT_LPF = { 'IIR': ButterworthLPF, 'FIR': FIRLPF }; /** * Change the sample rate of the samples to a new sample rate. * @param {!Array<number>|!TypedArray} samples The original samples. * @param {number} oldSampleRate The original sample rate. * @param {number} sampleRate The target sample rate. * @param {Object=} options The extra configuration, if needed. * @return {!Float64Array} the new samples. */ function resample(samples, oldSampleRate, sampleRate, options=null) { options = options || {}; // Make the new sample container /** @type {number} */ let rate = ((sampleRate - oldSampleRate) / oldSampleRate) + 1; /** @type {!Float64Array} */ let newSamples = new Float64Array(samples.length * (rate)); // Create the interpolator options.method = options.method || 'cubic'; /** @type {!Object} */ let interpolator = new Interpolator( samples.length, newSamples.length, { method: options.method, tension: options.tension || 0, sincFilterSize: options.sincFilterSize || 6, sincWindow: options.sincWindow || undefined, clip: options.clip || 'mirror' }); // Resample + LPF if (options.LPF === undefined) { options.LPF = DEFAULT_LPF_USE[options.method]; } if (options.LPF) { options.LPFType = options.LPFType || 'IIR'; const LPF = DEFAULT_LPF[options.LPFType]; // Upsampling if (sampleRate > oldSampleRate) { /** @type {!Object} */ let filter = new LPF( options.LPForder || DEFAULT_LPF_ORDER[options.LPFType], sampleRate, (oldSampleRate / 2)); upsample_( samples, newSamples, interpolator, filter); // Downsampling } else { /** @type {!Object} */ let filter = new LPF( options.LPForder || DEFAULT_LPF_ORDER[options.LPFType], oldSampleRate, sampleRate / 2); downsample_( samples, newSamples, interpolator, filter); } // Resample, no LPF } else { resample_(samples, newSamples, interpolator); } return newSamples; } /** * Resample. * @param {!Array<number>|!TypedArray} samples The original samples. * @param {!Float64Array} newSamples The container for the new samples. * @param {Object} interpolator The interpolator. * @private */ function resample_(samples, newSamples, interpolator) { // Resample for (let i = 0, len = newSamples.length; i < len; i++) { newSamples[i] = interpolator.interpolate(i, samples); } } /** * Upsample with LPF. * @param {!Array<number>|!TypedArray} samples The original samples. * @param {!Float64Array} newSamples The container for the new samples. * @param {Object} interpolator The interpolator. * @param {Object} filter The LPF object. * @private */ function upsample_(samples, newSamples, interpolator, filter) { // Resample and filter for (let i = 0, len = newSamples.length; i < len; i++) { newSamples[i] = filter.filter(interpolator.interpolate(i, samples)); } // Reverse filter filter.reset(); for (let i = newSamples.length - 1; i >= 0; i--) { newSamples[i] = filter.filter(newSamples[i]); } } /** * Downsample with LPF. * @param {!Array<number>|!TypedArray} samples The original samples. * @param {!Float64Array} newSamples The container for the new samples. * @param {Object} interpolator The interpolator. * @param {Object} filter The LPF object. * @private */ function downsample_(samples, newSamples, interpolator, filter) { // Filter for (let i = 0, len = samples.length; i < len; i++) { samples[i] = filter.filter(samples[i]); } // Reverse filter filter.reset(); for (let i = samples.length - 1; i >= 0; i--) { samples[i] = filter.filter(samples[i]); } // Resample resample_(samples, newSamples, interpolator); } export { resample };