UNPKG

prx-wavefile

Version:

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

158 lines (149 loc) 5.11 kB
/* * Copyright (c) 2017-2018 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. * */ /** * @fileoverview A module to change the bit depth of PCM samples. * @see https://github.com/rochars/wavefile * @see https://github.com/rochars/bitdepth */ /** * Change the bit depth of PCM samples. * @param {!Array|!TypedArray} samples The original samples. * @param {string} bithDepth The original bit depth. * @param {!TypedArray} newSamples The output array. * @param {string} targetBitDepth The target bit depth. * @throws {Error} If original or target bit depths are not valid. */ export function changeBitDepth(samples, bithDepth, newSamples, targetBitDepth) { // float to float, just copy the values if (["32f","64"].indexOf(bithDepth) > -1 && ["32f","64"].indexOf(targetBitDepth) > -1) { newSamples.set(samples); return; } validateBitDepth_(bithDepth); validateBitDepth_(targetBitDepth); /** @type {!Function} */ let toFunction = getBitDepthFunction_(bithDepth, targetBitDepth); /** @type {!Object<string, number>} */ let options = { oldMin: Math.pow(2, parseInt(bithDepth, 10)) / 2, newMin: Math.pow(2, parseInt(targetBitDepth, 10)) / 2, oldMax: (Math.pow(2, parseInt(bithDepth, 10)) / 2) - 1, newMax: (Math.pow(2, parseInt(targetBitDepth, 10)) / 2) - 1, }; // sign the samples if original is 8-bit sign8Bit_(bithDepth, samples, true); // change the resolution of the samples for (let i = 0, len = samples.length; i < len; i++) { newSamples[i] = toFunction(samples[i], options); } // unsign the samples if target is 8-bit sign8Bit_(targetBitDepth, newSamples, false); } /** * Change the bit depth from int to int. * @param {number} sample The sample. * @param {!Object<string, number>} args Data about the bit depths. * @return {number} * @private */ function intToInt_(sample, args) { if (sample > 0) { sample = parseInt((sample / args.oldMax) * args.newMax, 10); } else { sample = parseInt((sample / args.oldMin) * args.newMin, 10); } return sample; } /** * Change the bit depth from float to int. * @param {number} sample The sample. * @param {!Object<string, number>} args Data about the bit depths. * @return {number} * @private */ function floatToInt_(sample, args) { return parseInt( sample > 0 ? sample * args.newMax : sample * args.newMin, 10); } /** * Change the bit depth from int to float. * @param {number} sample The sample. * @param {!Object<string, number>} args Data about the bit depths. * @return {number} * @private */ function intToFloat_(sample, args) { return sample > 0 ? sample / args.oldMax : sample / args.oldMin; } /** * Return the function to change the bit depth of a sample. * @param {string} original The original bit depth of the data. * @param {string} target The new bit depth of the data. * @return {!Function} * @private */ function getBitDepthFunction_(original, target) { /** @type {!Function} */ let func = function(x) {return x;}; if (original != target) { if (["32f", "64"].includes(original)) { func = floatToInt_; } else { if (["32f", "64"].includes(target)) { func = intToFloat_; } else { func = intToInt_; } } } return func; } /** * Validate the bit depth. * @param {string} bitDepth The original bit depth. * @throws {Error} If bit depth is not valid. * @private */ function validateBitDepth_(bitDepth) { if ((bitDepth != "32f" && bitDepth != "64") && (parseInt(bitDepth, 10) < "8" || parseInt(bitDepth, 10) > "53")) { throw new Error("Invalid bit depth."); } } /** * Sign samples if they are 8-bit. * @param {string} bitDepth The bit depth code. * @param {!Array|!TypedArray} samples The samples. * @param {boolean} sign True to sign, false to unsign. * @private */ function sign8Bit_(bitDepth, samples, sign) { if (bitDepth == "8") { let factor = sign ? -128 : 128; for (let i = 0, len = samples.length; i < len; i++) { samples[i] = samples[i] += factor; } } }