mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-18 00:58:35 +00:00
164 lines
4.6 KiB
JavaScript
164 lines
4.6 KiB
JavaScript
import { getAudioContext } from './superdough.mjs';
|
|
import { ZZFX } from 'zzfx';
|
|
|
|
export function gainNode(value) {
|
|
const node = getAudioContext().createGain();
|
|
node.gain.value = value;
|
|
return node;
|
|
}
|
|
|
|
export const getZZFX = (value, t, duration) => {
|
|
let {
|
|
s,
|
|
note = 36,
|
|
freq,
|
|
//
|
|
randomness = 0,
|
|
attack = 0,
|
|
decay = 0,
|
|
sustain = 0.8,
|
|
release = 0.1,
|
|
shapeCurve = 1,
|
|
slide = 0,
|
|
deltaSlide = 0,
|
|
pitchJump = 0,
|
|
pitchJumpTime = 0,
|
|
repeatTime = 0,
|
|
noise = 0,
|
|
modulation = 0,
|
|
bitCrush = 0,
|
|
delay = 0,
|
|
tremolo = 0,
|
|
} = value;
|
|
if (typeof note === 'string') {
|
|
note = noteToMidi(note); // e.g. c3 => 48
|
|
}
|
|
// get frequency
|
|
if (!freq && typeof n === 'number') {
|
|
freq = midiToFreq(n);
|
|
}
|
|
const shape = ['zsine', 'ztri', 'zsaw', 'ztan', 'znoise'].indexOf(s) || 0;
|
|
|
|
const params = [
|
|
1, // volume
|
|
randomness, // randomness
|
|
freq,
|
|
attack,
|
|
duration, // sustain time
|
|
release,
|
|
shape,
|
|
shapeCurve,
|
|
slide,
|
|
deltaSlide,
|
|
pitchJump,
|
|
pitchJumpTime,
|
|
repeatTime,
|
|
noise,
|
|
modulation,
|
|
bitCrush,
|
|
delay,
|
|
sustain, // sustain volume!
|
|
decay,
|
|
tremolo,
|
|
];
|
|
const paramOrder = [
|
|
'volume',
|
|
'randomness',
|
|
'frequency',
|
|
'attack',
|
|
'sustain',
|
|
'release',
|
|
'shape',
|
|
'shapeCurve',
|
|
'slide',
|
|
'deltaSlide',
|
|
'pitchJump',
|
|
'pitchJumpTime',
|
|
'repeatTime',
|
|
'noise',
|
|
'modulation',
|
|
'bitCrush',
|
|
'delay',
|
|
'sustainVolume',
|
|
'decay',
|
|
'tremolo',
|
|
];
|
|
|
|
const readableParams = Object.fromEntries(paramOrder.map((param, i) => [param, params[i]]));
|
|
console.log(readableParams);
|
|
|
|
const samples = ZZFX.buildSamples(...params);
|
|
const context = getAudioContext();
|
|
const buffer = context.createBuffer(1, samples.length, context.sampleRate);
|
|
buffer.getChannelData(0).set(samples);
|
|
const source = getAudioContext().createBufferSource();
|
|
source.buffer = buffer;
|
|
source.start(t);
|
|
return {
|
|
node: source,
|
|
};
|
|
};
|
|
|
|
export const getOscillator = ({ s, freq, t }) => {
|
|
// make oscillator
|
|
const o = getAudioContext().createOscillator();
|
|
o.type = s || 'triangle';
|
|
o.frequency.value = Number(freq);
|
|
o.start(t);
|
|
//o.stop(t + duration + release);
|
|
const stop = (time) => o.stop(time);
|
|
return { node: o, stop };
|
|
};
|
|
|
|
// alternative to getADSR returning the gain node and a stop handle to trigger the release anytime in the future
|
|
export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => {
|
|
const gainNode = getAudioContext().createGain();
|
|
gainNode.gain.setValueAtTime(0, begin);
|
|
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
|
|
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
|
// sustain end
|
|
return {
|
|
node: gainNode,
|
|
stop: (t) => {
|
|
//if (typeof gainNode.gain.cancelAndHoldAtTime === 'function') {
|
|
// gainNode.gain.cancelAndHoldAtTime(t); // this seems to release instantly....
|
|
// see https://discord.com/channels/779427371270275082/937365093082079272/1086053607360712735
|
|
//} else {
|
|
// firefox: this will glitch when the sustain has not been reached yet at the time of release
|
|
gainNode.gain.setValueAtTime(sustain * velocity, t);
|
|
//}
|
|
gainNode.gain.linearRampToValueAtTime(0, t + release);
|
|
},
|
|
};
|
|
};
|
|
|
|
export const getADSR = (attack, decay, sustain, release, velocity, begin, end) => {
|
|
const gainNode = getAudioContext().createGain();
|
|
gainNode.gain.setValueAtTime(0, begin);
|
|
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
|
|
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
|
gainNode.gain.setValueAtTime(sustain * velocity, end); // sustain end
|
|
gainNode.gain.linearRampToValueAtTime(0, end + release); // release
|
|
// for some reason, using exponential ramping creates little cracklings
|
|
/* let t = begin;
|
|
gainNode.gain.setValueAtTime(0, t);
|
|
gainNode.gain.exponentialRampToValueAtTime(velocity, (t += attack));
|
|
const sustainGain = Math.max(sustain * velocity, 0.001);
|
|
gainNode.gain.exponentialRampToValueAtTime(sustainGain, (t += decay));
|
|
if (end - begin < attack + decay) {
|
|
gainNode.gain.cancelAndHoldAtTime(end);
|
|
} else {
|
|
gainNode.gain.setValueAtTime(sustainGain, end);
|
|
}
|
|
gainNode.gain.exponentialRampToValueAtTime(0.001, end + release); // release */
|
|
return gainNode;
|
|
};
|
|
|
|
export const getFilter = (type, frequency, Q) => {
|
|
const filter = getAudioContext().createBiquadFilter();
|
|
filter.type = type;
|
|
filter.frequency.value = frequency;
|
|
filter.Q.value = Q;
|
|
return filter;
|
|
};
|