113 lines
4.0 KiB
JavaScript
113 lines
4.0 KiB
JavaScript
// node_modules/zzfx/ZzFX.js
|
|
function zzfx(...parameters) {
|
|
return ZZFX.play(...parameters);
|
|
}
|
|
var ZZFX = {
|
|
// master volume scale
|
|
volume: 0.3,
|
|
// sample rate for audio
|
|
sampleRate: 44100,
|
|
// create shared audio context
|
|
x: new AudioContext(),
|
|
// play a sound from zzfx paramerters
|
|
play: function(...parameters) {
|
|
return this.playSamples(this.buildSamples(...parameters));
|
|
},
|
|
// play an array of samples
|
|
playSamples: function(...samples) {
|
|
const buffer = this.x.createBuffer(samples.length, samples[0].length, this.sampleRate), source = this.x.createBufferSource();
|
|
samples.map((d, i) => buffer.getChannelData(i).set(d));
|
|
source.buffer = buffer;
|
|
source.connect(this.x.destination);
|
|
source.start();
|
|
return source;
|
|
},
|
|
// build an array of samples
|
|
buildSamples: function(volume = 1, randomness = 0.05, frequency = 220, attack = 0, sustain = 0, release = 0.1, shape = 0, shapeCurve = 1, slide = 0, deltaSlide = 0, pitchJump = 0, pitchJumpTime = 0, repeatTime = 0, noise = 0, modulation = 0, bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0) {
|
|
let PI2 = Math.PI * 2, sampleRate = this.sampleRate, sign = (v) => v > 0 ? 1 : -1, startSlide = slide *= 500 * PI2 / sampleRate / sampleRate, startFrequency = frequency *= (1 + randomness * 2 * Math.random() - randomness) * PI2 / sampleRate, b = [], t = 0, tm = 0, i = 0, j = 1, r = 0, c = 0, s = 0, f, length;
|
|
attack = attack * sampleRate + 9;
|
|
decay *= sampleRate;
|
|
sustain *= sampleRate;
|
|
release *= sampleRate;
|
|
delay *= sampleRate;
|
|
deltaSlide *= 500 * PI2 / sampleRate ** 3;
|
|
modulation *= PI2 / sampleRate;
|
|
pitchJump *= PI2 / sampleRate;
|
|
pitchJumpTime *= sampleRate;
|
|
repeatTime = repeatTime * sampleRate | 0;
|
|
for (length = attack + decay + sustain + release + delay | 0; i < length; b[i++] = s) {
|
|
if (!(++c % (bitCrush * 100 | 0))) {
|
|
s = shape ? shape > 1 ? shape > 2 ? shape > 3 ? (
|
|
// wave shape
|
|
Math.sin((t % PI2) ** 3)
|
|
) : (
|
|
// 4 noise
|
|
Math.max(Math.min(Math.tan(t), 1), -1)
|
|
) : (
|
|
// 3 tan
|
|
1 - (2 * t / PI2 % 2 + 2) % 2
|
|
) : (
|
|
// 2 saw
|
|
1 - 4 * Math.abs(Math.round(t / PI2) - t / PI2)
|
|
) : (
|
|
// 1 triangle
|
|
Math.sin(t)
|
|
);
|
|
s = (repeatTime ? 1 - tremolo + tremolo * Math.sin(PI2 * i / repeatTime) : 1) * sign(s) * Math.abs(s) ** shapeCurve * // curve 0=square, 2=pointy
|
|
volume * this.volume * // envelope
|
|
(i < attack ? i / attack : (
|
|
// attack
|
|
i < attack + decay ? (
|
|
// decay
|
|
1 - (i - attack) / decay * (1 - sustainVolume)
|
|
) : (
|
|
// decay falloff
|
|
i < attack + decay + sustain ? (
|
|
// sustain
|
|
sustainVolume
|
|
) : (
|
|
// sustain volume
|
|
i < length - delay ? (
|
|
// release
|
|
(length - i - delay) / release * // release falloff
|
|
sustainVolume
|
|
) : (
|
|
// release volume
|
|
0
|
|
)
|
|
)
|
|
)
|
|
));
|
|
s = delay ? s / 2 + (delay > i ? 0 : (
|
|
// delay
|
|
(i < length - delay ? 1 : (length - i) / delay) * // release delay
|
|
b[i - delay | 0] / 2
|
|
)) : s;
|
|
}
|
|
f = (frequency += slide += deltaSlide) * // frequency
|
|
Math.cos(modulation * tm++);
|
|
t += f - f * noise * (1 - (Math.sin(i) + 1) * 1e9 % 2);
|
|
if (j && ++j > pitchJumpTime) {
|
|
frequency += pitchJump;
|
|
startFrequency += pitchJump;
|
|
j = 0;
|
|
}
|
|
if (repeatTime && !(++r % repeatTime)) {
|
|
frequency = startFrequency;
|
|
slide = startSlide;
|
|
j || (j = 1);
|
|
}
|
|
}
|
|
return b;
|
|
},
|
|
// get frequency of a musical note on a diatonic scale
|
|
getNote: function(semitoneOffset = 0, rootNoteFrequency = 440) {
|
|
return rootNoteFrequency * 2 ** (semitoneOffset / 12);
|
|
}
|
|
};
|
|
export {
|
|
ZZFX,
|
|
zzfx
|
|
};
|
|
//# sourceMappingURL=zzfx.js.map
|