mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-21 18:48:36 +00:00
fixed all the things
This commit is contained in:
parent
a2974099c1
commit
2ee392be9b
@ -86,7 +86,7 @@ export const midi2note = (n) => {
|
|||||||
// modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does)
|
// modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does)
|
||||||
export const _mod = (n, m) => ((n % m) + m) % m;
|
export const _mod = (n, m) => ((n % m) + m) % m;
|
||||||
|
|
||||||
export function nanFallback(value, fallback) {
|
export function nanFallback(value, fallback = 0) {
|
||||||
if (isNaN(Number(value))) {
|
if (isNaN(Number(value))) {
|
||||||
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
||||||
return fallback;
|
return fallback;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel.cycles/core';
|
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel.cycles/core';
|
||||||
import { getAudioContext, registerSound, getEnvelope, getADSRValues } from '@strudel.cycles/webaudio';
|
import { getAudioContext, registerSound, getParamADSR, getADSRValues } from '@strudel.cycles/webaudio';
|
||||||
import gm from './gm.mjs';
|
import gm from './gm.mjs';
|
||||||
|
|
||||||
let loadCache = {};
|
let loadCache = {};
|
||||||
@ -136,23 +136,27 @@ export function registerSoundfonts() {
|
|||||||
value.sustain,
|
value.sustain,
|
||||||
value.release,
|
value.release,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const { duration } = value;
|
||||||
const n = getSoundIndex(value.n, fonts.length);
|
const n = getSoundIndex(value.n, fonts.length);
|
||||||
const font = fonts[n];
|
const font = fonts[n];
|
||||||
const ctx = getAudioContext();
|
const ctx = getAudioContext();
|
||||||
const bufferSource = await getFontBufferSource(font, value, ctx);
|
const bufferSource = await getFontBufferSource(font, value, ctx);
|
||||||
bufferSource.start(time);
|
bufferSource.start(time);
|
||||||
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 0.3, time);
|
const envGain = ctx.createGain();
|
||||||
bufferSource.connect(envelope);
|
const node = bufferSource.connect(envGain);
|
||||||
const stop = (releaseTime) => {
|
const holdEnd = time + duration;
|
||||||
const silentAt = releaseEnvelope(releaseTime);
|
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, time, holdEnd, 'linear');
|
||||||
bufferSource.stop(silentAt);
|
let envEnd = holdEnd + release + 0.01;
|
||||||
};
|
|
||||||
|
bufferSource.stop(envEnd);
|
||||||
|
const stop = (releaseTime) => {};
|
||||||
bufferSource.onended = () => {
|
bufferSource.onended = () => {
|
||||||
bufferSource.disconnect();
|
bufferSource.disconnect();
|
||||||
envelope.disconnect();
|
node.disconnect();
|
||||||
onended();
|
onended();
|
||||||
};
|
};
|
||||||
return { node: envelope, stop };
|
return { node, stop };
|
||||||
},
|
},
|
||||||
{ type: 'soundfont', prebake: true, fonts },
|
{ type: 'soundfont', prebake: true, fonts },
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,33 +1,5 @@
|
|||||||
import { getAudioContext } from './superdough.mjs';
|
import { getAudioContext } from './superdough.mjs';
|
||||||
import { clamp } from './util.mjs';
|
import { clamp, nanFallback } from './util.mjs';
|
||||||
|
|
||||||
const setRelease = (param, phase, sustain, startTime, endTime, endValue, curve = 'linear') => {
|
|
||||||
const ctx = getAudioContext();
|
|
||||||
const ramp = curve === 'exponential' ? 'exponentialRampToValueAtTime' : 'linearRampToValueAtTime';
|
|
||||||
// if the decay stage is complete before the note event is done, we don't need to do anything special
|
|
||||||
if (phase < startTime) {
|
|
||||||
param.setValueAtTime(sustain, startTime);
|
|
||||||
param[ramp](endValue, endTime);
|
|
||||||
} else if (param.cancelAndHoldAtTime == null) {
|
|
||||||
//this replicates cancelAndHoldAtTime behavior for Firefox
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
//sustain at current value
|
|
||||||
const currValue = param.value;
|
|
||||||
param.cancelScheduledValues(0);
|
|
||||||
param.setValueAtTime(currValue, 0);
|
|
||||||
|
|
||||||
//release
|
|
||||||
param[ramp](endValue, endTime);
|
|
||||||
},
|
|
||||||
(startTime - ctx.currentTime) * 1000,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
//stop the envelope, hold the value, and then set the release stage
|
|
||||||
param.cancelAndHoldAtTime(startTime);
|
|
||||||
param[ramp](endValue, endTime);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export function gainNode(value) {
|
export function gainNode(value) {
|
||||||
const node = getAudioContext().createGain();
|
const node = getAudioContext().createGain();
|
||||||
@ -35,44 +7,13 @@ export function gainNode(value) {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
// alternative to getADSR returning the gain node and a stop handle to trigger the release anytime in the future
|
const getSlope = (y1, y2, x1, x2) => {
|
||||||
export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => {
|
const denom = x2 - x1;
|
||||||
const gainNode = getAudioContext().createGain();
|
if (denom === 0) {
|
||||||
let phase = begin;
|
return 0;
|
||||||
gainNode.gain.setValueAtTime(0, begin);
|
}
|
||||||
phase += attack;
|
return (y2 - y1) / (x2 - x1);
|
||||||
gainNode.gain.linearRampToValueAtTime(velocity, phase); // attack
|
|
||||||
phase += decay;
|
|
||||||
let sustainLevel = sustain * velocity;
|
|
||||||
gainNode.gain.linearRampToValueAtTime(sustainLevel, phase); // decay / sustain
|
|
||||||
// sustain end
|
|
||||||
return {
|
|
||||||
node: gainNode,
|
|
||||||
stop: (t) => {
|
|
||||||
const endTime = t + release;
|
|
||||||
setRelease(gainNode.gain, phase, sustainLevel, t, endTime, 0);
|
|
||||||
// helps prevent pops from overlapping sounds
|
|
||||||
return endTime;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getExpEnvelope = (attack, decay, sustain, release, velocity, begin) => {
|
|
||||||
sustain = Math.max(0.001, sustain);
|
|
||||||
velocity = Math.max(0.001, velocity);
|
|
||||||
const gainNode = getAudioContext().createGain();
|
|
||||||
gainNode.gain.setValueAtTime(0.0001, begin);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(velocity, begin + attack);
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(sustain * velocity, begin + attack + decay);
|
|
||||||
return {
|
|
||||||
node: gainNode,
|
|
||||||
stop: (t) => {
|
|
||||||
// similar to getEnvelope, this will glitch if sustain level has not been reached
|
|
||||||
gainNode.gain.exponentialRampToValueAtTime(0.0001, t + release);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getParamADSR = (
|
export const getParamADSR = (
|
||||||
param,
|
param,
|
||||||
attack,
|
attack,
|
||||||
@ -86,23 +27,47 @@ export const getParamADSR = (
|
|||||||
//exponential works better for frequency modulations (such as filter cutoff) due to human ear perception
|
//exponential works better for frequency modulations (such as filter cutoff) due to human ear perception
|
||||||
curve = 'exponential',
|
curve = 'exponential',
|
||||||
) => {
|
) => {
|
||||||
|
attack = nanFallback(attack);
|
||||||
|
decay = nanFallback(decay);
|
||||||
|
sustain = nanFallback(sustain);
|
||||||
|
release = nanFallback(release);
|
||||||
|
|
||||||
const ramp = curve === 'exponential' ? 'exponentialRampToValueAtTime' : 'linearRampToValueAtTime';
|
const ramp = curve === 'exponential' ? 'exponentialRampToValueAtTime' : 'linearRampToValueAtTime';
|
||||||
let phase = begin;
|
if (curve === 'exponential') {
|
||||||
|
min = Math.max(0.0001, min);
|
||||||
|
}
|
||||||
const range = max - min;
|
const range = max - min;
|
||||||
const peak = min + range;
|
const peak = min + range;
|
||||||
|
const sustainVal = min + sustain * range;
|
||||||
|
const duration = end - begin;
|
||||||
|
|
||||||
|
const envValAtTime = (time) => {
|
||||||
|
if (attack > time) {
|
||||||
|
return time * getSlope(min, peak, 0, attack) + 0;
|
||||||
|
} else {
|
||||||
|
return (time - attack) * getSlope(peak, sustainVal, 0, decay) + peak;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
param.setValueAtTime(min, begin);
|
param.setValueAtTime(min, begin);
|
||||||
phase += attack;
|
if (attack > duration) {
|
||||||
|
//attack
|
||||||
//attack
|
param[ramp](envValAtTime(duration), end);
|
||||||
param[ramp](peak, phase);
|
} else if (attack + decay > duration) {
|
||||||
phase += decay;
|
//attack
|
||||||
const sustainLevel = min + sustain * range;
|
param[ramp](envValAtTime(attack), begin + attack);
|
||||||
|
//decay
|
||||||
//decay
|
param[ramp](envValAtTime(duration), end);
|
||||||
param[ramp](sustainLevel, phase);
|
} else {
|
||||||
|
//attack
|
||||||
setRelease(param, phase, sustainLevel, end, end + release, min, curve);
|
param[ramp](envValAtTime(attack), begin + attack);
|
||||||
|
//decay
|
||||||
|
param[ramp](envValAtTime(attack + decay), begin + attack + decay);
|
||||||
|
//sustain
|
||||||
|
param.setValueAtTime(sustainVal, end);
|
||||||
|
}
|
||||||
|
//release
|
||||||
|
param[ramp](min, end + release);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getCompressor(ac, threshold, ratio, knee, attack, release) {
|
export function getCompressor(ac, threshold, ratio, knee, attack, release) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { noteToMidi, valueToMidi, getSoundIndex } from './util.mjs';
|
import { noteToMidi, valueToMidi, getSoundIndex } from './util.mjs';
|
||||||
import { getAudioContext, registerSound } from './index.mjs';
|
import { getAudioContext, registerSound } from './index.mjs';
|
||||||
import { getADSRValues, getEnvelope } from './helpers.mjs';
|
import { getADSRValues, getParamADSR } from './helpers.mjs';
|
||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
|
|
||||||
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
const bufferCache = {}; // string: Promise<ArrayBuffer>
|
||||||
@ -243,6 +243,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
|||||||
begin = 0,
|
begin = 0,
|
||||||
loopEnd = 1,
|
loopEnd = 1,
|
||||||
end = 1,
|
end = 1,
|
||||||
|
duration,
|
||||||
vib,
|
vib,
|
||||||
vibmod = 0.5,
|
vibmod = 0.5,
|
||||||
} = value;
|
} = value;
|
||||||
@ -255,7 +256,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
|||||||
const ac = getAudioContext();
|
const ac = getAudioContext();
|
||||||
// destructure adsr here, because the default should be different for synths and samples
|
// destructure adsr here, because the default should be different for synths and samples
|
||||||
|
|
||||||
const [attack, decay, sustain, release] = getADSRValues([value.attack, value.decay, value.sustain, value.release]);
|
let [attack, decay, sustain, release] = getADSRValues([value.attack, value.decay, value.sustain, value.release]);
|
||||||
//const soundfont = getSoundfontKey(s);
|
//const soundfont = getSoundfontKey(s);
|
||||||
const time = t + nudge;
|
const time = t + nudge;
|
||||||
|
|
||||||
@ -299,25 +300,29 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
|||||||
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
|
bufferSource.loopEnd = loopEnd * bufferSource.buffer.duration - offset;
|
||||||
}
|
}
|
||||||
bufferSource.start(time, offset);
|
bufferSource.start(time, offset);
|
||||||
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
|
const envGain = ac.createGain();
|
||||||
bufferSource.connect(envelope);
|
const node = bufferSource.connect(envGain);
|
||||||
|
const holdEnd = t + duration;
|
||||||
|
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
|
||||||
|
|
||||||
const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox...
|
const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox...
|
||||||
envelope.connect(out);
|
node.connect(out);
|
||||||
bufferSource.onended = function () {
|
bufferSource.onended = function () {
|
||||||
bufferSource.disconnect();
|
bufferSource.disconnect();
|
||||||
vibratoOscillator?.stop();
|
vibratoOscillator?.stop();
|
||||||
envelope.disconnect();
|
node.disconnect();
|
||||||
out.disconnect();
|
out.disconnect();
|
||||||
onended();
|
onended();
|
||||||
};
|
};
|
||||||
|
let envEnd = holdEnd + release + 0.01;
|
||||||
|
bufferSource.stop(envEnd);
|
||||||
const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => {
|
const stop = (endTime, playWholeBuffer = clip === undefined && loop === undefined) => {
|
||||||
let releaseTime = endTime;
|
// did not reimplement this behavior, because it mostly seems to confuse people...
|
||||||
if (playWholeBuffer) {
|
// if (playWholeBuffer) {
|
||||||
const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
// const bufferDuration = bufferSource.buffer.duration / bufferSource.playbackRate.value;
|
||||||
releaseTime = t + (end - begin) * bufferDuration;
|
// envEnd = t + (end - begin) * bufferDuration;
|
||||||
}
|
// }
|
||||||
const silentAt = releaseEnvelope(releaseTime);
|
// bufferSource.stop(envEnd);
|
||||||
bufferSource.stop(silentAt);
|
|
||||||
};
|
};
|
||||||
const handle = { node: out, bufferSource, stop };
|
const handle = { node: out, bufferSource, stop };
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { midiToFreq, noteToMidi } from './util.mjs';
|
import { midiToFreq, noteToMidi } from './util.mjs';
|
||||||
import { registerSound, getAudioContext } from './superdough.mjs';
|
import { registerSound, getAudioContext } from './superdough.mjs';
|
||||||
import { gainNode, getADSRValues, getEnvelope, getExpEnvelope } from './helpers.mjs';
|
import { gainNode, getADSRValues, getParamADSR } from './helpers.mjs';
|
||||||
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
||||||
|
|
||||||
const mod = (freq, range = 1, type = 'sine') => {
|
const mod = (freq, range = 1, type = 'sine') => {
|
||||||
@ -43,26 +43,30 @@ export function registerSynthSounds() {
|
|||||||
let { density } = value;
|
let { density } = value;
|
||||||
sound = getNoiseOscillator(s, t, density);
|
sound = getNoiseOscillator(s, t, density);
|
||||||
}
|
}
|
||||||
|
|
||||||
let { node: o, stop, triggerRelease } = sound;
|
let { node: o, stop, triggerRelease } = sound;
|
||||||
|
|
||||||
// turn down
|
// turn down
|
||||||
const g = gainNode(0.3);
|
const g = gainNode(0.3);
|
||||||
|
|
||||||
// gain envelope
|
const { duration } = value;
|
||||||
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
|
|
||||||
|
|
||||||
o.onended = () => {
|
o.onended = () => {
|
||||||
o.disconnect();
|
o.disconnect();
|
||||||
g.disconnect();
|
g.disconnect();
|
||||||
onended();
|
onended();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const envGain = gainNode(1);
|
||||||
|
let node = o.connect(g).connect(envGain);
|
||||||
|
const holdEnd = t + duration;
|
||||||
|
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
|
||||||
|
const envEnd = holdEnd + release + 0.01;
|
||||||
|
triggerRelease?.(envEnd);
|
||||||
|
stop(envEnd);
|
||||||
return {
|
return {
|
||||||
node: o.connect(g).connect(envelope),
|
node,
|
||||||
stop: (releaseTime) => {
|
stop: (releaseTime) => {},
|
||||||
const silentAt = releaseEnvelope(releaseTime);
|
|
||||||
triggerRelease?.(releaseTime);
|
|
||||||
stop(silentAt);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ type: 'synth', prebake: true },
|
{ type: 'synth', prebake: true },
|
||||||
@ -122,6 +126,7 @@ export function getOscillator(
|
|||||||
fmrelease: fmRelease,
|
fmrelease: fmRelease,
|
||||||
fmvelocity: fmVelocity,
|
fmvelocity: fmVelocity,
|
||||||
fmwave: fmWaveform = 'sine',
|
fmwave: fmWaveform = 'sine',
|
||||||
|
duration,
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
let ac = getAudioContext();
|
let ac = getAudioContext();
|
||||||
@ -151,26 +156,38 @@ export function getOscillator(
|
|||||||
o.start(t);
|
o.start(t);
|
||||||
|
|
||||||
// FM
|
// FM
|
||||||
let stopFm, fmEnvelope;
|
let stopFm;
|
||||||
|
let envGain = ac.createGain();
|
||||||
if (fmModulationIndex) {
|
if (fmModulationIndex) {
|
||||||
const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform);
|
const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform);
|
||||||
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) {
|
||||||
// no envelope by default
|
// no envelope by default
|
||||||
modulator.connect(o.frequency);
|
modulator.connect(o.frequency);
|
||||||
} else {
|
} else {
|
||||||
fmAttack = fmAttack ?? 0.001;
|
const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]);
|
||||||
fmDecay = fmDecay ?? 0.001;
|
|
||||||
fmSustain = fmSustain ?? 1;
|
const holdEnd = t + duration;
|
||||||
fmRelease = fmRelease ?? 0.001;
|
// let envEnd = holdEnd + release + 0.01;
|
||||||
fmVelocity = fmVelocity ?? 1;
|
|
||||||
fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t);
|
getParamADSR(
|
||||||
|
envGain.gain,
|
||||||
|
attack,
|
||||||
|
decay,
|
||||||
|
sustain,
|
||||||
|
release,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
t,
|
||||||
|
holdEnd,
|
||||||
|
fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
|
||||||
|
);
|
||||||
|
|
||||||
if (fmEnvelopeType === 'exp') {
|
if (fmEnvelopeType === 'exp') {
|
||||||
fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t);
|
envGain.maxValue = fmModulationIndex * 2;
|
||||||
fmEnvelope.node.maxValue = fmModulationIndex * 2;
|
envGain.minValue = 0.00001;
|
||||||
fmEnvelope.node.minValue = 0.00001;
|
|
||||||
}
|
}
|
||||||
modulator.connect(fmEnvelope.node);
|
modulator.connect(envGain);
|
||||||
fmEnvelope.node.connect(o.frequency);
|
envGain.connect(o.frequency);
|
||||||
}
|
}
|
||||||
stopFm = stop;
|
stopFm = stop;
|
||||||
}
|
}
|
||||||
@ -202,7 +219,7 @@ export function getOscillator(
|
|||||||
o.stop(time);
|
o.stop(time);
|
||||||
},
|
},
|
||||||
triggerRelease: (time) => {
|
triggerRelease: (time) => {
|
||||||
fmEnvelope?.stop(time);
|
// envGain?.stop(time);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export const valueToMidi = (value, fallbackValue) => {
|
|||||||
return fallbackValue;
|
return fallbackValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function nanFallback(value, fallback) {
|
export function nanFallback(value, fallback = 0) {
|
||||||
if (isNaN(Number(value))) {
|
if (isNaN(Number(value))) {
|
||||||
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
|
||||||
return fallback;
|
return fallback;
|
||||||
|
|||||||
@ -57,32 +57,36 @@ export function SoundsTab() {
|
|||||||
<ImportSoundsButton onComplete={() => settingsMap.setKey('soundsFilter', 'user')} />
|
<ImportSoundsButton onComplete={() => settingsMap.setKey('soundsFilter', 'user')} />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal">
|
<div className="min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal">
|
||||||
{soundEntries.map(([name, { data, onTrigger }]) => (
|
{soundEntries.map(([name, { data, onTrigger }]) => {
|
||||||
<span
|
return (
|
||||||
key={name}
|
<span
|
||||||
className="cursor-pointer hover:opacity-50"
|
key={name}
|
||||||
onMouseDown={async () => {
|
className="cursor-pointer hover:opacity-50"
|
||||||
const ctx = getAudioContext();
|
onMouseDown={async () => {
|
||||||
const params = {
|
const ctx = getAudioContext();
|
||||||
note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined,
|
const params = {
|
||||||
s: name,
|
note: ['synth', 'soundfont'].includes(data.type) ? 'a3' : undefined,
|
||||||
clip: 1,
|
s: name,
|
||||||
release: 0.5,
|
clip: 1,
|
||||||
};
|
release: 0.5,
|
||||||
const time = ctx.currentTime + 0.05;
|
sustain: 1,
|
||||||
const onended = () => trigRef.current?.node?.disconnect();
|
duration: 0.5,
|
||||||
trigRef.current = Promise.resolve(onTrigger(time, params, onended));
|
};
|
||||||
trigRef.current.then((ref) => {
|
const time = ctx.currentTime + 0.05;
|
||||||
connectToDestination(ref?.node);
|
const onended = () => trigRef.current?.node?.disconnect();
|
||||||
});
|
trigRef.current = Promise.resolve(onTrigger(time, params, onended));
|
||||||
}}
|
trigRef.current.then((ref) => {
|
||||||
>
|
connectToDestination(ref?.node);
|
||||||
{' '}
|
});
|
||||||
{name}
|
}}
|
||||||
{data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''}
|
>
|
||||||
{data?.type === 'soundfont' ? `(${data.fonts.length})` : ''}
|
{' '}
|
||||||
</span>
|
{name}
|
||||||
))}
|
{data?.type === 'sample' ? `(${getSamples(data.samples)})` : ''}
|
||||||
|
{data?.type === 'soundfont' ? `(${data.fonts.length})` : ''}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''}
|
{!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user