UNPKG

murmuraba

Version:

Real-time audio noise reduction with advanced chunked processing for web applications

86 lines (85 loc) 3.49 kB
export class AudioResampler { static resamplePCMIfNeeded(pcmData, options) { const { targetSampleRate, inputSampleRate, logger } = options; // Validate input parameters if (!pcmData || pcmData.length === 0) { throw new Error('PCM data cannot be empty'); } if (!Number.isFinite(inputSampleRate) || inputSampleRate <= 0) { throw new Error(`Invalid input sample rate: ${inputSampleRate}`); } if (!Number.isFinite(targetSampleRate) || targetSampleRate <= 0) { throw new Error(`Invalid target sample rate: ${targetSampleRate}`); } // No resampling needed if (inputSampleRate === targetSampleRate) { logger?.debug(`No resampling needed: already at ${targetSampleRate}Hz`); return { resampledData: pcmData, outputSampleRate: targetSampleRate, wasResampled: false }; } logger?.info(`Resampling from ${inputSampleRate}Hz to ${targetSampleRate}Hz...`); try { const resampled = this.resamplePCM(pcmData, inputSampleRate, targetSampleRate); logger?.debug(`Resampling complete: ${resampled.length} samples at ${targetSampleRate}Hz`); return { resampledData: resampled, outputSampleRate: targetSampleRate, wasResampled: true }; } catch (error) { throw new Error(`Resampling failed: ${error instanceof Error ? error.message : String(error)}`); } } static resampleToRNNoiseRate(pcmData, inputSampleRate, logger) { return this.resamplePCMIfNeeded(pcmData, { targetSampleRate: this.TARGET_SAMPLE_RATE, inputSampleRate, logger }); } static pcm16ToFloat32(pcm) { const f = new Float32Array(pcm.length); for (let i = 0; i < pcm.length; ++i) { f[i] = pcm[i] / 32768.0; } return f; } static float32ToPcm16(float32) { const pcm = new Int16Array(float32.length); for (let i = 0; i < float32.length; ++i) { pcm[i] = Math.max(-32768, Math.min(32767, Math.round(float32[i] * 32768))); } return pcm; } static resamplePCM(pcm, fromRate, toRate) { const input = this.pcm16ToFloat32(pcm); const output = this.linearInterpolationResample(input, fromRate, toRate); return this.float32ToPcm16(output); } /** * Simple linear interpolation resampler * This is a basic implementation that should work for most audio resampling needs */ static linearInterpolationResample(input, fromRate, toRate) { if (fromRate === toRate) { return input; } const ratio = fromRate / toRate; const outputLength = Math.floor(input.length / ratio); const output = new Float32Array(outputLength); for (let i = 0; i < outputLength; i++) { const srcIndex = i * ratio; const srcIndexFloor = Math.floor(srcIndex); const srcIndexCeil = Math.min(srcIndexFloor + 1, input.length - 1); const fraction = srcIndex - srcIndexFloor; // Linear interpolation between two samples output[i] = input[srcIndexFloor] * (1 - fraction) + input[srcIndexCeil] * fraction; } return output; } } AudioResampler.TARGET_SAMPLE_RATE = 48000;