23 lines
1.7 KiB
JavaScript
23 lines
1.7 KiB
JavaScript
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
const definitions={
|
|
'Small room':{seconds:.5,rt60:.25,seed:0x51a11},
|
|
Hall:{seconds:2,rt60:1.4,seed:0x4a11},
|
|
Plate:{seconds:1.5,rt60:1,seed:0x91a7e},
|
|
Cathedral:{seconds:5,rt60:4,seed:0xca7ed},
|
|
};
|
|
export const BUILTIN_IRS=Object.freeze(Object.fromEntries(Object.entries(definitions).map(([name,d])=>[name,Object.freeze({...d,version:1})])));
|
|
export function generateIR(name,sampleRate,channels=2,{maxFrames=65536}={}) {
|
|
const spec=BUILTIN_IRS[name];if(!spec||!Number.isInteger(sampleRate)||sampleRate<8000||sampleRate>96000||![1,2,4].includes(channels)) throw new RangeError('Unsupported generated IR');
|
|
const frames=Math.max(1,Math.min(Math.round(spec.seconds*sampleRate),maxFrames));let seed=spec.seed>>>0;
|
|
const paths=Array.from({length:channels},()=>new Float32Array(frames));
|
|
const next=()=>{seed=(Math.imul(seed,1664525)+1013904223)>>>0;return seed/0x100000000*2-1;};
|
|
for(let path=0;path<channels;path++) {
|
|
const x=paths[path];x[0]=1;
|
|
for(let tap=1;tap<Math.min(24,frames);tap++) {const at=Math.min(frames-1,Math.round(sampleRate*(.004+tap*.009+next()*.001)));x[at]+=(next()*.22)/(1+tap*.25);}
|
|
for(let i=1;i<frames;i++) {const t=i/sampleRate, fade=Math.min(1,t/.012,(frames-i)/Math.max(1,sampleRate*.02));x[i]+=next()*.045*Math.exp(-6.91*t/spec.rt60)*Math.max(0,fade);}
|
|
}
|
|
let peak=0;for(const path of paths) for(const value of path) peak=Math.max(peak,Math.abs(value));
|
|
for(const path of paths) for(let i=0;i<path.length;i++) path[i]*=.5/peak;
|
|
return {name,sampleRate,numberOfChannels:channels,length:frames,seed:spec.seed,version:1,truncated:frames<spec.seconds*sampleRate,getChannelData:i=>paths[i]};
|
|
}
|