x-law
Version:
A-Law and mu-Law codecs for JavaScript/TypeScript
652 lines (647 loc) • 10.6 kB
JavaScript
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 };