Skip to content

Instantly share code, notes, and snippets.

@kbrgl
Created January 28, 2025 21:20
Show Gist options
  • Save kbrgl/5a57ca166f401efc0ca9165bb768e427 to your computer and use it in GitHub Desktop.
Save kbrgl/5a57ca166f401efc0ca9165bb768e427 to your computer and use it in GitHub Desktop.
/**
* Transforms a stream of bytes (Uint8Array) into 32-bit float arrays (Float32Array).
*
* This transformer is needed to convert audio data from an HTTP response body
* (ReadableStream<Uint8Array>) into the format required by the Web Audio API
* (ReadableStream<Float32Array>).
*
* The transformer buffers incoming bytes until it has enough to create complete
* 32-bit floats (4 bytes each). Any remaining bytes at the end that don't form
* a complete float (1-3 bytes) are discarded to maintain data integrity.
*
* @example
* response.body
* .pipeThrough(new Uint8ArrayToFloat32ArrayTransformer())
* .pipeTo(dst);
*/
export class Uint8ArrayToFloat32ArrayTransformer extends TransformStream<
Uint8Array,
Float32Array
> {
overflow: Uint8Array | null = null;
constructor() {
super({});
this.overflow = null;
}
start() {
this.overflow = null;
}
transform(
value: Uint8Array,
controller: TransformStreamDefaultController<Float32Array>,
) {
let shifted = value;
if (this.overflow) {
shifted = new Uint8Array([...this.overflow, ...value]);
this.overflow = null;
}
// Ensure value length is a multiple of 4 bytes (32-bit float)
const nearestMultipleOf4 = Math.trunc(shifted.length / 4) * 4;
const truncated = shifted.slice(0, nearestMultipleOf4);
if (truncated.length < shifted.length) {
this.overflow = shifted.slice(nearestMultipleOf4);
}
controller.enqueue(new Float32Array(truncated.buffer));
}
flush(_controller: TransformStreamDefaultController<Float32Array>) {
if (this.overflow) {
console.warn(
`Discarding ${this.overflow.length} leftover bytes`,
this.overflow,
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment