75 lines
2.2 KiB
JavaScript
75 lines
2.2 KiB
JavaScript
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 = {};
|
|
const promises = [];
|
|
for (const key in properties) {
|
|
const property = properties[key];
|
|
const oscillator = {
|
|
count: Infinity,
|
|
elapsed: 0,
|
|
offset: 0,
|
|
...property,
|
|
};
|
|
if (!('median' in oscillator)) {
|
|
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;
|
|
oscillator.promise = new Promise((resolve) => {
|
|
oscillator.stop = resolve;
|
|
});
|
|
oscillator.promise.then(() => {
|
|
delete oscillators[key];
|
|
});
|
|
promises.push(oscillator.promise);
|
|
oscillators[key] = oscillator;
|
|
}
|
|
let stop;
|
|
const promise = new TickingPromise(
|
|
(resolve) => {
|
|
stop = resolve;
|
|
Promise.all(promises).then(resolve);
|
|
},
|
|
(elapsed) => {
|
|
for (const key in oscillators) {
|
|
const oscillator = oscillators[key];
|
|
oscillator.elapsed += elapsed;
|
|
if (oscillator.elapsed >= oscillator.frequency) {
|
|
if (0 === --oscillator.count) {
|
|
oscillator.stop();
|
|
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};
|
|
}
|