.out error handling

This commit is contained in:
Felix Roos 2022-07-29 00:05:20 +02:00
parent 4f460eeb32
commit 712a2ae018

View File

@ -126,146 +126,150 @@ const splitSN = (s, n) => {
Pattern.prototype.out = function () { Pattern.prototype.out = function () {
return this.onTrigger(async (t, hap, ct) => { return this.onTrigger(async (t, hap, ct) => {
const ac = getAudioContext(); try {
// calculate correct time (tone.js workaround) const ac = getAudioContext();
t = ac.currentTime + t - ct; // calculate correct time (tone.js workaround)
// destructure value t = ac.currentTime + t - ct;
let { // destructure value
freq, let {
s, freq,
sf, s,
clip = 0, // if 1, samples will be cut off when the hap ends sf,
n = 0, clip = 0, // if 1, samples will be cut off when the hap ends
note, n = 0,
gain = 1, note,
cutoff, gain = 1,
resonance = 1, cutoff,
hcutoff, resonance = 1,
hresonance = 1, hcutoff,
bandf, hresonance = 1,
bandq = 1, bandf,
pan, bandq = 1,
attack = 0.001, pan,
decay = 0.05, attack = 0.001,
sustain = 0.5, decay = 0.05,
release = 0.001, sustain = 0.5,
speed = 1, // sample playback speed release = 0.001,
begin = 0, speed = 1, // sample playback speed
end = 1, begin = 0,
} = hap.value; end = 1,
const { velocity = 1 } = hap.context; } = hap.value;
gain *= velocity; // legacy fix for velocity const { velocity = 1 } = hap.context;
// the chain will hold all audio nodes that connect to each other gain *= velocity; // legacy fix for velocity
const chain = []; // the chain will hold all audio nodes that connect to each other
if (typeof s === 'string') { const chain = [];
[s, n] = splitSN(s, n); if (typeof s === 'string') {
} [s, n] = splitSN(s, n);
if (typeof note === 'string') {
[note, n] = splitSN(note, n);
}
if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
// with synths, n and note are the same thing
n = note || n;
if (typeof n === 'string') {
n = toMidi(n); // e.g. c3 => 48
} }
// get frequency if (typeof note === 'string') {
if (!freq && typeof n === 'number') { [note, n] = splitSN(note, n);
freq = fromMidi(n); // + 48);
} }
// make oscillator if (!s || ['sine', 'square', 'triangle', 'sawtooth'].includes(s)) {
const o = getOscillator({ t, s, freq, duration: hap.duration, release }); // with synths, n and note are the same thing
chain.push(o); n = note || n;
// level down oscillators as they are really loud compared to samples i've tested if (typeof n === 'string') {
const g = ac.createGain(); n = toMidi(n); // e.g. c3 => 48
g.gain.value = 0.3;
chain.push(g);
// TODO: make adsr work with samples without pops
// envelope
const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hap.duration);
chain.push(adsr);
} else {
// load sample
if (speed === 0) {
// no playback
return;
}
if (!s) {
console.warn('no sample specified');
return;
}
const soundfont = getSoundfontKey(s);
let bufferSource;
try {
if (soundfont) {
// is soundfont
bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac);
} else {
// is sample from loaded samples(..)
bufferSource = await getSampleBufferSource(s, n, note);
} }
} catch (err) { // get frequency
console.warn(err); if (!freq && typeof n === 'number') {
return; freq = fromMidi(n); // + 48);
} }
// asny stuff above took too long? // make oscillator
if (ac.currentTime > t) { const o = getOscillator({ t, s, freq, duration: hap.duration, release });
console.warn('sample still loading:', s, n); chain.push(o);
return; // level down oscillators as they are really loud compared to samples i've tested
} const g = ac.createGain();
if (!bufferSource) { g.gain.value = 0.3;
console.warn('no buffer source'); chain.push(g);
return; // TODO: make adsr work with samples without pops
} // envelope
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value; const adsr = getADSR(attack, decay, sustain, release, 1, t, t + hap.duration);
// TODO: nudge, unit, cut, loop chain.push(adsr);
let duration = soundfont || clip ? hap.duration : bufferSource.buffer.duration;
// let duration = bufferSource.buffer.duration;
const offset = begin * duration;
duration = ((end - begin) * duration) / Math.abs(speed);
if (soundfont || clip) {
bufferSource.start(t, offset); // duration does not work here for some reason
} else { } else {
bufferSource.start(t, offset, duration); // load sample
} if (speed === 0) {
chain.push(bufferSource); // no playback
if (soundfont || clip) { return;
const env = ac.createGain(); }
const releaseLength = 0.1; if (!s) {
env.gain.value = 0.6; console.warn('no sample specified');
env.gain.setValueAtTime(env.gain.value, t + duration); return;
env.gain.linearRampToValueAtTime(0, t + duration + releaseLength); }
// env.gain.linearRampToValueAtTime(0, t + duration + releaseLength); const soundfont = getSoundfontKey(s);
chain.push(env); let bufferSource;
bufferSource.stop(t + duration + releaseLength);
} else {
bufferSource.stop(t + duration);
}
}
// master out
const master = ac.createGain();
master.gain.value = gain;
chain.push(master);
// filters try {
cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance)); if (soundfont) {
hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance)); // is soundfont
bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq)); bufferSource = await globalThis.getFontBufferSource(soundfont, note || n, ac);
// TODO vowel } else {
// TODO delay / delaytime / delayfeedback // is sample from loaded samples(..)
// panning bufferSource = await getSampleBufferSource(s, n, note);
if (pan !== undefined) { }
const panner = ac.createStereoPanner(); } catch (err) {
panner.pan.value = 2 * pan - 1; console.warn(err);
chain.push(panner); return;
} }
// master out // asny stuff above took too long?
/* const master = ac.createGain(); if (ac.currentTime > t) {
console.warn('sample still loading:', s, n);
return;
}
if (!bufferSource) {
console.warn('no buffer source');
return;
}
bufferSource.playbackRate.value = Math.abs(speed) * bufferSource.playbackRate.value;
// TODO: nudge, unit, cut, loop
let duration = soundfont || clip ? hap.duration : bufferSource.buffer.duration;
// let duration = bufferSource.buffer.duration;
const offset = begin * duration;
duration = ((end - begin) * duration) / Math.abs(speed);
if (soundfont || clip) {
bufferSource.start(t, offset); // duration does not work here for some reason
} else {
bufferSource.start(t, offset, duration);
}
chain.push(bufferSource);
if (soundfont || clip) {
const env = ac.createGain();
const releaseLength = 0.1;
env.gain.value = 0.6;
env.gain.setValueAtTime(env.gain.value, t + duration);
env.gain.linearRampToValueAtTime(0, t + duration + releaseLength);
// env.gain.linearRampToValueAtTime(0, t + duration + releaseLength);
chain.push(env);
bufferSource.stop(t + duration + releaseLength);
} else {
bufferSource.stop(t + duration);
}
}
// master out
const master = ac.createGain();
master.gain.value = gain;
chain.push(master);
// filters
cutoff !== undefined && chain.push(getFilter('lowpass', cutoff, resonance));
hcutoff !== undefined && chain.push(getFilter('highpass', hcutoff, hresonance));
bandf !== undefined && chain.push(getFilter('bandpass', bandf, bandq));
// TODO vowel
// TODO delay / delaytime / delayfeedback
// panning
if (pan !== undefined) {
const panner = ac.createStereoPanner();
panner.pan.value = 2 * pan - 1;
chain.push(panner);
}
// master out
/* const master = ac.createGain();
master.gain.value = 0.8 * gain; master.gain.value = 0.8 * gain;
chain.push(master); */ chain.push(master); */
chain.push(ac.destination); chain.push(ac.destination);
// connect chain elements together // connect chain elements together
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
} catch (e) {
console.warn('.out error:', e);
}
}); });
}; };