UNPKG

mediabunny

Version:

Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.

166 lines (141 loc) 4.19 kB
/*! * Copyright (c) 2026-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Bitstream } from '../../shared/bitstream'; import { assert, assertNever } from '../misc'; import { FileSlice, readBytes, readU16Be, readU8 } from '../reader'; type BlockSizeOrUncommon = number | 'uncommon-u16' | 'uncommon-u8'; type SampleRateOrUncommon = | number | 'uncommon-u8' | 'uncommon-u16' | 'uncommon-u16-10'; // https://www.rfc-editor.org/rfc/rfc9639.html#name-block-size-bits export const getBlockSizeOrUncommon = (bits: number): BlockSizeOrUncommon | null => { if (bits === 0b0000) { return null; } else if (bits === 0b0001) { return 192; } else if (bits >= 0b0010 && bits <= 0b0101) { return 144 * 2 ** bits; } else if (bits === 0b0110) { return 'uncommon-u8'; } else if (bits === 0b0111) { return 'uncommon-u16'; } else if (bits >= 0b1000 && bits <= 0b1111) { return 2 ** bits; } else { return null; } }; // https://www.rfc-editor.org/rfc/rfc9639.html#name-sample-rate-bits export const getSampleRateOrUncommon = ( sampleRateBits: number, streamInfoSampleRate: number, ): SampleRateOrUncommon | null => { switch (sampleRateBits) { case 0b0000: return streamInfoSampleRate; case 0b0001: return 88200; case 0b0010: return 176400; case 0b0011: return 192000; case 0b0100: return 8000; case 0b0101: return 16000; case 0b0110: return 22050; case 0b0111: return 24000; case 0b1000: return 32000; case 0b1001: return 44100; case 0b1010: return 48000; case 0b1011: return 96000; case 0b1100: return 'uncommon-u8'; case 0b1101: return 'uncommon-u16'; case 0b1110: return 'uncommon-u16-10'; default: return null; } }; // https://www.rfc-editor.org/rfc/rfc9639.html#name-coded-number export const readCodedNumber = (fileSlice: FileSlice): number => { let ones = 0; const bitstream1 = new Bitstream(readBytes(fileSlice, 1)); while (bitstream1.readBits(1) === 1) { ones++; } if (ones === 0) { return bitstream1.readBits(7); } const bitArray: number[] = []; const extraBytes = ones - 1; const bitstream2 = new Bitstream(readBytes(fileSlice, extraBytes)); const firstByteBits = 8 - ones - 1; for (let i = 0; i < firstByteBits; i++) { bitArray.unshift(bitstream1.readBits(1)); } for (let i = 0; i < extraBytes; i++) { for (let j = 0; j < 8; j++) { const val = bitstream2.readBits(1); if (j < 2) { continue; } bitArray.unshift(val); } } const encoded = bitArray.reduce((acc, bit, index) => { return acc | (bit << index); }, 0); return encoded; }; export const readBlockSize = ( slice: FileSlice, blockSizeBits: BlockSizeOrUncommon, ) => { if (blockSizeBits === 'uncommon-u16') { return readU16Be(slice) + 1; } else if (blockSizeBits === 'uncommon-u8') { return readU8(slice) + 1; } else if (typeof blockSizeBits === 'number') { return blockSizeBits; } else { assertNever(blockSizeBits); assert(false); } }; export const readSampleRate = ( slice: FileSlice, sampleRateOrUncommon: SampleRateOrUncommon, ) => { if (sampleRateOrUncommon === 'uncommon-u16') { return readU16Be(slice); } if (sampleRateOrUncommon === 'uncommon-u16-10') { return readU16Be(slice) * 10; } if (sampleRateOrUncommon === 'uncommon-u8') { return readU8(slice); } if (typeof sampleRateOrUncommon === 'number') { return sampleRateOrUncommon; } return null; }; // https://www.rfc-editor.org/rfc/rfc9639.html#section-9.1.1 export const calculateCrc8 = (data: Uint8Array) => { const polynomial = 0x07; // x^8 + x^2 + x^1 + x^0 let crc = 0x00; // Initialize CRC to 0 for (const byte of data) { crc ^= byte; // XOR byte into least significant byte of crc for (let i = 0; i < 8; i++) { // For each bit in the byte if ((crc & 0x80) !== 0) { // If the leftmost bit (MSB) is set crc = (crc << 1) ^ polynomial; // Shift left and XOR with polynomial } else { crc <<= 1; // Just shift left } crc &= 0xff; // Ensure CRC remains 8-bit } } return crc; };