1import { drywet, onceEnded, releaseAudioNode } from './helpers.mjs'; 2import { getAudioContext } from './audioContext.mjs'; 3 4let noiseCache = {}; 5 6// lazy generates noise buffers and keeps them forever 7export function getNoiseBuffer(type, density) { 8 const ac = getAudioContext(); 9 if (noiseCache[type]) { 10 return noiseCache[type]; 11 } 12 const bufferSize = 2 * ac.sampleRate; 13 const noiseBuffer = ac.createBuffer(1, bufferSize, ac.sampleRate); 14 const output = noiseBuffer.getChannelData(0); 15 let lastOut = 0; 16 let b0, b1, b2, b3, b4, b5, b6; 17 b0 = b1 = b2 = b3 = b4 = b5 = b6 = 0.0; 18 19 for (let i = 0; i < bufferSize; i++) { 20 if (type === 'white') { 21 output[i] = Math.random() * 2 - 1; 22 } else if (type === 'brown') { 23 let white = Math.random() * 2 - 1; 24 output[i] = (lastOut + 0.02 * white) / 1.02; 25 lastOut = output[i]; 26 } else if (type === 'pink') { 27 let white = Math.random() * 2 - 1; 28 b0 = 0.99886 * b0 + white * 0.0555179; 29 b1 = 0.99332 * b1 + white * 0.0750759; 30 b2 = 0.969 * b2 + white * 0.153852; 31 b3 = 0.8665 * b3 + white * 0.3104856; 32 b4 = 0.55 * b4 + white * 0.5329522; 33 b5 = -0.7616 * b5 - white * 0.016898; 34 output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; 35 output[i] *= 0.11; 36 b6 = white * 0.115926; 37 } else if (type === 'crackle') { 38 const probability = density * 0.01; 39 if (Math.random() < probability) { 40 output[i] = Math.random() * 2 - 1; 41 } else { 42 output[i] = 0; 43 } 44 } 45 } 46 47 // Prevent caching to randomize crackles 48 if (type !== 'crackle') noiseCache[type] = noiseBuffer; 49 return noiseBuffer; 50} 51 52// expects one of noises as type 53export function getNoiseOscillator(type = 'white', t, density = 0.02) { 54 const ac = getAudioContext(); 55 const o = ac.createBufferSource(); 56 o.buffer = getNoiseBuffer(type, density); 57 o.loop = true; 58 o.start(t); 59 return { 60 node: o, 61 stop: (time) => o.stop(time), 62 }; 63} 64 65export function getNoiseMix(inputNode, wet, t) { 66 const noiseOscillator = getNoiseOscillator('pink', t); 67 const noiseMix = drywet(inputNode, noiseOscillator.node, wet); 68 onceEnded(noiseOscillator.node, () => { 69 releaseAudioNode(noiseOscillator.node); 70 }); 71 return { 72 node: noiseMix.node, 73 stop: (time) => noiseOscillator?.stop(time), 74 teardown: noiseMix.teardown, 75 }; 76}