livebook/assets/js/lib/codec.js
2024-02-03 00:58:44 +08:00

104 lines
2.5 KiB
JavaScript

/**
* Encodes PCM float-32 in native endianness into a WAV binary.
*/
export function encodePcmAsWav(buffer, numChannels, samplingRate) {
// See http://soundfile.sapp.org/doc/WaveFormat
const HEADER_SIZE = 44;
const wavBuffer = new ArrayBuffer(HEADER_SIZE + buffer.byteLength);
const view = new DataView(wavBuffer);
const numFrames = buffer.byteLength / 4;
const bytesPerSample = 4;
const blockAlign = numChannels * bytesPerSample;
const byteRate = samplingRate * blockAlign;
const dataSize = numFrames * blockAlign;
let offset = 0;
function writeUint32Big(int) {
view.setUint32(offset, int, false);
offset += 4;
}
function writeUint32(int) {
view.setUint32(offset, int, true);
offset += 4;
}
function writeUint16(int) {
view.setUint16(offset, int, true);
offset += 2;
}
function writeFloat32(int) {
view.setFloat32(offset, int, true);
offset += 4;
}
writeUint32Big(0x52494646);
writeUint32(36 + dataSize);
writeUint32Big(0x57415645);
writeUint32Big(0x666d7420);
writeUint32(16);
writeUint16(3); // 3 represents 32-bit float PCM
writeUint16(numChannels);
writeUint32(samplingRate);
writeUint32(byteRate);
writeUint16(blockAlign);
writeUint16(bytesPerSample * 8);
writeUint32Big(0x64617461);
writeUint32(dataSize);
const array = new Float32Array(buffer);
for (let i = 0; i < array.length; i++) {
writeFloat32(array[i]);
}
return wavBuffer;
}
const HEADER_LENGTH = 4;
/**
* Builds a single buffer with JSON-serialized `meta` and `buffer`.
*/
export function encodeAnnotatedBuffer(meta, buffer) {
const encoder = new TextEncoder();
const metaArray = encoder.encode(JSON.stringify(meta));
const raw = new ArrayBuffer(
HEADER_LENGTH + metaArray.byteLength + buffer.byteLength,
);
const view = new DataView(raw);
view.setUint32(0, metaArray.byteLength);
new Uint8Array(raw, HEADER_LENGTH, metaArray.byteLength).set(metaArray);
new Uint8Array(raw, HEADER_LENGTH + metaArray.byteLength).set(
new Uint8Array(buffer),
);
return raw;
}
/**
* Decodes binary annotated with JSON-serialized metadata.
*/
export function decodeAnnotatedBuffer(raw) {
const view = new DataView(raw);
const metaArrayLength = view.getUint32(0);
const metaArray = new Uint8Array(raw, HEADER_LENGTH, metaArrayLength);
const buffer = raw.slice(HEADER_LENGTH + metaArrayLength);
const decoder = new TextDecoder();
const meta = JSON.parse(decoder.decode(metaArray));
return [meta, buffer];
}