UNPKG

wavefile

Version:

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

246 lines (231 loc) 8.81 kB
/* * Copyright (c) 2017-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. * */ /** * @fileoverview binary parser. * @see https://github.com/rochars/byte-data * @see https://github.com/rochars/wavefile */ import { endianness } from './lib/endianness'; import { pack as packUTF8, unpack as unpackUTF8 } from './lib/utf8-parser'; import { IntParser } from './lib/int-parser'; import { FloatParser } from './lib/float-parser'; /** * Read a string of UTF-8 characters from a byte buffer. * @param {!(Uint8Array|Array<number>)} buffer A byte buffer. * @param {number} [index=0] The buffer index to start reading. * @param {number} [end=buffer.length] The index to stop reading, non inclusive. * @return {string} */ export function unpackString(buffer, index=0, end=buffer.length) { return unpackUTF8(buffer, index, end); } /** * Write a string of UTF-8 characters as a byte buffer. * @param {string} str The string to pack. * @return {!Array<number>} The UTF-8 string bytes. */ export function packString(str) { /** @type {!Array<number>} */ let buffer = []; packUTF8(str, buffer); return buffer; } /** * Write a string of UTF-8 characters to a byte buffer. * @param {string} str The string to pack. * @param {!(Uint8Array|Array<number>)} buffer The output buffer. * @param {number} [index=0] The buffer index to start writing. * @return {number} The next index to write in the buffer. */ export function packStringTo(str, buffer, index=0) { return packUTF8(str, buffer, index); } // Numbers /** * Pack a array of numbers to a byte buffer. * All other packing functions are interfaces to this function. * @param {!(Array<number>|TypedArray)} values The values to pack. * @param {!{bits:number, * fp: (boolean|undefined), * signed: (boolean|undefined), * be: (boolean|undefined)}} theType The type definition. * @param {!(Uint8Array|Array<number>)} buffer The buffer to write on. * @param {number} [index=0] The buffer index to start writing. * @return {number} The next index to write. * @throws {Error} If the type definition is not valid. */ export function packArrayTo(values, theType, buffer, index=0) { theType = theType || {}; /** @type {!Object} */ let packer = getParser_(theType.bits, theType.fp, theType.signed); /** @type {number} */ let offset = Math.ceil(theType.bits / 8); /** @type {number} */ let i = 0; /** @type {number} */ let start = index; for (let valuesLen = values.length; i < valuesLen; i++) { index = packer.pack(buffer, values[i], index); } if (theType.be) { endianness(buffer, offset, start, index); } return index; } /** * Unpack a array of numbers from a byte buffer to a array or a typed array. * All other unpacking functions are interfaces to this function. * @param {!(Uint8Array|Array<number>)} buffer The byte buffer. * @param {!{bits:number, * fp: (boolean|undefined), * signed: (boolean|undefined), * be: (boolean|undefined)}} theType The type definition. * @param {!(TypedArray|Array<number>)} output The output array or typed array. * @param {number} [start=0] The buffer index to start reading. * @param {number} [end=buffer.length] The buffer index to stop reading. * @throws {Error} If the type definition is not valid. */ export function unpackArrayTo( buffer, theType, output, start=0, end=buffer.length) { theType = theType || {}; /** @type {!Object} */ let parser = getParser_(theType.bits, theType.fp, theType.signed); // getUnpackLen_ will adjust the end index according to the size // of the input buffer and the byte offset or throw a error on bad // end index if safe=true end = getUnpackLen_(buffer, start, end, parser.offset); if (theType.be) { /** @type {!(Uint8Array|Array<number>)} */ let readBuffer = copyBuffer_(buffer); if (theType.be) { endianness(readBuffer, parser.offset, start, end); } unpack_(readBuffer, output, start, end, parser); } else { unpack_(buffer, output, start, end, parser); } } /** * Pack a number to a byte buffer. * @param {number} value The value. * @param {!{bits:number, * fp: (boolean|undefined), * signed: (boolean|undefined), * be: (boolean|undefined)}} theType The type definition. * @param {!(Uint8Array|Array<number>)} buffer The byte buffer to write on. * @param {number} [index=0] The buffer index to write. * @return {number} The next index to write. * @throws {Error} If the type definition is not valid. */ export function packTo(value, theType, buffer, index=0) { return packArrayTo([value], theType, buffer, index); } /** * Pack a number as a array of bytes. * @param {number} value The number to pack. * @param {!{bits:number, * fp: (boolean|undefined), * signed: (boolean|undefined), * be: (boolean|undefined)}} theType The type definition. * @return {!Array<number>} The packed value. * @throws {Error} If the type definition is not valid. */ export function pack(value, theType) { /** @type {!Array<number>} */ let output = []; packTo(value, theType, output, 0); return output; } /** * Unpack a number from a byte buffer. * @param {!(Uint8Array|Array<number>)} buffer The byte buffer. * @param {!{bits:number, * fp: (boolean|undefined), * signed: (boolean|undefined), * be: (boolean|undefined)}} theType The type definition. * @param {number} [index=0] The buffer index to read. * @return {number} * @throws {Error} If the type definition is not valid. */ export function unpack(buffer, theType, index=0) { let output = []; unpackArrayTo(buffer, theType, output, index, index + Math.ceil(theType.bits / 8)); return output[0]; } /** * Unpack a array of numbers from a byte buffer to a array or a typed array. * @param {!(Uint8Array|Array<number>)} buffer The byte buffer. * @param {!(TypedArray|Array<number>)} output The output array or typed array. * @param {number} start The buffer index to start reading. * @param {number} end The buffer index to stop reading. * @param {!Object} parser The parser. * @private */ function unpack_(buffer, output, start, end, parser) { /** @type {number} */ let offset = parser.offset; for (let index = 0, j = start; j < end; j += offset, index++) { output[index] = parser.unpack(buffer, j); } } /** * Copy a byte buffer as a Array or Uint8Array. * @param {!(Uint8Array|Array<number>)} buffer The byte buffer. * @return {!(Uint8Array|Array<number>)} * @private */ function copyBuffer_(buffer) { return new Uint8Array(buffer); } /** * Adjust the end index according to the input buffer length and the * type offset. * @param {!(Uint8Array|Array<number>)} buffer The byte buffer. * @param {number} start The buffer index to start reading. * @param {number} end The buffer index to stop reading. * @param {number} offset The number of bytes used by the type. * @private */ function getUnpackLen_(buffer, start, end, offset) { /** @type {number} */ let extra = (end - start) % offset; return end - extra; } /** * Return a parser for int, uint or fp numbers. * @param {number} bits The number of bits. * @param {boolean|undefined} fp True for fp numbers, false otherwise. * @param {boolean|undefined} signed True for signed ints, false otherwise. * @return {!Object} * @private */ function getParser_(bits, fp, signed) { if (fp && bits == 32) { return new FloatParser(8, 23); } else if(fp && bits == 64) { return new FloatParser(11, 52); } return new IntParser(bits, signed); }