export class Stereo48kToMono16kDownsampler { private readonly pendingMono48k: number[] = []; pushStereo48kChunk(chunk: Buffer): Int16Array { if (chunk.length < 4) { return new Int16Array(); } for (let offset = 0; offset + 3 < chunk.length; offset += 4) { const left = chunk.readInt16LE(offset); const right = chunk.readInt16LE(offset + 2); this.pendingMono48k.push(Math.round((left + right) / 2)); } const outputLength = Math.floor(this.pendingMono48k.length / 3); if (outputLength === 0) { return new Int16Array(); } const output = new Int16Array(outputLength); let readIndex = 0; for (let index = 0; index < outputLength; index += 1) { const a = this.pendingMono48k[readIndex]; const b = this.pendingMono48k[readIndex + 1]; const c = this.pendingMono48k[readIndex + 2]; output[index] = Math.round((a + b + c) / 3); readIndex += 3; } this.pendingMono48k.splice(0, readIndex); return output; } } export function int16ArrayToFloat32(input: Int16Array): Float32Array { const output = new Float32Array(input.length); for (let index = 0; index < input.length; index += 1) { output[index] = input[index] / 32768; } return output; } export function float32ToPcm16Buffer(input: Float32Array): Buffer { const buffer = Buffer.allocUnsafe(input.length * 2); for (let index = 0; index < input.length; index += 1) { const value = Math.max(-1, Math.min(1, input[index])); const scaled = value < 0 ? value * 32768 : value * 32767; buffer.writeInt16LE(Math.round(scaled), index * 2); } return buffer; } export function takeFrame(source: number[], frameSize: number): Int16Array | null { if (source.length < frameSize) { return null; } const values = source.splice(0, frameSize); return Int16Array.from(values); }