diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index a03658a8..6449969e 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1214,6 +1214,16 @@ const generic_params = [ * @name waveloss */ ['waveloss'], + /* + * Noise crackle density + * + * @name density + * @param {number | Pattern} density between 0 and x + * @example + * s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)) + * + */ + ['density'], // TODO: midi effects? ['dur'], // ['modwheel'], diff --git a/packages/superdough/noise.mjs b/packages/superdough/noise.mjs index 2c8c1d4a..24779470 100644 --- a/packages/superdough/noise.mjs +++ b/packages/superdough/noise.mjs @@ -4,7 +4,7 @@ import { getAudioContext } from './superdough.mjs'; let noiseCache = {}; // lazy generates noise buffers and keeps them forever -function getNoiseBuffer(type) { +function getNoiseBuffer(type, density) { const ac = getAudioContext(); if (noiseCache[type]) { return noiseCache[type]; @@ -34,17 +34,26 @@ function getNoiseBuffer(type) { output[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362; output[i] *= 0.11; b6 = white * 0.115926; + } else if (type === 'crackle') { + const probability = density * 0.01; + if (Math.random() < probability) { + output[i] = Math.random() * 2 - 1; + } else { + output[i] = 0; + } } } - noiseCache[type] = noiseBuffer; + + // Prevent caching to randomize crackles + if (type !== 'crackle') noiseCache[type] = noiseBuffer; return noiseBuffer; } // expects one of noises as type -export function getNoiseOscillator(type = 'white', t) { +export function getNoiseOscillator(type = 'white', t, density = 0.02) { const ac = getAudioContext(); const o = ac.createBufferSource(); - o.buffer = getNoiseBuffer(type); + o.buffer = getNoiseBuffer(type, density); o.loop = true; o.start(t); return { diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 00e2f42c..ed133366 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -241,6 +241,7 @@ export const superdough = async (value, deadline, hapDuration) => { source, gain = 0.8, postgain = 1, + density = 0.03, // filters ftype = '12db', fanchor = 0.5, diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index dafc2e7c..edc3c01e 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -22,7 +22,7 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { }; const waveforms = ['sine', 'square', 'triangle', 'sawtooth']; -const noises = ['pink', 'white', 'brown']; +const noises = ['pink', 'white', 'brown', 'crackle']; export function registerSynthSounds() { [...waveforms, ...noises].forEach((s) => { @@ -36,7 +36,8 @@ export function registerSynthSounds() { if (waveforms.includes(s)) { sound = getOscillator(s, t, value); } else { - sound = getNoiseOscillator(s, t); + let { density } = value; + sound = getNoiseOscillator(s, t, density); } let { node: o, stop, triggerRelease } = sound; diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 432276ef..d6ac5061 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -42,6 +42,10 @@ Some amount of pink noise can also be added to any oscillator by using the `nois ").scope()`} /> +You can also use the `crackle` type to play some subtle noise crackles. You can control noise amount by using the `density` parameter: + +".slow(4)).scope()`} /> + ### Additive Synthesis To tame the harsh sound of the basic waveforms, we can set the `n` control to limit the overtones of the waveform: