@effetune/dsp 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -5
- package/dist/assets/effetune-dsp.meta.json +91 -37
- package/dist/assets/effetune-dsp.simd.wasm +0 -0
- package/dist/assets/effetune-dsp.wasm +0 -0
- package/dist/catalog/effects-v1.json +798 -12
- package/dist/errors.js +21 -0
- package/dist/generated-effects.d.ts +164 -4
- package/dist/generated-effects.js +94 -3
- package/dist/generated-graph-contract.js +8 -0
- package/dist/graph-document.js +644 -0
- package/dist/graph-engine.js +493 -0
- package/dist/graph.js +467 -0
- package/dist/index.d.ts +247 -1
- package/dist/index.js +22 -0
- package/dist/internal/dsp-engine-binding.js +142 -1
- package/dist/internal/dsp-params.generated.js +362 -16
- package/dist/preset.js +29 -0
- package/dist/runtime.js +73 -5
- package/dist/schemas/chain-v1.schema.json +996 -143
- package/dist/schemas/graph-v1.schema.json +8208 -0
- package/dist/semantics.js +88 -30
- package/dist/worklet-processor.js +8 -1
- package/dist/worklet.d.ts +1 -0
- package/dist/worklet.js +15 -0
- package/package.json +2 -1
package/dist/preset.js
CHANGED
|
@@ -198,6 +198,24 @@ const LEGACY_OBJECT_ARRAYS_V1 = Object.freeze({
|
|
|
198
198
|
count: 5,
|
|
199
199
|
members: Object.freeze({ balance: 'balance' })
|
|
200
200
|
}),
|
|
201
|
+
PhaseSelectEQ: Object.freeze({
|
|
202
|
+
effectLabel: 'Phase Select EQ',
|
|
203
|
+
arrayKey: 'regions',
|
|
204
|
+
count: 5,
|
|
205
|
+
members: Object.freeze({
|
|
206
|
+
en: 'regionEnabled',
|
|
207
|
+
ofl: 'outerFrequencyLow',
|
|
208
|
+
fl: 'coreFrequencyLow',
|
|
209
|
+
fh: 'coreFrequencyHigh',
|
|
210
|
+
ofh: 'outerFrequencyHigh',
|
|
211
|
+
opl: 'outerPhaseLow',
|
|
212
|
+
pl: 'corePhaseLow',
|
|
213
|
+
ph: 'corePhaseHigh',
|
|
214
|
+
oph: 'outerPhaseHigh',
|
|
215
|
+
gn: 'gain'
|
|
216
|
+
}),
|
|
217
|
+
itemLabel: 'region'
|
|
218
|
+
}),
|
|
201
219
|
MultibandSaturation: Object.freeze({
|
|
202
220
|
effectLabel: 'Multiband Saturation',
|
|
203
221
|
arrayKey: 'bands',
|
|
@@ -254,6 +272,10 @@ const LEGACY_ECHOED_STRUCTURAL_KEYS_V1 = Object.freeze(['pluginType', 'ch', 'ib'
|
|
|
254
272
|
// dropped for these two effects only.
|
|
255
273
|
const LEGACY_ECHOED_ENABLED_EFFECTS_V1 = Object.freeze(['HornResonator', 'HornResonatorPlus']);
|
|
256
274
|
|
|
275
|
+
// These analyzers persist their frequency-axis choice for display only. Validate
|
|
276
|
+
// the known values before discarding it so other unknown parameters remain strict.
|
|
277
|
+
const LEGACY_FREQUENCY_SCALE_EFFECTS_V1 = Object.freeze(['SpectrumAnalyzer', 'Spectrogram']);
|
|
278
|
+
|
|
257
279
|
function dropEchoedStructuralKeysV1(parameters, effectType) {
|
|
258
280
|
for (const key of LEGACY_ECHOED_STRUCTURAL_KEYS_V1) delete parameters[key];
|
|
259
281
|
if (LEGACY_ECHOED_ENABLED_EFFECTS_V1.includes(effectType)) delete parameters.en;
|
|
@@ -339,6 +361,13 @@ function expandLegacyObjectArrayV1(parameters, {
|
|
|
339
361
|
|
|
340
362
|
function prepareLegacyParametersV1(effectType, source) {
|
|
341
363
|
const parameters = dropEchoedStructuralKeysV1({ ...source }, effectType);
|
|
364
|
+
if (LEGACY_FREQUENCY_SCALE_EFFECTS_V1.includes(effectType) &&
|
|
365
|
+
Object.hasOwn(parameters, 'sc')) {
|
|
366
|
+
if (parameters.sc !== 'log' && parameters.sc !== 'linear') {
|
|
367
|
+
throw new ValidationError(`Legacy ${effectType} contains invalid frequency scale display state.`);
|
|
368
|
+
}
|
|
369
|
+
delete parameters.sc;
|
|
370
|
+
}
|
|
342
371
|
let processingEnabled = true;
|
|
343
372
|
if (effectType === 'Matrix' && Object.hasOwn(parameters, 'mx')) {
|
|
344
373
|
if (Object.hasOwn(parameters, 'matrixRoutes')) {
|
package/dist/runtime.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createEngineSession } from './engine.js';
|
|
|
4
4
|
import { StateError, ValidationError } from './errors.js';
|
|
5
5
|
import {
|
|
6
6
|
normalizeChainDocument,
|
|
7
|
+
canonicalizeEffectForProcessing,
|
|
7
8
|
channelRange,
|
|
8
9
|
packEffect,
|
|
9
10
|
setEffectParameter,
|
|
@@ -90,18 +91,31 @@ function updateEffectParameters(effect, parameters) {
|
|
|
90
91
|
return updated;
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
function updateProcessingEffectParameters(effect, parameters) {
|
|
95
|
+
return canonicalizeEffectForProcessing(updateEffectParameters(effect, parameters));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function canonicalizeDocumentForProcessing(document) {
|
|
99
|
+
return {
|
|
100
|
+
version: 1,
|
|
101
|
+
chain: document.chain.map(canonicalizeEffectForProcessing)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
function replaceEffect(document, index, effect) {
|
|
94
106
|
const chain = [...document.chain];
|
|
95
107
|
chain[index] = effect;
|
|
96
108
|
return { version: 1, chain };
|
|
97
109
|
}
|
|
98
110
|
|
|
99
|
-
function validateParameterEvents(events, document, frameCount) {
|
|
111
|
+
function validateParameterEvents(events, document, processingDocument, frameCount) {
|
|
100
112
|
if (events === undefined) return [];
|
|
101
113
|
if (!Array.isArray(events)) {
|
|
102
114
|
throw new ValidationError('events must be an array.');
|
|
103
115
|
}
|
|
104
116
|
let workingDocument = cloneDocument(document);
|
|
117
|
+
let workingProcessingDocument = cloneDocument(processingDocument);
|
|
118
|
+
const pendingPatches = new Map();
|
|
105
119
|
let previousFrame = -1;
|
|
106
120
|
return events.map((event, eventIndex) => {
|
|
107
121
|
if (!isRecord(event)) {
|
|
@@ -123,12 +137,32 @@ function validateParameterEvents(events, document, frameCount) {
|
|
|
123
137
|
previousFrame = event.frame;
|
|
124
138
|
const index = effectIndex(workingDocument, event.effectId);
|
|
125
139
|
const effect = updateEffectParameters(workingDocument.chain[index], event.parameters);
|
|
140
|
+
let pending = pendingPatches.get(event.effectId);
|
|
141
|
+
if (!pending || pending.frame !== event.frame) {
|
|
142
|
+
pending = {
|
|
143
|
+
frame: event.frame,
|
|
144
|
+
effect: workingProcessingDocument.chain[index],
|
|
145
|
+
parameters: {}
|
|
146
|
+
};
|
|
147
|
+
pendingPatches.set(event.effectId, pending);
|
|
148
|
+
}
|
|
149
|
+
Object.assign(pending.parameters, event.parameters);
|
|
150
|
+
const processingEffect = updateProcessingEffectParameters(
|
|
151
|
+
pending.effect,
|
|
152
|
+
pending.parameters
|
|
153
|
+
);
|
|
126
154
|
workingDocument = replaceEffect(workingDocument, index, effect);
|
|
155
|
+
workingProcessingDocument = replaceEffect(
|
|
156
|
+
workingProcessingDocument,
|
|
157
|
+
index,
|
|
158
|
+
processingEffect
|
|
159
|
+
);
|
|
127
160
|
return {
|
|
128
161
|
frame: event.frame,
|
|
129
162
|
effectId: event.effectId,
|
|
130
163
|
effect,
|
|
131
|
-
|
|
164
|
+
processingEffect,
|
|
165
|
+
packed: packEffect(processingEffect)
|
|
132
166
|
};
|
|
133
167
|
});
|
|
134
168
|
}
|
|
@@ -142,6 +176,9 @@ class ChainStream {
|
|
|
142
176
|
}) {
|
|
143
177
|
this._initialDocument = cloneDocument(document);
|
|
144
178
|
this._document = cloneDocument(document);
|
|
179
|
+
this._initialProcessingDocument = canonicalizeDocumentForProcessing(document);
|
|
180
|
+
this._processingDocument = cloneDocument(this._initialProcessingDocument);
|
|
181
|
+
this._pendingProcessingPatches = new Map();
|
|
145
182
|
this._session = session;
|
|
146
183
|
this._sampleRate = sampleRate;
|
|
147
184
|
this._channels = channels;
|
|
@@ -154,7 +191,7 @@ class ChainStream {
|
|
|
154
191
|
|
|
155
192
|
get preset() {
|
|
156
193
|
this._assertOpen();
|
|
157
|
-
return cloneDocument(this.
|
|
194
|
+
return cloneDocument(this._processingDocument);
|
|
158
195
|
}
|
|
159
196
|
|
|
160
197
|
get effects() {
|
|
@@ -180,9 +217,27 @@ class ChainStream {
|
|
|
180
217
|
const index = effectIndex(this._document, effectId);
|
|
181
218
|
validateStreamParameterUpdate(this._document.chain[index], parameterName);
|
|
182
219
|
const effect = setEffectParameter(this._document.chain[index], parameterName, value);
|
|
183
|
-
|
|
220
|
+
let pending = this._pendingProcessingPatches.get(effectId);
|
|
221
|
+
if (!pending) {
|
|
222
|
+
pending = {
|
|
223
|
+
effect: this._processingDocument.chain[index],
|
|
224
|
+
parameters: {}
|
|
225
|
+
};
|
|
226
|
+
this._pendingProcessingPatches.set(effectId, pending);
|
|
227
|
+
}
|
|
228
|
+
pending.parameters[parameterName] = value;
|
|
229
|
+
const processingEffect = updateProcessingEffectParameters(
|
|
230
|
+
pending.effect,
|
|
231
|
+
pending.parameters
|
|
232
|
+
);
|
|
233
|
+
const packed = packEffect(processingEffect);
|
|
184
234
|
this._session?.setPacked(effectId, packed.values, packed.hash, packed.bytes);
|
|
185
235
|
this._document = replaceEffect(this._document, index, effect);
|
|
236
|
+
this._processingDocument = replaceEffect(
|
|
237
|
+
this._processingDocument,
|
|
238
|
+
index,
|
|
239
|
+
processingEffect
|
|
240
|
+
);
|
|
186
241
|
return this;
|
|
187
242
|
}
|
|
188
243
|
|
|
@@ -211,9 +266,15 @@ class ChainStream {
|
|
|
211
266
|
`Audio has ${layout.channels} channel(s); this stream requires ${this._channels}.`
|
|
212
267
|
);
|
|
213
268
|
}
|
|
214
|
-
const parameterEvents = validateParameterEvents(
|
|
269
|
+
const parameterEvents = validateParameterEvents(
|
|
270
|
+
events,
|
|
271
|
+
this._document,
|
|
272
|
+
this._processingDocument,
|
|
273
|
+
layout.frames
|
|
274
|
+
);
|
|
215
275
|
const output = audio.map(channel => new Float32Array(channel));
|
|
216
276
|
if (layout.frames === 0) return output;
|
|
277
|
+
this._pendingProcessingPatches.clear();
|
|
217
278
|
|
|
218
279
|
let offset = 0;
|
|
219
280
|
let eventIndex = 0;
|
|
@@ -228,6 +289,11 @@ class ChainStream {
|
|
|
228
289
|
);
|
|
229
290
|
const index = effectIndex(this._document, event.effectId);
|
|
230
291
|
this._document = replaceEffect(this._document, index, event.effect);
|
|
292
|
+
this._processingDocument = replaceEffect(
|
|
293
|
+
this._processingDocument,
|
|
294
|
+
index,
|
|
295
|
+
event.processingEffect
|
|
296
|
+
);
|
|
231
297
|
eventIndex += 1;
|
|
232
298
|
}
|
|
233
299
|
const nextEventFrame = parameterEvents[eventIndex]?.frame ?? layout.frames;
|
|
@@ -254,6 +320,8 @@ class ChainStream {
|
|
|
254
320
|
this._assertOpen();
|
|
255
321
|
this._session?.reset();
|
|
256
322
|
this._document = cloneDocument(this._initialDocument);
|
|
323
|
+
this._processingDocument = cloneDocument(this._initialProcessingDocument);
|
|
324
|
+
this._pendingProcessingPatches.clear();
|
|
257
325
|
this._processedFrames = 0;
|
|
258
326
|
return this;
|
|
259
327
|
}
|