69 lines
2.4 KiB
JavaScript
69 lines
2.4 KiB
JavaScript
// 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 };
|
|
}
|