lib/utils.js (65 lines of code) (raw):

const atob = globalThis.atob; const btoa = globalThis.btoa; /** * Basic utilities for the RealtimeAPI * @class */ export class RealtimeUtils { /** * Converts Float32Array of amplitude data to ArrayBuffer in Int16Array format * @param {Float32Array} float32Array * @returns {ArrayBuffer} */ static floatTo16BitPCM(float32Array) { const buffer = new ArrayBuffer(float32Array.length * 2); const view = new DataView(buffer); let offset = 0; for (let i = 0; i < float32Array.length; i++, offset += 2) { let s = Math.max(-1, Math.min(1, float32Array[i])); view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true); } return buffer; } /** * Converts a base64 string to an ArrayBuffer * @param {string} base64 * @returns {ArrayBuffer} */ static base64ToArrayBuffer(base64) { const binaryString = atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; } /** * Converts an ArrayBuffer, Int16Array or Float32Array to a base64 string * @param {ArrayBuffer|Int16Array|Float32Array} arrayBuffer * @returns {string} */ static arrayBufferToBase64(arrayBuffer) { if (arrayBuffer instanceof Float32Array) { arrayBuffer = this.floatTo16BitPCM(arrayBuffer); } else if (arrayBuffer instanceof Int16Array) { arrayBuffer = arrayBuffer.buffer; } let binary = ''; let bytes = new Uint8Array(arrayBuffer); const chunkSize = 0x8000; // 32KB chunk size for (let i = 0; i < bytes.length; i += chunkSize) { let chunk = bytes.subarray(i, i + chunkSize); binary += String.fromCharCode.apply(null, chunk); } return btoa(binary); } /** * Merge two Int16Arrays from Int16Arrays or ArrayBuffers * @param {ArrayBuffer|Int16Array} left * @param {ArrayBuffer|Int16Array} right * @returns {Int16Array} */ static mergeInt16Arrays(left, right) { if (left instanceof ArrayBuffer) { left = new Int16Array(left); } if (right instanceof ArrayBuffer) { right = new Int16Array(right); } if (!(left instanceof Int16Array) || !(right instanceof Int16Array)) { throw new Error(`Both items must be Int16Array`); } const newValues = new Int16Array(left.length + right.length); for (let i = 0; i < left.length; i++) { newValues[i] = left[i]; } for (let j = 0; j < right.length; j++) { newValues[left.length + j] = right[j]; } return newValues; } /** * Generates an id to send with events and messages * @param {string} prefix * @param {number} [length] * @returns {string} */ static generateId(prefix, length = 21) { // base58; non-repeating chars const chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; const str = Array(length - prefix.length) .fill(0) .map((_) => chars[Math.floor(Math.random() * chars.length)]) .join(''); return `${prefix}${str}`; } }