UNPKG

x-law

Version:

A-Law and mu-Law codecs for JavaScript/TypeScript

652 lines (647 loc) 10.6 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/lib/alaw.ts var alaw_exports = {}; __export(alaw_exports, { decode: () => decode, decodeBuffer: () => decodeBuffer, decodeSample: () => decodeSample, encode: () => encode, encodeBuffer: () => encodeBuffer, encodeSample: () => encodeSample }); var LOG_TABLE = [ 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 ]; function encodeSample(sample) { let compandedValue; sample = sample == -32768 ? -32767 : sample; let sign = ~sample >> 8 & 128; if (!sign) { sample = sample * -1; } if (sample > 32635) { sample = 32635; } if (sample >= 256) { let exponent = LOG_TABLE[sample >> 8 & 127]; let mantissa = sample >> exponent + 3 & 15; compandedValue = exponent << 4 | mantissa; } else { compandedValue = sample >> 4; } return compandedValue ^ (sign ^ 85); } __name(encodeSample, "encodeSample"); function decodeSample(sample) { let sign = 0; sample ^= 85; if (sample & 128) { sample &= -129; sign = -1; } let position = ((sample & 240) >> 4) + 4; let decoded = 0; if (position != 4) { decoded = 1 << position | (sample & 15) << position - 4 | 1 << position - 5; } else { decoded = sample << 1 | 1; } decoded = sign === 0 ? decoded : -decoded; return decoded * 8 * -1; } __name(decodeSample, "decodeSample"); function encode(samples) { let aLawSamples = new Uint8Array(samples.length); for (let i = 0; i < samples.length; i++) { aLawSamples[i] = encodeSample(samples[i]); } return aLawSamples; } __name(encode, "encode"); function decode(samples) { let pcmSamples = new Int16Array(samples.length); for (let i = 0; i < samples.length; i++) { pcmSamples[i] = decodeSample(samples[i]); } return pcmSamples; } __name(decode, "decode"); function encodeBuffer(buffer) { const numSamples = Math.floor(buffer.length / 2); const samples = new Int16Array(numSamples); for (let i = 0; i < numSamples; i++) { samples[i] = buffer.readInt16LE(i * 2); } return Buffer.from(encode(samples).buffer); } __name(encodeBuffer, "encodeBuffer"); function decodeBuffer(buffer) { const samples = decode(new Uint8Array(buffer)); return Buffer.from(samples.buffer); } __name(decodeBuffer, "decodeBuffer"); // src/lib/mulaw.ts var mulaw_exports = {}; __export(mulaw_exports, { decode: () => decode2, decodeBuffer: () => decodeBuffer2, decodeSample: () => decodeSample2, encode: () => encode2, encodeBuffer: () => encodeBuffer2, encodeSample: () => encodeSample2 }); var BIAS = 132; var CLIP = 32635; var encodeTable = [ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 ]; var decodeTable = [0, 132, 396, 924, 1980, 4092, 8316, 16764]; function encodeSample2(sample) { const sign = sample < 0 ? 128 : 0; sample = Math.abs(sample); sample += BIAS; if (sample > CLIP) sample = CLIP; const exponent = encodeTable[sample >> 7 & 255]; const mantissa = sample >> exponent + 3 & 15; return ~(sign | exponent << 4 | mantissa) & 255; } __name(encodeSample2, "encodeSample"); function decodeSample2(sample) { sample = ~sample & 255; const sign = sample & 128 ? -1 : 1; const exponent = sample >> 4 & 7; const mantissa = sample & 15; const decodedSample = decodeTable[exponent] + (mantissa << exponent + 3); return sign * decodedSample; } __name(decodeSample2, "decodeSample"); function encode2(samples) { const muLawSamples = new Uint8Array(samples.length); for (let i = 0; i < samples.length; i++) { muLawSamples[i] = encodeSample2(samples[i]); } return muLawSamples; } __name(encode2, "encode"); function decode2(samples) { const pcmSamples = new Int16Array(samples.length); for (let i = 0; i < samples.length; i++) { pcmSamples[i] = decodeSample2(samples[i]); } return pcmSamples; } __name(decode2, "decode"); function encodeBuffer2(buffer) { const numSamples = Math.floor(buffer.length / 2); const samples = new Int16Array(numSamples); for (let i = 0; i < numSamples; i++) { samples[i] = buffer.readInt16LE(i * 2); } return Buffer.from(encode2(samples).buffer); } __name(encodeBuffer2, "encodeBuffer"); function decodeBuffer2(buffer) { const samples = decode2(new Uint8Array(buffer)); return Buffer.from(samples.buffer); } __name(decodeBuffer2, "decodeBuffer"); // src/lib/utils.ts var utils_exports = {}; __export(utils_exports, { calculateLoudness: () => calculateLoudness, createWavHeader: () => createWavHeader, resample: () => resample }); var BIT_DEPTHS = [8, 16, 24, 32, 48]; function calculateLoudness(buffer, bitDepth) { if (!(buffer instanceof Buffer) || buffer.length === 0) { throw new Error("Invalid buffer, must be a non-empty Buffer."); } if (!BIT_DEPTHS.includes(bitDepth)) { throw new Error("Invalid bit depth, supported values are 8, 16, 24, 32, and 48."); } if (bitDepth === 48) { throw new Error("48-bit audio is not yet implemented."); } const bytesPerSample = Math.ceil(bitDepth / 8); if (buffer.length % bytesPerSample !== 0) { throw new Error( `Invalid buffer length ${buffer.length}. Must be a multiple of ${bytesPerSample} bytes for ${bitDepth}-bit audio.` ); } const maxValue = Math.pow(2, bitDepth - 1) - 1; const numSamples = buffer.length / bytesPerSample; let sumOfSquares = 0; for (let i = 0; i < numSamples; i++) { const offset = i * bytesPerSample; let sample; switch (bitDepth) { case 8: sample = buffer[offset]; if (sample & 128) sample = sample - 256; break; case 16: sample = buffer[offset] | buffer[offset + 1] << 8; if (sample & 32768) sample = sample - 65536; break; case 24: sample = buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16; if (sample & 8388608) sample = sample | -16777216; break; case 32: sample = buffer[offset] | buffer[offset + 1] << 8 | buffer[offset + 2] << 16 | buffer[offset + 3] << 24; break; default: throw new Error(`Unsupported bit depth: ${bitDepth}`); } const normalized = sample / maxValue; sumOfSquares += normalized * normalized; } const rms = Math.sqrt(sumOfSquares / numSamples); return rms <= 1e-10 ? -100 : 20 * Math.log10(rms); } __name(calculateLoudness, "calculateLoudness"); function createWavHeader(dataSize, sampleRate, channels, bitDepth) { const headerData = [ { value: "RIFF", type: "string" }, { value: 36 + dataSize, type: "uint32" }, { value: "WAVE", type: "string" }, { value: "fmt ", type: "string" }, { value: 16, type: "uint32" }, { value: 1, type: "uint16" }, { value: channels, type: "uint16" }, { value: sampleRate, type: "uint32" }, { value: sampleRate * channels * bitDepth / 8, type: "uint32" }, { value: channels * bitDepth / 8, type: "uint16" }, { value: bitDepth, type: "uint16" }, { value: "data", type: "string" }, { value: dataSize, type: "uint32" } ]; const header = Buffer.alloc(44); let offset = 0; headerData.forEach(({ value, type }) => { if (type === "string") { header.write(value, offset); offset += 4; } else if (type === "uint32") { header.writeUInt32LE(value, offset); offset += 4; } else if (type === "uint16") { header.writeUInt16LE(value, offset); offset += 2; } }); return header; } __name(createWavHeader, "createWavHeader"); var resample = /* @__PURE__ */ __name((samples, inputSampleRate, targetSampleRate, bitDepth) => { if (inputSampleRate <= 0 || targetSampleRate <= 0) { throw new Error("Sample rates must be positive."); } if (!BIT_DEPTHS.includes(bitDepth)) { throw new Error(`Invalid bit depth. Allowed values are: ${BIT_DEPTHS.join(", ")}`); } const ratio = targetSampleRate / inputSampleRate; const outLength = Math.round(samples.length * ratio); const resampled = new Array(outLength); const maxSample = (1 << bitDepth - 1) - 1; const minSample = -1 << bitDepth - 1; for (let i = 0; i < outLength; i++) { const sourcePos = i / ratio; const index1 = Math.floor(sourcePos); const index2 = Math.min(index1 + 1, samples.length - 1); const alpha = sourcePos - index1; const interpolated = samples[index1] * (1 - alpha) + samples[index2] * alpha; const intSample = Math.round(interpolated); resampled[i] = Math.max(minSample, Math.min(maxSample, intSample)); } return resampled; }, "resample"); export { alaw_exports as alaw, mulaw_exports as mulaw, utils_exports as utils };