@vessel-dsp/runtime 0.4.1 → 0.6.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 +1 -1
- package/dist/reference-runtime.d.ts +13 -0
- package/dist/reference-runtime.d.ts.map +1 -1
- package/dist/reference-runtime.js +198 -19
- package/dist/reference-runtime.js.map +1 -1
- package/dist/taper.d.ts +29 -1
- package/dist/taper.d.ts.map +1 -1
- package/dist/taper.js +115 -0
- package/dist/taper.js.map +1 -1
- package/dist/wasm/v2_dsp.cjs +1 -1
- package/dist/wasm/v2_dsp.wasm +0 -0
- package/dist/worklet/v2-audio-worklet.js +175 -43
- package/package.json +2 -2
|
@@ -585,37 +585,38 @@ var require_v2_dsp = __commonJS({
|
|
|
585
585
|
var _v2_testonly_resample_reset = Module["_v2_testonly_resample_reset"] = (a0) => (_v2_testonly_resample_reset = Module["_v2_testonly_resample_reset"] = wasmExports["q"])(a0);
|
|
586
586
|
var _v2_testonly_resample_process = Module["_v2_testonly_resample_process"] = (a0, a1) => (_v2_testonly_resample_process = Module["_v2_testonly_resample_process"] = wasmExports["r"])(a0, a1);
|
|
587
587
|
var _v2_testonly_resample_destroy = Module["_v2_testonly_resample_destroy"] = (a0) => (_v2_testonly_resample_destroy = Module["_v2_testonly_resample_destroy"] = wasmExports["s"])(a0);
|
|
588
|
-
var
|
|
589
|
-
var
|
|
590
|
-
var
|
|
591
|
-
var
|
|
592
|
-
var
|
|
593
|
-
var
|
|
594
|
-
var
|
|
595
|
-
var
|
|
596
|
-
var
|
|
597
|
-
var
|
|
598
|
-
var
|
|
599
|
-
var
|
|
600
|
-
var
|
|
601
|
-
var
|
|
602
|
-
var
|
|
603
|
-
var
|
|
604
|
-
var
|
|
605
|
-
var
|
|
606
|
-
var
|
|
607
|
-
var
|
|
608
|
-
var
|
|
609
|
-
var
|
|
610
|
-
var
|
|
611
|
-
var
|
|
612
|
-
var
|
|
613
|
-
var
|
|
614
|
-
var
|
|
615
|
-
var
|
|
616
|
-
var
|
|
617
|
-
var
|
|
618
|
-
var
|
|
588
|
+
var _v2_testonly_taper_fraction = Module["_v2_testonly_taper_fraction"] = (a0, a1) => (_v2_testonly_taper_fraction = Module["_v2_testonly_taper_fraction"] = wasmExports["t"])(a0, a1);
|
|
589
|
+
var _v2_engine_reset = Module["_v2_engine_reset"] = (a0) => (_v2_engine_reset = Module["_v2_engine_reset"] = wasmExports["u"])(a0);
|
|
590
|
+
var _v2_engine_set_control = Module["_v2_engine_set_control"] = (a0, a1, a2) => (_v2_engine_set_control = Module["_v2_engine_set_control"] = wasmExports["v"])(a0, a1, a2);
|
|
591
|
+
var _v2_engine_get_control = Module["_v2_engine_get_control"] = (a0, a1) => (_v2_engine_get_control = Module["_v2_engine_get_control"] = wasmExports["w"])(a0, a1);
|
|
592
|
+
var _v2_engine_set_supply = Module["_v2_engine_set_supply"] = (a0, a1, a2, a3, a4) => (_v2_engine_set_supply = Module["_v2_engine_set_supply"] = wasmExports["x"])(a0, a1, a2, a3, a4);
|
|
593
|
+
var _v2_engine_process_sample = Module["_v2_engine_process_sample"] = (a0, a1) => (_v2_engine_process_sample = Module["_v2_engine_process_sample"] = wasmExports["y"])(a0, a1);
|
|
594
|
+
var _v2_engine_process_block = Module["_v2_engine_process_block"] = (a0, a1, a2, a3) => (_v2_engine_process_block = Module["_v2_engine_process_block"] = wasmExports["z"])(a0, a1, a2, a3);
|
|
595
|
+
var _v2_engine_get_input_buffer = Module["_v2_engine_get_input_buffer"] = (a0, a1) => (_v2_engine_get_input_buffer = Module["_v2_engine_get_input_buffer"] = wasmExports["A"])(a0, a1);
|
|
596
|
+
var _v2_engine_get_output_buffer = Module["_v2_engine_get_output_buffer"] = (a0, a1) => (_v2_engine_get_output_buffer = Module["_v2_engine_get_output_buffer"] = wasmExports["B"])(a0, a1);
|
|
597
|
+
var _v2_engine_process_internal = Module["_v2_engine_process_internal"] = (a0, a1) => (_v2_engine_process_internal = Module["_v2_engine_process_internal"] = wasmExports["C"])(a0, a1);
|
|
598
|
+
var _v2_engine_get_last_iteration_count = Module["_v2_engine_get_last_iteration_count"] = (a0) => (_v2_engine_get_last_iteration_count = Module["_v2_engine_get_last_iteration_count"] = wasmExports["D"])(a0);
|
|
599
|
+
var _v2_engine_get_last_converged = Module["_v2_engine_get_last_converged"] = (a0) => (_v2_engine_get_last_converged = Module["_v2_engine_get_last_converged"] = wasmExports["E"])(a0);
|
|
600
|
+
var _v2_engine_get_max_iterations = Module["_v2_engine_get_max_iterations"] = (a0) => (_v2_engine_get_max_iterations = Module["_v2_engine_get_max_iterations"] = wasmExports["F"])(a0);
|
|
601
|
+
var _v2_engine_get_operating_point = Module["_v2_engine_get_operating_point"] = (a0, a1, a2) => (_v2_engine_get_operating_point = Module["_v2_engine_get_operating_point"] = wasmExports["G"])(a0, a1, a2);
|
|
602
|
+
var _v2_engine_get_state = Module["_v2_engine_get_state"] = (a0, a1, a2) => (_v2_engine_get_state = Module["_v2_engine_get_state"] = wasmExports["H"])(a0, a1, a2);
|
|
603
|
+
var _v2_engine_get_last_error = Module["_v2_engine_get_last_error"] = (a0) => (_v2_engine_get_last_error = Module["_v2_engine_get_last_error"] = wasmExports["I"])(a0);
|
|
604
|
+
var _v2_engine_get_schedule_solves = Module["_v2_engine_get_schedule_solves"] = (a0) => (_v2_engine_get_schedule_solves = Module["_v2_engine_get_schedule_solves"] = wasmExports["J"])(a0);
|
|
605
|
+
var _v2_engine_get_schedule_fallbacks = Module["_v2_engine_get_schedule_fallbacks"] = (a0) => (_v2_engine_get_schedule_fallbacks = Module["_v2_engine_get_schedule_fallbacks"] = wasmExports["K"])(a0);
|
|
606
|
+
var _v2_engine_get_predictor_seeds = Module["_v2_engine_get_predictor_seeds"] = (a0) => (_v2_engine_get_predictor_seeds = Module["_v2_engine_get_predictor_seeds"] = wasmExports["L"])(a0);
|
|
607
|
+
var _v2_engine_get_one_iteration_solves = Module["_v2_engine_get_one_iteration_solves"] = (a0) => (_v2_engine_get_one_iteration_solves = Module["_v2_engine_get_one_iteration_solves"] = wasmExports["M"])(a0);
|
|
608
|
+
var _v2_engine_get_total_iterations = Module["_v2_engine_get_total_iterations"] = (a0) => (_v2_engine_get_total_iterations = Module["_v2_engine_get_total_iterations"] = wasmExports["N"])(a0);
|
|
609
|
+
var _v2_engine_get_predictor_order = Module["_v2_engine_get_predictor_order"] = (a0, a1) => (_v2_engine_get_predictor_order = Module["_v2_engine_get_predictor_order"] = wasmExports["O"])(a0, a1);
|
|
610
|
+
var _v2_engine_get_kernel_solves = Module["_v2_engine_get_kernel_solves"] = (a0) => (_v2_engine_get_kernel_solves = Module["_v2_engine_get_kernel_solves"] = wasmExports["P"])(a0);
|
|
611
|
+
var _v2_engine_get_repivoted_schedule_blocks = Module["_v2_engine_get_repivoted_schedule_blocks"] = (a0) => (_v2_engine_get_repivoted_schedule_blocks = Module["_v2_engine_get_repivoted_schedule_blocks"] = wasmExports["Q"])(a0);
|
|
612
|
+
var _v2_engine_get_abandoned_schedule_blocks = Module["_v2_engine_get_abandoned_schedule_blocks"] = (a0) => (_v2_engine_get_abandoned_schedule_blocks = Module["_v2_engine_get_abandoned_schedule_blocks"] = wasmExports["R"])(a0);
|
|
613
|
+
var _v2_engine_get_dropped_schedule_blocks = Module["_v2_engine_get_dropped_schedule_blocks"] = (a0) => (_v2_engine_get_dropped_schedule_blocks = Module["_v2_engine_get_dropped_schedule_blocks"] = wasmExports["S"])(a0);
|
|
614
|
+
var _malloc = Module["_malloc"] = (a0) => (_malloc = Module["_malloc"] = wasmExports["T"])(a0);
|
|
615
|
+
var _free = Module["_free"] = (a0) => (_free = Module["_free"] = wasmExports["U"])(a0);
|
|
616
|
+
var ___trap = () => (___trap = wasmExports["V"])();
|
|
617
|
+
var __emscripten_stack_restore = (a0) => (__emscripten_stack_restore = wasmExports["W"])(a0);
|
|
618
|
+
var __emscripten_stack_alloc = (a0) => (__emscripten_stack_alloc = wasmExports["X"])(a0);
|
|
619
|
+
var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["Y"])();
|
|
619
620
|
Module["ccall"] = ccall;
|
|
620
621
|
Module["cwrap"] = cwrap;
|
|
621
622
|
Module["setValue"] = setValue;
|
|
@@ -1167,6 +1168,58 @@ function cascadeLatencyHostSamples(stages) {
|
|
|
1167
1168
|
}
|
|
1168
1169
|
|
|
1169
1170
|
// src/taper.ts
|
|
1171
|
+
var IMPLEMENTED_TAPERS = {
|
|
1172
|
+
linear: true,
|
|
1173
|
+
logarithmic: true,
|
|
1174
|
+
"reverse-logarithmic": true,
|
|
1175
|
+
"reverse-linear": true,
|
|
1176
|
+
"s-curve": true
|
|
1177
|
+
};
|
|
1178
|
+
var IMPLEMENTED_TAPER_NAMES = Object.keys(IMPLEMENTED_TAPERS);
|
|
1179
|
+
function unrecognisedTapers(program) {
|
|
1180
|
+
const found = [];
|
|
1181
|
+
const check = (where, taper) => {
|
|
1182
|
+
if (taper === void 0) return;
|
|
1183
|
+
if (typeof taper !== "string" || !Object.hasOwn(IMPLEMENTED_TAPERS, taper)) {
|
|
1184
|
+
found.push({ where, taper: String(taper) });
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
for (const control of program.controls) {
|
|
1188
|
+
check(`control "${control.id}"`, control.taper);
|
|
1189
|
+
}
|
|
1190
|
+
for (const block of program.blocks) {
|
|
1191
|
+
if (block.kind === "mna") {
|
|
1192
|
+
for (const stamp of block.stamps) {
|
|
1193
|
+
if ("taper" in stamp) {
|
|
1194
|
+
check(`${stamp.kind} stamp in block "${block.id}"`, stamp.taper);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
} else if (block.clockControl != null) {
|
|
1198
|
+
check(`clockControl of block "${block.id}"`, block.clockControl.taper);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
return found;
|
|
1202
|
+
}
|
|
1203
|
+
var S_CURVE_SEGMENTS = [
|
|
1204
|
+
{ x0: 0, h: 0.3, y0: 0, y1: 0.1, m0: 0, m1: 0.6 },
|
|
1205
|
+
{ x0: 0.3, h: 0.2, y0: 0.1, y1: 0.5, m0: 0.6, m1: 2 },
|
|
1206
|
+
{ x0: 0.5, h: 0.2, y0: 0.5, y1: 0.9, m0: 2, m1: 0.6 },
|
|
1207
|
+
{ x0: 0.7, h: 0.3, y0: 0.9, y1: 1, m0: 0.6, m1: 0 }
|
|
1208
|
+
];
|
|
1209
|
+
function sCurveFraction(x) {
|
|
1210
|
+
if (x <= 0) return 0;
|
|
1211
|
+
if (x >= 1) return 1;
|
|
1212
|
+
const segment = x < 0.3 ? 0 : x < 0.5 ? 1 : x < 0.7 ? 2 : 3;
|
|
1213
|
+
const { x0, h, y0, y1, m0, m1 } = S_CURVE_SEGMENTS[segment];
|
|
1214
|
+
const t = (x - x0) / h;
|
|
1215
|
+
const t2 = t * t;
|
|
1216
|
+
const t3 = t2 * t;
|
|
1217
|
+
const h00 = 2 * t3 - 3 * t2 + 1;
|
|
1218
|
+
const h10 = t3 - 2 * t2 + t;
|
|
1219
|
+
const h01 = -2 * t3 + 3 * t2;
|
|
1220
|
+
const h11 = t3 - t2;
|
|
1221
|
+
return h00 * y0 + h10 * h * m0 + h01 * y1 + h11 * h * m1;
|
|
1222
|
+
}
|
|
1170
1223
|
function taperFraction(taper, position) {
|
|
1171
1224
|
const x = Math.min(1, Math.max(0, position));
|
|
1172
1225
|
const audioBase = 1089 / 49;
|
|
@@ -1179,6 +1232,9 @@ function taperFraction(taper, position) {
|
|
|
1179
1232
|
if (taper === "reverse-linear") {
|
|
1180
1233
|
return 1 - x;
|
|
1181
1234
|
}
|
|
1235
|
+
if (taper === "s-curve") {
|
|
1236
|
+
return sCurveFraction(x);
|
|
1237
|
+
}
|
|
1182
1238
|
return x;
|
|
1183
1239
|
}
|
|
1184
1240
|
|
|
@@ -1339,7 +1395,10 @@ var OPAMP_FOLD_STREAK = 6;
|
|
|
1339
1395
|
function opAmpHalfSwing(railHigh, railLow) {
|
|
1340
1396
|
return Math.max((railHigh - railLow) / 2, 1e-9);
|
|
1341
1397
|
}
|
|
1342
|
-
var
|
|
1398
|
+
var OPAMP_DEFAULT_GAIN_BANDWIDTH_HZ = 1e6;
|
|
1399
|
+
function opAmpGainBandwidthHz(stamp) {
|
|
1400
|
+
return stamp.gainBandwidthHz ?? OPAMP_DEFAULT_GAIN_BANDWIDTH_HZ;
|
|
1401
|
+
}
|
|
1343
1402
|
var OPAMP_POLE_BAND = 8;
|
|
1344
1403
|
function boundOpAmpRaw(raw, halfSwing) {
|
|
1345
1404
|
const limit = OPAMP_POLE_BAND * halfSwing;
|
|
@@ -1850,6 +1909,8 @@ var ReferenceRuntime = class {
|
|
|
1850
1909
|
limiterTrace = typeof process !== "undefined" && process.env?.VESSEL_LIMITER_TRACE === "1";
|
|
1851
1910
|
limitedIterationCount = 0;
|
|
1852
1911
|
limitedIterationPerSample = [];
|
|
1912
|
+
/** Fold reseeds taken so far (diagnostic, read past `private` by the stall study's tracer). */
|
|
1913
|
+
foldReseedCount = 0;
|
|
1853
1914
|
/** Series ohms of whatever drives the input jack -- see `prepare()`'s option. */
|
|
1854
1915
|
inputSourceOhms = 0;
|
|
1855
1916
|
/**
|
|
@@ -2162,6 +2223,12 @@ var ReferenceRuntime = class {
|
|
|
2162
2223
|
`program requires ${missingModels.length === 1 ? "a DSP model" : "DSP models"} this runtime does not implement: ${missingModels.join(", ")}`
|
|
2163
2224
|
);
|
|
2164
2225
|
}
|
|
2226
|
+
const unrecognised = unrecognisedTapers(this.program);
|
|
2227
|
+
if (unrecognised.length > 0) {
|
|
2228
|
+
throw new RuntimeError(
|
|
2229
|
+
`program names ${unrecognised.length === 1 ? "a taper" : "tapers"} this runtime does not implement: ${unrecognised.map((entry) => `"${entry.taper}" (${entry.where})`).join(", ")}; it implements ${IMPLEMENTED_TAPER_NAMES.join(", ")}`
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2165
2232
|
const dispatched = this.program.blocks.filter(
|
|
2166
2233
|
(block) => block.kind === "macro"
|
|
2167
2234
|
);
|
|
@@ -4025,7 +4092,9 @@ var ReferenceRuntime = class {
|
|
|
4025
4092
|
let bestDelta = Number.POSITIVE_INFINITY;
|
|
4026
4093
|
let noImprovement = 0;
|
|
4027
4094
|
let foldStreak = 0;
|
|
4028
|
-
let
|
|
4095
|
+
let foldReseeds = 0;
|
|
4096
|
+
let lastWorstNode = -1;
|
|
4097
|
+
let lastWorstSign = 0;
|
|
4029
4098
|
for (let iteration = 0; iteration < iterations; iteration += 1) {
|
|
4030
4099
|
used = iteration + 1;
|
|
4031
4100
|
this.limitedIterate = false;
|
|
@@ -4153,6 +4222,12 @@ var ReferenceRuntime = class {
|
|
|
4153
4222
|
}
|
|
4154
4223
|
}
|
|
4155
4224
|
}
|
|
4225
|
+
const damping = dc ? 1 : this.opAmpStepDamping(block, blockIndex, current, next, dt, dc);
|
|
4226
|
+
if (damping < 1) {
|
|
4227
|
+
for (let i = 0; i < size; i++) {
|
|
4228
|
+
next[i] = current[i] + damping * (next[i] - current[i]);
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4156
4231
|
if (relaxing) {
|
|
4157
4232
|
for (let i = 0; i < size; i++) {
|
|
4158
4233
|
next[i] = current[i] + alpha * (next[i] - current[i]);
|
|
@@ -4161,6 +4236,10 @@ var ReferenceRuntime = class {
|
|
|
4161
4236
|
const delta = maxAbsDifference(next, current);
|
|
4162
4237
|
worstNode = worstDifferenceIndex(next, current);
|
|
4163
4238
|
worstDelta = delta;
|
|
4239
|
+
const worstSign = Math.sign((next[worstNode] ?? 0) - (current[worstNode] ?? 0));
|
|
4240
|
+
const reversed = dc || worstNode === lastWorstNode && worstSign !== 0 && lastWorstSign !== 0 && worstSign !== lastWorstSign;
|
|
4241
|
+
lastWorstNode = worstNode;
|
|
4242
|
+
lastWorstSign = worstSign;
|
|
4164
4243
|
if (delta < bestDelta * 0.999) {
|
|
4165
4244
|
bestDelta = delta;
|
|
4166
4245
|
noImprovement = 0;
|
|
@@ -4173,7 +4252,7 @@ var ReferenceRuntime = class {
|
|
|
4173
4252
|
}
|
|
4174
4253
|
}
|
|
4175
4254
|
} else {
|
|
4176
|
-
noImprovement += 1;
|
|
4255
|
+
noImprovement += reversed ? 1 : 0;
|
|
4177
4256
|
if (!relaxing) {
|
|
4178
4257
|
if (noImprovement >= NEWTON_NON_CONTRACTING_LIMIT && iteration >= NEWTON_RELAXATION_EARLIEST_ITERATION) {
|
|
4179
4258
|
relaxing = true;
|
|
@@ -4204,13 +4283,15 @@ var ReferenceRuntime = class {
|
|
|
4204
4283
|
}
|
|
4205
4284
|
const limitedOpamp = this.limitedOpamp;
|
|
4206
4285
|
foldStreak = limitedOpamp?.folded === true ? foldStreak + 1 : 0;
|
|
4207
|
-
if (!dc &&
|
|
4208
|
-
|
|
4286
|
+
if (!dc && limitedOpamp !== null && foldStreak >= OPAMP_FOLD_STREAK) {
|
|
4287
|
+
foldReseeds += 1;
|
|
4288
|
+
this.foldReseedCount += 1;
|
|
4209
4289
|
foldStreak = 0;
|
|
4210
4290
|
const wasHigh = (start[limitedOpamp.output] ?? limitedOpamp.centre) >= limitedOpamp.centre;
|
|
4211
|
-
|
|
4291
|
+
const toHigh = foldReseeds % 2 === 1 ? wasHigh : !wasHigh;
|
|
4292
|
+
current[limitedOpamp.output] = toHigh ? limitedOpamp.railHigh : limitedOpamp.railLow;
|
|
4212
4293
|
this.opampHistory.set(limitedOpamp.key, {
|
|
4213
|
-
differential:
|
|
4294
|
+
differential: toHigh ? limitedOpamp.band : -limitedOpamp.band,
|
|
4214
4295
|
step: 0,
|
|
4215
4296
|
cap: limitedOpamp.maxStep
|
|
4216
4297
|
});
|
|
@@ -4250,6 +4331,53 @@ var ReferenceRuntime = class {
|
|
|
4250
4331
|
}
|
|
4251
4332
|
return { solution: current, converged, used, worstNode, worstDelta };
|
|
4252
4333
|
}
|
|
4334
|
+
/**
|
|
4335
|
+
* The factor the Newton update is scaled by so that no railed op-amp's differential moves
|
|
4336
|
+
* further than its step cap and no op-amp output node moves further than one full swing:
|
|
4337
|
+
* the smallest `cap / |requested differential step|` and `2 * halfSwing / |requested output
|
|
4338
|
+
* step|` over the block's railed op-amps, or 1. `current` is the iterate the rows were
|
|
4339
|
+
* stamped at and `next` the undamped solve. The history read here is the one the stamps
|
|
4340
|
+
* wrote this iteration, so `differential` is the point each row's tangent was taken at and
|
|
4341
|
+
* `cap` the step the next evaluation will allow; a request inside both bounds scales nothing.
|
|
4342
|
+
* Mirrored in `Engine.cpp` (`opAmpStepDamping`); the two must agree to the operation.
|
|
4343
|
+
*/
|
|
4344
|
+
opAmpStepDamping(block, blockIndex, current, next, dt, dc) {
|
|
4345
|
+
let damping = 1;
|
|
4346
|
+
for (const stamp of block.stamps) {
|
|
4347
|
+
if (stamp.kind !== "ideal-opamp" || stamp.railHigh === null || stamp.railLow === null) {
|
|
4348
|
+
continue;
|
|
4349
|
+
}
|
|
4350
|
+
const history = this.opampHistory.get(
|
|
4351
|
+
packHistoryKey3(blockIndex, stamp.plus, stamp.minus, stamp.output)
|
|
4352
|
+
);
|
|
4353
|
+
if (history === void 0) continue;
|
|
4354
|
+
const halfSwing = opAmpHalfSwing(stamp.railHigh, stamp.railLow);
|
|
4355
|
+
const alpha = dc ? 1 : opAmpPoleAlpha(
|
|
4356
|
+
stamp.openLoopGain,
|
|
4357
|
+
opAmpGainBandwidthHz(stamp),
|
|
4358
|
+
dt,
|
|
4359
|
+
this.sampleRate
|
|
4360
|
+
);
|
|
4361
|
+
const effectiveGain = alpha * stamp.openLoopGain;
|
|
4362
|
+
const linearWidth = halfSwing / effectiveGain;
|
|
4363
|
+
const band = OPAMP_DIFFERENTIAL_BAND * linearWidth;
|
|
4364
|
+
const nextDifferential = clamp(
|
|
4365
|
+
(next[stamp.plus] ?? 0) - (next[stamp.minus] ?? 0),
|
|
4366
|
+
-band,
|
|
4367
|
+
band
|
|
4368
|
+
);
|
|
4369
|
+
const magnitude = Math.abs(nextDifferential - history.differential);
|
|
4370
|
+
if (magnitude > history.cap) {
|
|
4371
|
+
damping = Math.min(damping, history.cap / magnitude);
|
|
4372
|
+
}
|
|
4373
|
+
const swing = 2 * halfSwing;
|
|
4374
|
+
const outputStep = Math.abs((next[stamp.output] ?? 0) - (current[stamp.output] ?? 0));
|
|
4375
|
+
if (outputStep > swing) {
|
|
4376
|
+
damping = Math.min(damping, swing / outputStep);
|
|
4377
|
+
}
|
|
4378
|
+
}
|
|
4379
|
+
return damping;
|
|
4380
|
+
}
|
|
4253
4381
|
/**
|
|
4254
4382
|
* Build-order step 6, vertical slice: the part of a block's matrix that is the same for
|
|
4255
4383
|
* every Newton iteration of this sample -- every stamp `newtonPortRowsOf` calls linear,
|
|
@@ -5073,7 +5201,7 @@ var ReferenceRuntime = class {
|
|
|
5073
5201
|
const key = packHistoryKey3(blockIndex, stamp.plus, stamp.minus, stamp.output);
|
|
5074
5202
|
const alpha = opAmpPoleAlpha(
|
|
5075
5203
|
stamp.openLoopGain,
|
|
5076
|
-
|
|
5204
|
+
opAmpGainBandwidthHz(stamp),
|
|
5077
5205
|
dt,
|
|
5078
5206
|
this.sampleRate
|
|
5079
5207
|
);
|
|
@@ -5718,26 +5846,29 @@ var ReferenceRuntime = class {
|
|
|
5718
5846
|
);
|
|
5719
5847
|
const alpha = dc ? 1 : opAmpPoleAlpha(
|
|
5720
5848
|
stamp.openLoopGain,
|
|
5721
|
-
|
|
5849
|
+
opAmpGainBandwidthHz(stamp),
|
|
5722
5850
|
dt,
|
|
5723
5851
|
this.sampleRate
|
|
5724
5852
|
);
|
|
5725
5853
|
const effectiveGain = alpha * stamp.openLoopGain;
|
|
5726
5854
|
const linearWidth = halfSwing / effectiveGain;
|
|
5727
5855
|
const previous = this.opampHistory.get(opampKey);
|
|
5728
|
-
const previousDifferential = previous?.differential ?? 0;
|
|
5729
5856
|
const rawDifferential = (solution[stamp.plus] ?? 0) - (solution[stamp.minus] ?? 0);
|
|
5730
5857
|
const band = OPAMP_DIFFERENTIAL_BAND * linearWidth;
|
|
5731
5858
|
const bounded = clamp(rawDifferential, -band, band);
|
|
5859
|
+
const previousDifferential = previous?.differential ?? bounded;
|
|
5732
5860
|
const step = bounded - previousDifferential;
|
|
5733
5861
|
const maxStep = OPAMP_MAX_DIFFERENTIAL_STEP * linearWidth;
|
|
5734
5862
|
const reversing = previous !== void 0 && previous.step !== 0 && step !== 0 && Math.sign(step) !== Math.sign(previous.step);
|
|
5735
5863
|
const previousCap = previous?.cap ?? maxStep;
|
|
5736
5864
|
const cap = reversing ? Math.max(previousCap / 2, linearWidth / 64) : Math.min(maxStep, previousCap * 1.2);
|
|
5737
5865
|
const limited = Math.abs(step) > cap;
|
|
5866
|
+
const folded = cap <= linearWidth / 8 && Math.abs(step) >= linearWidth / 256;
|
|
5738
5867
|
if (limited) {
|
|
5739
5868
|
this.limitedIterate = true;
|
|
5740
5869
|
this.limitedBy = `opamp +=${stamp.plus} -=${stamp.minus} out=${stamp.output}`;
|
|
5870
|
+
}
|
|
5871
|
+
if (limited || folded) {
|
|
5741
5872
|
this.limitedOpamp = {
|
|
5742
5873
|
key: opampKey,
|
|
5743
5874
|
output: stamp.output,
|
|
@@ -5746,7 +5877,7 @@ var ReferenceRuntime = class {
|
|
|
5746
5877
|
railLow: stamp.railLow,
|
|
5747
5878
|
band,
|
|
5748
5879
|
maxStep,
|
|
5749
|
-
folded
|
|
5880
|
+
folded
|
|
5750
5881
|
};
|
|
5751
5882
|
}
|
|
5752
5883
|
const differential = limited ? previousDifferential + Math.sign(step) * cap : bounded;
|
|
@@ -5763,10 +5894,11 @@ var ReferenceRuntime = class {
|
|
|
5763
5894
|
clamp(scaled, -OPAMP_SATURATION_BAND, OPAMP_SATURATION_BAND)
|
|
5764
5895
|
);
|
|
5765
5896
|
const slope = effectiveGain * (1 - derivativeShape * derivativeShape);
|
|
5897
|
+
const linearisedAt = limited ? differential : rawDifferential;
|
|
5766
5898
|
matrix[row][stamp.output] += 1;
|
|
5767
5899
|
matrix[row][stamp.plus] -= slope;
|
|
5768
5900
|
matrix[row][stamp.minus] += slope;
|
|
5769
|
-
rhs[row] = output - slope *
|
|
5901
|
+
rhs[row] = output - slope * linearisedAt;
|
|
5770
5902
|
break;
|
|
5771
5903
|
}
|
|
5772
5904
|
case "vccs": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vessel-dsp/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Real-time MNA simulation runtime and solver console for VesselDSP Program ROMs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"guitar-pedal",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"generate-kernels": "bun scripts/generate-kernels.ts"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@vessel-dsp/compiler": "0.
|
|
61
|
+
"@vessel-dsp/compiler": "0.6.0"
|
|
62
62
|
},
|
|
63
63
|
"publishConfig": {
|
|
64
64
|
"access": "public"
|