import TickingPromise from '@/util/ticking-promise.js'; const Modulators = { flat: () => 0.5, random: () => Math.random(), sawtooth: (unit) => unit, sine: (unit) => 0.5 * (1 + Math.sin(unit * Math.PI * 2)), square: (unit) => (unit < 0.5 ? 0 : 1), triangle: (unit) => 2 * Math.abs(((unit + 0.75) % 1) - 0.5), }; export default function lfo(object, properties) { const oscillators = {}; for (const key in properties) { const property = properties[key]; const oscillator = { count: Infinity, elapsed: 0, offset: 0, ...property, }; if (!oscillator.median) { oscillator.median = oscillator.magnitude / 2; } if (!oscillator.modulators) { oscillator.modulators = [Modulators.triangle]; } else { const {modulators} = oscillator; for (const i in modulators) { if ('string' === typeof modulators[i]) { modulators[i] = Modulators[modulators[i]]; } } } oscillator.low = oscillator.median - oscillator.magnitude / 2; oscillators[key] = oscillator; } let stop; const promise = new TickingPromise( (resolve) => { stop = resolve; }, (elapsed, resolve) => { for (const key in oscillators) { const oscillator = oscillators[key]; oscillator.elapsed += elapsed; if (oscillator.elapsed >= oscillator.frequency) { if (0 === --oscillator.count) { resolve(); return; } oscillator.elapsed = oscillator.elapsed % oscillator.frequency; } const x = ((oscillator.offset + oscillator.elapsed) / oscillator.frequency) % 1; let y = 0; for (const modulator of oscillator.modulators) { y += modulator(x); } object[key] = oscillator.low + oscillator.magnitude * (y / oscillator.modulators.length); } }, ); return {stop, oscillators, promise}; }