// SPDX-License-Identifier: GPL-3.0-or-later const copyBuffer=(buffer,rate=buffer.sampleRate)=>{ const channels=Array.from({length:buffer.numberOfChannels},(_,i)=>new Float32Array(buffer.getChannelData(i))); return {sampleRate:rate,numberOfChannels:channels.length,length:channels[0]?.length||0,getChannelData:i=>channels[i]}; }; export async function decodeIR(context,bytes) { const decoded=await context.decodeAudioData(bytes.slice(0)); return resampleIR(decoded,context.sampleRate); } export function resampleIR(buffer,targetRate) { if(!Number.isFinite(targetRate)||targetRate<8000||targetRate>96000||!buffer?.numberOfChannels||buffer.numberOfChannels>4||!buffer.length) throw new RangeError('Invalid IR or target sample rate'); if(buffer.sampleRate===targetRate) return copyBuffer(buffer,targetRate); const length=Math.max(1,Math.round(buffer.length*targetRate/buffer.sampleRate)); const channels=[]; for(let ch=0;chchannels[i]}; } export function trimIR(buffer,{thresholdDb=-80}={}) { if(!Number.isFinite(thresholdDb)||thresholdDb>0||thresholdDb<-160) throw new RangeError('Invalid trim threshold'); const channels=Array.from({length:buffer.numberOfChannels},(_,i)=>buffer.getChannelData(i)); const threshold=10**(thresholdDb/20);let first=buffer.length,last=-1; for(let i=0;i=threshold){first=Math.min(first,i);last=i;}} if(last<0) first=last=0; const result=channels.map(channel=>channel.slice(first,last+1)); return {buffer:{sampleRate:buffer.sampleRate,numberOfChannels:result.length,length:result[0]?.length||0,getChannelData:i=>result[i]},startFrame:first,endFrame:last+1}; } export function normalizeIR(buffer,{peak=0.5}={}) { if(!Number.isFinite(peak)||peak<=0||peak>1) throw new RangeError('Peak must be in (0, 1]'); const channels=Array.from({length:buffer.numberOfChannels},(_,i)=>new Float32Array(buffer.getChannelData(i))); let max=0;for(const channel of channels) for(const value of channel) max=Math.max(max,Math.abs(value)); const gain=max===0?1:peak/max;for(const channel of channels) for(let i=0;ichannels[i]}; } export function prepareIR(context,buffer,{trim=false,normalize=true,peak=0.5,thresholdDb=-80}={}) { let result=resampleIR(buffer,context.sampleRate); if(trim) result=trimIR(result,{thresholdDb}).buffer; if(normalize) result=normalizeIR(result,{peak}); return result; }