// Phoenix binary WebSocket packet decoders. Keep this module dependency-free so // protocol compatibility can be tested without a browser. export function decodePhoenixSpectroBuffer(buffer) { if (!(buffer instanceof ArrayBuffer) || buffer.byteLength < 20) return null; const view = new DataView(buffer); if (view.getUint32(0, true) !== 0x50585350) return null; const seq = view.getUint32(4, true); const sampleRate = view.getUint32(8, true); const fftSize = view.getUint32(12, true); const count = view.getUint32(16, true); if (count < 1 || buffer.byteLength !== 20 + count * 4) return null; return { seq, sampleRate, fftSize, bins: new Float32Array(buffer, 20, count), }; } export function decodePhoenixVisualsBuffer(buffer) { if (!(buffer instanceof ArrayBuffer) || buffer.byteLength < 32) return null; const view = new DataView(buffer); if (view.getUint32(0, true) !== 0x50585653 || view.getUint16(4, true) !== 1) return null; const flags = view.getUint16(6, true); const seq = view.getUint32(8, true); const xyCount = view.getUint32(12, true); const waveColumns = view.getUint32(16, true); const waveChannels = view.getUint16(20, true); const waveColumnSamples = view.getUint32(24, true); const waveSampleRate = view.getUint32(28, true); if (waveChannels > 2) return null; const xyBytes = xyCount * 8; const waveValues = waveColumns * waveChannels * 2; const expectedBytes = 32 + xyBytes + waveValues * 4; if (!Number.isSafeInteger(expectedBytes) || buffer.byteLength !== expectedBytes) return null; let offset = 32; let left = null; let right = null; if ((flags & 1) && xyCount > 0) { left = new Float32Array(xyCount); right = new Float32Array(xyCount); for (let index = 0; index < xyCount; index++) { left[index] = view.getFloat32(offset, true); right[index] = view.getFloat32(offset + 4, true); offset += 8; } } else { offset += xyBytes; } let wave = null; if ((flags & 2) && waveValues > 0 && (waveChannels === 1 || waveChannels === 2)) { const data = new Float32Array(waveValues); for (let index = 0; index < waveValues; index++) { data[index] = view.getFloat32(offset + index * 4, true); } wave = { data, columns: waveColumns, channels: waveChannels, columnSamples: waveColumnSamples, sampleRate: waveSampleRate, }; } return { seq, left, right, wave }; }