Import kabelsalat, update transpiler to handle K and S, add worklet control
This commit is contained in:
parent
fd472a64d9
commit
5caaf423ff
8 changed files with 311 additions and 8 deletions
|
|
@ -36,6 +36,7 @@
|
|||
"vite-plugin-bundle-audioworklet": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kabelsalat/lib": "^0.4.0",
|
||||
"nanostores": "^0.11.3"
|
||||
},
|
||||
"engines": {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
gainNode,
|
||||
getCompressor,
|
||||
getDistortion,
|
||||
getFrequencyFromValue,
|
||||
getLfo,
|
||||
getWorklet,
|
||||
releaseAudioNode,
|
||||
|
|
@ -407,9 +408,9 @@ function mapChannelNumbers(channels) {
|
|||
}
|
||||
|
||||
class Chain {
|
||||
constructor(head) {
|
||||
this.audioNodes = [head];
|
||||
this.tails = [head];
|
||||
constructor() {
|
||||
this.audioNodes = [];
|
||||
this.tails = [];
|
||||
}
|
||||
connect(...nodes) {
|
||||
nodes.forEach((node) => {
|
||||
|
|
@ -538,6 +539,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
value.s = s;
|
||||
}
|
||||
|
||||
const chain = new Chain(); // connection manager which tracks audio nodes for releasing
|
||||
|
||||
// get source AudioNode
|
||||
let sourceNode;
|
||||
if (source) {
|
||||
|
|
@ -578,7 +581,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
return;
|
||||
}
|
||||
|
||||
const chain = new Chain(sourceNode); // connection manager which tracks audio nodes for releasing
|
||||
chain.connect(sourceNode);
|
||||
|
||||
FX = [...FX, value]; // run through the FX chain and then run through all FX outside of it as well
|
||||
for (let [idx, fx] of Object.entries(FX)) {
|
||||
const key = idx == FX.length - 1 ? 'main' : idx;
|
||||
|
|
@ -596,7 +600,6 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
delayfeedback = getDefaultValue('delayfeedback'),
|
||||
delaysync = getDefaultValue('delaysync'),
|
||||
delaytime,
|
||||
stretch = getDefaultValue('stretch'),
|
||||
i = getDefaultValue('i'),
|
||||
} = fx;
|
||||
gain = applyGainCurve(nanFallback(gain, 1));
|
||||
|
|
@ -607,8 +610,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future
|
||||
delaytime = delaytime ?? cycleToSeconds(delaysync, cps);
|
||||
|
||||
if (stretch !== undefined) {
|
||||
const phaseVocoder = getWorklet(ac, 'phase-vocoder-processor', { pitchFactor: stretch });
|
||||
if (fx.stretch !== undefined) {
|
||||
const phaseVocoder = getWorklet(ac, 'phase-vocoder-processor', { pitchFactor: fx.stretch });
|
||||
chain.connect(phaseVocoder);
|
||||
fxNodes['stretch'] = [phaseVocoder];
|
||||
}
|
||||
|
|
@ -844,6 +847,17 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
filterChain.forEach((f) => chain.connect(f));
|
||||
chain.audioNodes.push(lfo);
|
||||
}
|
||||
if (fx.workletSrc !== undefined) {
|
||||
const workletNode = getWorklet(ac, 'generic-processor', {});
|
||||
chain.connect(workletNode);
|
||||
const workletSrc = fx.workletSrc
|
||||
.replace(/\bpat\[(\d+)\]/g, (_, i) => fx.workletInputs[i])
|
||||
.replaceAll('sFreq', getFrequencyFromValue(value))
|
||||
.replaceAll('sGate', `cc('strudel-gate-${chainID}')`);
|
||||
/* global compileKabel */
|
||||
const { src, ugens, registers } = compileKabel(workletSrc);
|
||||
workletNode.port.postMessage({ src, schema: { ugens, registers }, start: t, end });
|
||||
}
|
||||
// delay
|
||||
if (key !== 'main' && delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||
const dry = gainNode(1);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
import OLAProcessor from './ola-processor';
|
||||
import FFT from './fft.js';
|
||||
import { getDistortionAlgorithm } from './helpers.mjs';
|
||||
import * as ugens from '@kabelsalat/lib/src/ugens.js';
|
||||
|
||||
const UGENS = new Map(Object.entries(ugens));
|
||||
|
||||
const blockSize = 128;
|
||||
const PI = Math.PI;
|
||||
|
|
@ -1478,3 +1481,73 @@ class TransientProcessor extends AudioWorkletProcessor {
|
|||
}
|
||||
|
||||
registerProcessor('transient-processor', TransientProcessor);
|
||||
|
||||
class GenericProcessor extends AudioWorkletProcessor {
|
||||
constructor() {
|
||||
super();
|
||||
this.playPos = 0;
|
||||
const channels = 16;
|
||||
this.outputs = new Array(channels).fill(0);
|
||||
this.sources = new Array(channels).fill(0);
|
||||
this.gateEnded = false;
|
||||
this.started = false;
|
||||
this.port.onmessage = (event) => {
|
||||
let {
|
||||
src,
|
||||
schema: { ugens, registers },
|
||||
start,
|
||||
end,
|
||||
} = event.data;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.registers = new Array(registers).fill(0);
|
||||
this.src = `o.fill(0); // reset outputs\n${src}`;
|
||||
this.nodes = [];
|
||||
for (let i = 0; i < ugens.length; i++) {
|
||||
const ugen = ugens[i];
|
||||
const nodeClass = UGENS.get(ugen.type);
|
||||
const node = new nodeClass(i, ugen, sampleRate);
|
||||
if (node.type === 'cc' && ugen.inputs?.[0]?.includes('strudel-gate')) {
|
||||
node.setValue(1);
|
||||
this.gateNode = node;
|
||||
}
|
||||
this.nodes[i] = node;
|
||||
}
|
||||
this.genSample = new Function(
|
||||
'time',
|
||||
'nodes',
|
||||
'input',
|
||||
'r', // registers
|
||||
'o', // outputs
|
||||
's', // sources
|
||||
this.src,
|
||||
);
|
||||
};
|
||||
}
|
||||
process(inputs, outputs) {
|
||||
const input = inputs[0]?.[0];
|
||||
if (this.genSample === undefined || currentTime < this.start) {
|
||||
// pending
|
||||
return true;
|
||||
} else if (input === undefined) {
|
||||
// if no input and started, return false and close worklet
|
||||
// else just keep waiting (true)
|
||||
return !this.started;
|
||||
}
|
||||
this.started = true;
|
||||
if (!this.gateEnded && currentTime > this.end) {
|
||||
this.gateNode?.setValue(0);
|
||||
this.gateEnded = true;
|
||||
}
|
||||
const outL = outputs[0][0];
|
||||
const outR = outputs[0][1] ?? outputs[0][0];
|
||||
for (let n = 0; n < blockSize; n++) {
|
||||
this.genSample(this.playPos, this.nodes, input ? input[n] : 0, this.registers, this.outputs, this.sources);
|
||||
outL[n] = this.outputs[0];
|
||||
outR[n] = this.outputs[1];
|
||||
this.playPos += 1 / sampleRate;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
registerProcessor('generic-processor', GenericProcessor);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue