UNPKG

wavefile

Version:

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

317 lines (304 loc) 8.95 kB
/* * imaadpcm: IMA ADPCM codec in JavaScript. * Copyright (c) 2018-2019 Rafael da Silva Rocha. * Copyright (c) 2016 acida. MIT License. * * 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 IMA ADPCM codec. * @see https://github.com/rochars/wavefile * @see https://github.com/rochars/imaadpcm */ /** * @type {!Array<number>} * @private */ const INDEX_TABLE = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]; /** * @type {!Array<number>} * @private */ const STEP_TABLE = [ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767]; /** * Encode 16-bit PCM samples into 4-bit IMA ADPCM samples. * @param {!Int16Array} samples A array of samples. * @return {!Uint8Array} */ export function encode(samples) { /** @type {!Object} */ let state = { index: 0, predicted: 0, step: 7 }; /** @type {!Uint8Array} */ let adpcmSamples = new Uint8Array((samples.length)); /** @type {!Array<number>} */ let block = []; /** @type {number} */ let fileIndex = 0; /** @type {number} */ let blockCount = 0; for (let i = 0, len = samples.length; i < len; i++) { if ((i % 505 == 0 && i != 0)) { adpcmSamples.set(encodeBlock(block, state), fileIndex); fileIndex += 256; block = []; blockCount++; } block.push(samples[i]); } let samplesLength = samples.length / 2; if (samplesLength % 2) { samplesLength++; } return adpcmSamples.slice(0, samplesLength + 512 + blockCount * 4); } /** * Decode IMA ADPCM samples into 16-bit PCM samples. * @param {!Uint8Array} adpcmSamples A array of ADPCM samples. * @param {number} blockAlign The block size. * @return {!Int16Array} */ export function decode(adpcmSamples, blockAlign=256) { /** @type {!Object} */ let state = { index: 0, predicted: 0, step: 7 }; /** @type {!Int16Array} */ let samples = new Int16Array(adpcmSamples.length * 2); /** @type {!Array<number>} */ let block = []; /** @type {number} */ let fileIndex = 0; for (let i = 0, len = adpcmSamples.length; i < len; i++) { if (i % blockAlign == 0 && i != 0) { let decoded = decodeBlock(block, state); samples.set(decoded, fileIndex); fileIndex += decoded.length; block = []; } block.push(adpcmSamples[i]); } return samples; } /** * Encode a block of 505 16-bit samples as 4-bit ADPCM samples. * @param {!Array<number>} block A sample block of 505 samples. * @param {!Object} state The encoder state. * @return {!Array<number>} */ function encodeBlock(block, state) { /** @type {!Array<number>} */ let adpcmSamples = blockHead_(block[0], state); for (let i = 3, len = block.length; i < len; i+=2) { /** @type {number} */ let sample2 = encodeSample_(block[i], state); /** @type {number} */ let sample = encodeSample_(block[i + 1], state); adpcmSamples.push((sample << 4) | sample2); } return adpcmSamples; } /** * Decode a block of ADPCM samples into 16-bit PCM samples. * @param {!Array<number>} block A adpcm sample block. * @param {!Object} state The decoder state. * @return {!Array<number>} */ function decodeBlock(block, state) { state.predicted = sign_((block[1] << 8) | block[0]); state.index = block[2]; state.step = STEP_TABLE[state.index]; /** @type {!Array<number>} */ let result = [ state.predicted, state.predicted ]; for (let i = 4, len = block.length; i < len; i++) { /** @type {number} */ let original_sample = block[i]; /** @type {number} */ let second_sample = original_sample >> 4; /** @type {number} */ let first_sample = (second_sample << 4) ^ original_sample; result.push(decodeSample_(first_sample, state)); result.push(decodeSample_(second_sample, state)); } return result; } /** * Sign a 16-bit integer. * @param {number} num A 16-bit integer. * @return {number} * @private */ function sign_(num) { return num > 32768 ? num - 65536 : num; } /** * Compress a 16-bit PCM sample into a 4-bit ADPCM sample. * @param {number} sample The sample. * @param {!Object} state The encoder state. * @return {number} * @private */ function encodeSample_(sample, state) { /** @type {number} */ let delta = sample - state.predicted; /** @type {number} */ let value = 0; if (delta >= 0) { value = 0; } else { value = 8; delta = -delta; } /** @type {number} */ let step = STEP_TABLE[state.index]; /** @type {number} */ let diff = step >> 3; if (delta > step) { value |= 4; delta -= step; diff += step; } step >>= 1; if (delta > step) { value |= 2; delta -= step; diff += step; } step >>= 1; if (delta > step) { value |= 1; diff += step; } updateEncoder_(value, diff, state); return value; } /** * Set the value for encoderPredicted_ and encoderIndex_ * after each sample is compressed. * @param {number} value The compressed ADPCM sample * @param {number} diff The calculated difference * @param {!Object} state The encoder state. * @private */ function updateEncoder_(value, diff, state) { if (value & 8) { state.predicted -= diff; } else { state.predicted += diff; } if (state.predicted < -0x8000) { state.predicted = -0x8000; } else if (state.predicted > 0x7fff) { state.predicted = 0x7fff; } state.index += INDEX_TABLE[value & 7]; if (state.index < 0) { state.index = 0; } else if (state.index > 88) { state.index = 88; } } /** * Decode a 4-bit ADPCM sample into a 16-bit PCM sample. * @param {number} nibble A 4-bit adpcm sample. * @param {!Object} state The decoder state. * @return {number} * @private */ function decodeSample_(nibble, state) { /** @type {number} */ let difference = 0; if (nibble & 4) { difference += state.step; } if (nibble & 2) { difference += state.step >> 1; } if (nibble & 1) { difference += state.step >> 2; } difference += state.step >> 3; if (nibble & 8) { difference = -difference; } state.predicted += difference; if (state.predicted > 32767) { state.predicted = 32767; } else if (state.predicted < -32767) { state.predicted = -32767; } updateDecoder_(nibble, state); return state.predicted; } /** * Update the index and step after decoding a sample. * @param {number} nibble A 4-bit adpcm sample. * @param {!Object} state The decoder state. * @private */ function updateDecoder_(nibble, state) { state.index += INDEX_TABLE[nibble]; if (state.index < 0) { state.index = 0; } else if (state.index > 88) { state.index = 88; } state.step = STEP_TABLE[state.index]; } /** * Return the head of a ADPCM sample block. * @param {number} sample The first sample of the block. * @param {!Object} state The encoder state. * @return {!Array<number>} * @private */ function blockHead_(sample, state) { encodeSample_(sample, state); /** @type {!Array<number>} */ let adpcmSamples = []; adpcmSamples.push(sample & 0xFF); adpcmSamples.push((sample >> 8) & 0xFF); adpcmSamples.push(state.index); adpcmSamples.push(0); return adpcmSamples; }