Mostly working version
This commit is contained in:
parent
0dbca05de0
commit
19b710c401
9 changed files with 56 additions and 70 deletions
|
|
@ -2320,7 +2320,6 @@ export const { curve } = registerControl('curve');
|
|||
export const { deltaSlide } = registerControl('deltaSlide');
|
||||
export const { pitchJump } = registerControl('pitchJump');
|
||||
export const { pitchJumpTime } = registerControl('pitchJumpTime');
|
||||
export const { lfo, repeatTime } = registerControl('lfo', 'repeatTime');
|
||||
// noise on the frequency or as bubo calls it "frequency fog" :)
|
||||
export const { znoise } = registerControl('znoise');
|
||||
export const { zmod } = registerControl('zmod');
|
||||
|
|
|
|||
|
|
@ -3710,7 +3710,7 @@ addConfigAlias('lfo', 'target', 't');
|
|||
addConfigAlias('lfo', 'param', 'p');
|
||||
addConfigAlias('lfo', 'rate', 'r');
|
||||
addConfigAlias('lfo', 'depth', 'dep', 'dp', 'd');
|
||||
addConfigAlias('lfo', 'dc');
|
||||
addConfigAlias('lfo', 'dcoffset', 'dc');
|
||||
addConfigAlias('lfo', 'shape', 'sh');
|
||||
addConfigAlias('lfo', 'skew', 'sk');
|
||||
addConfigAlias('lfo', 'curve', 'c');
|
||||
|
|
@ -3730,7 +3730,7 @@ addConfigAlias('send', 'depth', 'dep', 'dp', 'd');
|
|||
addConfigAlias('send', 'dc');
|
||||
addConfigAlias('send', 'offset', 'off', 'o');
|
||||
|
||||
Pattern.prototype.mod = function (type, config, idx) {
|
||||
Pattern.prototype.modulate = function (type, config, idx) {
|
||||
if (config == null || typeof config !== 'object') {
|
||||
return this;
|
||||
}
|
||||
|
|
@ -3766,7 +3766,7 @@ Pattern.prototype.mod = function (type, config, idx) {
|
|||
* @param {string | Pattern} [config.target] Node (and parameter if specified like `lpf.frequency`) to modulate. Aliases: target, t
|
||||
* @param {number | Pattern} [config.rate] Modulation rate. Aliases: rate, r
|
||||
* @param {number | Pattern} [config.depth] Modulation depth. Aliases: dep, dp, d
|
||||
* @param {number | Pattern} [config.dc] DC offset / bias for the waveform
|
||||
* @param {number | Pattern} [config.dcoffset] DC offset / bias for the waveform. Aliases: dc
|
||||
* @param {number | Pattern} [config.shape] Waveform shape index. Aliases: sh
|
||||
* @param {number | Pattern} [config.skew] Waveform skew amount. Aliases: sk
|
||||
* @param {number | Pattern} [config.curve] Exponential curve amount. Aliases: c
|
||||
|
|
@ -3775,7 +3775,7 @@ Pattern.prototype.mod = function (type, config, idx) {
|
|||
* @returns Pattern
|
||||
*/
|
||||
Pattern.prototype.lfo = function (config, idx) {
|
||||
return this.mod('lfo', config, idx);
|
||||
return this.modulate('lfo', config, idx);
|
||||
};
|
||||
export const lfo = (config) => pure({}).lfo(config);
|
||||
|
||||
|
|
@ -3799,7 +3799,7 @@ export const lfo = (config) => pure({}).lfo(config);
|
|||
* @returns Pattern
|
||||
*/
|
||||
Pattern.prototype.env = function (config, idx) {
|
||||
return this.mod('env', config, idx);
|
||||
return this.modulate('env', config, idx);
|
||||
};
|
||||
export const env = (config) => pure({}).env(config);
|
||||
|
||||
|
|
@ -3820,6 +3820,6 @@ export const env = (config) => pure({}).env(config);
|
|||
* @returns Pattern
|
||||
*/
|
||||
Pattern.prototype.send = function (config, idx) {
|
||||
return this.mod('send', config, idx);
|
||||
return this.modulate('send', config, idx);
|
||||
};
|
||||
export const send = (config) => pure({}).send(config);
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ export function registerSoundfonts() {
|
|||
node.disconnect();
|
||||
onended();
|
||||
};
|
||||
return { node, stop };
|
||||
return { node, stop, source: bufferSource };
|
||||
},
|
||||
{ type: 'soundfont', prebake: true, fonts },
|
||||
);
|
||||
|
|
|
|||
|
|
@ -105,22 +105,16 @@ function getModulationShapeInput(val) {
|
|||
return { tri: 0, triangle: 0, sine: 1, ramp: 2, saw: 3, square: 4 }[val] ?? 0;
|
||||
}
|
||||
|
||||
export function getLfo(audioContext, begin, end, properties = {}) {
|
||||
const { shape = 0, ...props } = properties;
|
||||
const { dcoffset = -0.5, depth = 1 } = properties;
|
||||
export function getEnvelope(audioContext, properties = {}) {
|
||||
return getWorklet(audioContext, 'envelope-processor', properties);
|
||||
}
|
||||
|
||||
export function getLfo(audioContext, properties = {}) {
|
||||
// Extract some params we need for deriving other params
|
||||
const { begin, shape = 0, ...props } = properties;
|
||||
const lfoprops = {
|
||||
frequency: 1,
|
||||
depth,
|
||||
skew: 0.5,
|
||||
phaseoffset: 0,
|
||||
time: begin,
|
||||
begin,
|
||||
end,
|
||||
shape: getModulationShapeInput(shape),
|
||||
dcoffset,
|
||||
min: dcoffset * depth,
|
||||
max: dcoffset * depth + depth,
|
||||
curve: 1,
|
||||
...props,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
|
|||
const stop = (endTime) => {
|
||||
bufferSource.stop(endTime);
|
||||
};
|
||||
const handle = { node: out, bufferSource, stop };
|
||||
const handle = { node: out, source: bufferSource, stop };
|
||||
|
||||
// cut groups
|
||||
if (cut !== undefined) {
|
||||
|
|
|
|||
|
|
@ -16,13 +16,14 @@ import {
|
|||
getADSRValues,
|
||||
getCompressor,
|
||||
getDistortion,
|
||||
getEnvelope,
|
||||
getLfo,
|
||||
getParamADSR,
|
||||
getWorklet,
|
||||
webAudioTimeout,
|
||||
} from './helpers.mjs';
|
||||
import { map } from 'nanostores';
|
||||
import { logger } from './logger.mjs';
|
||||
import { errorLogger, logger } from './logger.mjs';
|
||||
import { loadBuffer } from './sampler.mjs';
|
||||
import { getAudioContext } from './audioContext.mjs';
|
||||
import { SuperdoughAudioController } from './superdoughoutput.mjs';
|
||||
|
|
@ -292,18 +293,6 @@ function getSuperdoughAudioController() {
|
|||
return controller;
|
||||
}
|
||||
|
||||
export function getLfo(audioContext, properties = {}) {
|
||||
// Extract some params we need for deriving other params
|
||||
const { begin, shape = 0, ...props } = properties;
|
||||
const lfoprops = {
|
||||
time: begin,
|
||||
shape: getModulationShapeInput(shape),
|
||||
...props,
|
||||
};
|
||||
|
||||
return getWorklet(audioContext, 'lfo-processor', lfoprops);
|
||||
}
|
||||
|
||||
export function connectToDestination(input, channels) {
|
||||
const controller = getSuperdoughAudioController();
|
||||
controller.output.connectToDestination(input, channels);
|
||||
|
|
@ -445,7 +434,7 @@ function _getTargetParams(nodes, target) {
|
|||
if (!targetNodes) {
|
||||
const keys = Object.keys(nodes);
|
||||
errorLogger(
|
||||
new Error(`Could not connect to target '${target}' — it does not exist. Available targets: ${keys.join(', ')}`),
|
||||
`Could not connect to target '${target}' — it does not exist. Available targets: ${keys.join(', ')}`,
|
||||
'superdough',
|
||||
);
|
||||
return [];
|
||||
|
|
@ -456,9 +445,7 @@ function _getTargetParams(nodes, target) {
|
|||
if (!targetParam) {
|
||||
const available = _getNodeParams(targetNode);
|
||||
errorLogger(
|
||||
new Error(
|
||||
`Could not connect to parameter '${param}' on '${targetName}'. Available parameters: ${available.join(', ')}`,
|
||||
),
|
||||
`Could not connect to parameter '${param}' on '${target}'. Available parameters: ${available.join(', ')}`,
|
||||
'superdough',
|
||||
);
|
||||
return;
|
||||
|
|
@ -472,26 +459,27 @@ function connectLFO(idx, params, nodeTracker) {
|
|||
const { rate = 1, sync, cps, target, ...filteredParams } = params;
|
||||
filteredParams['frequency'] = sync !== undefined ? sync / cps : rate;
|
||||
const ac = getAudioContext();
|
||||
lfoNode = getLfo(ac, filteredParams);
|
||||
const lfoNode = getLfo(ac, filteredParams);
|
||||
nodeTracker[`lfo${idx}`] = [lfoNode];
|
||||
_getTargetParams(target).forEach(lfoNode.connect);
|
||||
_getTargetParams(nodeTracker, target).forEach((t) => lfoNode.connect(t));
|
||||
}
|
||||
|
||||
function connectEnvelope(idx, params, nodeTracker) {
|
||||
const { target, ...filteredParams } = params;
|
||||
const ac = getAudioContext();
|
||||
envNode = getEnvelope(ac, filteredParams);
|
||||
const envNode = getEnvelope(ac, filteredParams);
|
||||
nodeTracker[`env${idx}`] = [envNode];
|
||||
_getTargetParams(nodeTracker, target).forEach(envNode.connect);
|
||||
_getTargetParams(nodeTracker, target).forEach((t) => envNode.connect(t));
|
||||
}
|
||||
|
||||
function connectSendModulator(params, signal, nodeTracker, pendingConnections) {
|
||||
function connectSendModulator(params, signal, nodeTracker, chainID) {
|
||||
const ac = getAudioContext();
|
||||
const dc = new ConstantSourceNode(ac, { offset: params.dc ?? 0 });
|
||||
dc.start(t);
|
||||
dc.start(params.begin);
|
||||
const offset = new ConstantSourceNode(ac, { offset: params.offset ?? 0 });
|
||||
offset.start(t);
|
||||
offset.start(params.begin);
|
||||
const raw = dc.connect(gainNode(1));
|
||||
const modulator = post
|
||||
const modulator = signal
|
||||
.connect(raw)
|
||||
.connect(gainNode((params.depth ?? 1) / 0.3))
|
||||
.connect(gainNode(1));
|
||||
|
|
@ -499,7 +487,7 @@ function connectSendModulator(params, signal, nodeTracker, pendingConnections) {
|
|||
webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
_getTargetParams(nodeTracker, params.target).forEach(signal.connect);
|
||||
_getTargetParams(nodeTracker, params.target).forEach((t) => modulator.connect(t));
|
||||
},
|
||||
0,
|
||||
params.begin,
|
||||
|
|
@ -507,12 +495,13 @@ function connectSendModulator(params, signal, nodeTracker, pendingConnections) {
|
|||
webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
signal.disconnect();
|
||||
modulator.disconnect();
|
||||
delete pendingConnections[params.id][chainID];
|
||||
},
|
||||
0,
|
||||
params.end + 0.05,
|
||||
);
|
||||
return modulator;
|
||||
}
|
||||
|
||||
let activeSoundSources = new Map();
|
||||
|
|
@ -679,7 +668,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
if (soundHandle) {
|
||||
sourceNode = soundHandle.node;
|
||||
activeSoundSources.set(chainID, new WeakRef(soundHandle)); // allow GC
|
||||
nodes['source'] = [soundHandle.oscillator];
|
||||
nodes['source'] = [soundHandle.source];
|
||||
}
|
||||
} else {
|
||||
throw new Error(`sound ${s} not found! Is it loaded?`);
|
||||
|
|
@ -761,7 +750,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
hpParams.type = 'highpass';
|
||||
const hp1 = filt(hpParams);
|
||||
nodes['hpf'] = [hp1];
|
||||
chain.push(hp());
|
||||
chain.push(hp1);
|
||||
if (ftype === '24db') {
|
||||
const hp2 = filt(hpParams);
|
||||
nodes['hpf'].push(hp1);
|
||||
|
|
@ -791,7 +780,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
const bpParams = pickAndRename(value, bpMap);
|
||||
bpParams.type = 'bandpass';
|
||||
const bp1 = filt(bpParams);
|
||||
chain.push(bp());
|
||||
chain.push(bp1);
|
||||
if (ftype === '24db') {
|
||||
const bp2 = filt(bpParams);
|
||||
nodes['bpf'].push(bp2);
|
||||
|
|
@ -946,7 +935,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
|
||||
// finally, now that `nodes` is populated, set up modulators
|
||||
if (value.lfo) {
|
||||
for (const [params, idx] of Object.entries(value.lfo)) {
|
||||
for (const [idx, params] of Object.entries(value.lfo)) {
|
||||
connectLFO(
|
||||
idx,
|
||||
{
|
||||
|
|
@ -955,13 +944,12 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
begin: t,
|
||||
end: endWithRelease,
|
||||
},
|
||||
'lfo',
|
||||
nodes,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (value.env) {
|
||||
for (const [params, idx] of Object.entries(value.env)) {
|
||||
for (const [idx, params] of Object.entries(value.env)) {
|
||||
connectEnvelope(
|
||||
idx,
|
||||
{
|
||||
|
|
@ -969,7 +957,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
begin: t,
|
||||
end: endWithRelease,
|
||||
},
|
||||
'envelope',
|
||||
nodes,
|
||||
);
|
||||
}
|
||||
|
|
@ -978,22 +965,27 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
idToNodes[value.id] = new WeakRef(nodes);
|
||||
}
|
||||
if (value.send) {
|
||||
for (const p of value.env) {
|
||||
for (const p of value.send) {
|
||||
const modNodes = idToNodes[p.id];
|
||||
if (!modNodes) {
|
||||
logger(
|
||||
`[superdough] Could not connect to pattern ${p.id} -- make sure a pattern with this name exists. Available targets: ${Object.keys(idToNodes).join(', ')}`,
|
||||
);
|
||||
} else {
|
||||
connectSendModulator(params, post);
|
||||
const modulator = connectSendModulator({ ...p, begin: t, end: endWithRelease }, post, nodes, chainID);
|
||||
pendingConnections[p.id] ??= {};
|
||||
pendingConnections[p.id][chainID] = new WeakRef([modulator, p.target, endWithRelease]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.id in pendingConnections) {
|
||||
for (const data of Object.values(pendingConnections[id])) {
|
||||
const [modulator, target, end] = data.deref();
|
||||
for (const data of Object.values(pendingConnections[value.id])) {
|
||||
const derefData = data.deref();
|
||||
if (!derefData) {
|
||||
delete pendingConnections[value.id];
|
||||
continue;
|
||||
}
|
||||
const [modulator, target, end] = derefData;
|
||||
const targets = _getTargetParams(nodes, target);
|
||||
webAudioTimeout(
|
||||
ac,
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export function registerSynthSounds() {
|
|||
stop(envEnd);
|
||||
return {
|
||||
node,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (endTime) => {
|
||||
stop(endTime);
|
||||
},
|
||||
|
|
@ -141,7 +141,7 @@ export function registerSynthSounds() {
|
|||
|
||||
return {
|
||||
node,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (endTime) => {
|
||||
o.stop(endTime);
|
||||
},
|
||||
|
|
@ -208,7 +208,7 @@ export function registerSynthSounds() {
|
|||
|
||||
return {
|
||||
node: envGain,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (time) => {
|
||||
timeoutNode.stop(time);
|
||||
},
|
||||
|
|
@ -285,7 +285,7 @@ export function registerSynthSounds() {
|
|||
|
||||
return {
|
||||
node: envGain,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (time) => {
|
||||
timeoutNode.stop(time);
|
||||
},
|
||||
|
|
@ -363,7 +363,7 @@ export function registerSynthSounds() {
|
|||
|
||||
return {
|
||||
node: envGain,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (time) => {
|
||||
timeoutNode.stop(time);
|
||||
},
|
||||
|
|
@ -409,7 +409,7 @@ export function registerSynthSounds() {
|
|||
stop(envEnd);
|
||||
return {
|
||||
node,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (endTime) => {
|
||||
stop(endTime);
|
||||
},
|
||||
|
|
@ -503,7 +503,7 @@ export function getOscillator(s, t, value) {
|
|||
|
||||
return {
|
||||
node: noiseMix?.node || o,
|
||||
oscillator: o,
|
||||
source: o,
|
||||
stop: (time) => {
|
||||
fmModulator.stop(time);
|
||||
vibratoOscillator?.stop(time);
|
||||
|
|
|
|||
|
|
@ -964,7 +964,7 @@ class EnvelopeProcessor extends AudioWorkletProcessor {
|
|||
{ name: 'attackCurve', defaultValue: 0, minValue: -1, maxValue: 1 },
|
||||
{ name: 'decayCurve', defaultValue: 0, minValue: -1, maxValue: 1 },
|
||||
{ name: 'releaseCurve', defaultValue: 0, minValue: -1, maxValue: 1 },
|
||||
{ name: 'peak', defaultValue: 1 },
|
||||
{ name: 'depth', defaultValue: 1 },
|
||||
{ name: 'retrigger', defaultValue: 1, minValue: 0, maxValue: 1 },
|
||||
];
|
||||
}
|
||||
|
|
@ -1026,7 +1026,7 @@ class EnvelopeProcessor extends AudioWorkletProcessor {
|
|||
const aCurve = pv(params.attackCurve, i);
|
||||
const dCurve = pv(params.decayCurve, i);
|
||||
const rCurve = pv(params.releaseCurve, i);
|
||||
const peak = pv(params.peak, i);
|
||||
const depth = pv(params.depth, i);
|
||||
const states = [
|
||||
{ time: Number.POSITIVE_INFINITY, start: 0, target: 0 }, // idle
|
||||
{ time: attack, start: this.attackStart, target: 1, curve: aCurve },
|
||||
|
|
@ -1040,7 +1040,7 @@ class EnvelopeProcessor extends AudioWorkletProcessor {
|
|||
this.state = (this.state + 1) % states.length;
|
||||
time = states[this.state].time;
|
||||
}
|
||||
out[i] = this.val * peak;
|
||||
out[i] = this.val * depth;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ export const getZZFX = (value, t) => {
|
|||
source.start(t);
|
||||
return {
|
||||
node: source,
|
||||
source,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue