prx-wavefile
Version:
Create, read and write wav files according to the specs.
158 lines (149 loc) • 5.11 kB
JavaScript
/*
* 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;
}
}
}