@vessel-dsp/runtime 0.6.3 → 0.12.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.
Binary file
@@ -1360,6 +1360,77 @@ function reverbCombGain(delaySeconds, decaySeconds) {
1360
1360
  return Math.min(0.999, 10 ** (-3 * delaySeconds / decaySeconds));
1361
1361
  }
1362
1362
  var DEFAULT_NEWTON_MAX_ITERATIONS = 1024;
1363
+ function ldrTableResistanceOhms(currentsAmps, ohms, currentAmps, minOhms, maxOhms) {
1364
+ if (!(currentAmps > 0)) {
1365
+ return maxOhms;
1366
+ }
1367
+ const n = currentsAmps.length;
1368
+ let lo = 0;
1369
+ let hi = 1;
1370
+ if (currentAmps <= currentsAmps[0]) {
1371
+ lo = 0;
1372
+ hi = 1;
1373
+ } else if (currentAmps >= currentsAmps[n - 1]) {
1374
+ lo = n - 2;
1375
+ hi = n - 1;
1376
+ } else {
1377
+ for (let i = 0; i < n - 1; i++) {
1378
+ if (currentAmps >= currentsAmps[i] && currentAmps <= currentsAmps[i + 1]) {
1379
+ lo = i;
1380
+ hi = i + 1;
1381
+ break;
1382
+ }
1383
+ }
1384
+ }
1385
+ const iA = currentsAmps[lo];
1386
+ const iB = currentsAmps[hi];
1387
+ const rA = ohms[lo];
1388
+ const rB = ohms[hi];
1389
+ const lnI = Math.log(currentAmps);
1390
+ const lnI0 = Math.log(iA);
1391
+ const lnI1 = Math.log(iB);
1392
+ const lnR0 = Math.log(rA);
1393
+ const lnR1 = Math.log(rB);
1394
+ const slope = (lnR1 - lnR0) / (lnI1 - lnI0);
1395
+ const raw = Math.exp(lnR0 + (lnI - lnI0) * slope);
1396
+ return Math.min(Math.max(raw, minOhms), maxOhms);
1397
+ }
1398
+ function dbxVcaGainLinear(gainDb) {
1399
+ return Math.pow(10, gainDb / 20);
1400
+ }
1401
+ function dbxVcaRestTerms(vControl, sustainGain, sustainRef) {
1402
+ return sustainGain * (vControl - sustainRef);
1403
+ }
1404
+ function dbxVcaEnvelopeTerms(inputAmps, slopeVoltsPerNeper, kneeVolts) {
1405
+ const absIn = Math.abs(inputAmps);
1406
+ if (!(absIn > 0)) return 0;
1407
+ return Math.max(0, slopeVoltsPerNeper * Math.log(absIn * 1e6) + kneeVolts);
1408
+ }
1409
+ function dbxVcaKneeVolts(vControl, kneeBaseVolts, kneeTrack) {
1410
+ return kneeBaseVolts + kneeTrack * (vControl - 4.65);
1411
+ }
1412
+ function dbxVcaReleaseTc(depth01, baseSeconds, slowdown) {
1413
+ const depth = Math.min(1, Math.max(0, depth01));
1414
+ return baseSeconds * Math.pow(slowdown, depth);
1415
+ }
1416
+ function dbxVcaPrimeTarget(inputAmps, refCurrentAmps) {
1417
+ return Math.abs(inputAmps) > refCurrentAmps ? 0 : 1;
1418
+ }
1419
+ function dbxVcaShutdownTarget(peakTargetVolts, edgeThreshVolts, primed01, gainPerVolt) {
1420
+ const excess = peakTargetVolts - edgeThreshVolts;
1421
+ if (!(excess > 0) || !(primed01 > 0)) return 0;
1422
+ const primed = Math.min(1, primed01);
1423
+ return gainPerVolt * excess * excess * primed;
1424
+ }
1425
+ function dbxVcaVictimTc(primed01, fastSeconds, slowSeconds) {
1426
+ const primed = Math.min(1, Math.max(0, primed01));
1427
+ return primed > 0.05 ? slowSeconds : fastSeconds;
1428
+ }
1429
+ function dbxVcaEnvelopeK(tcSeconds, sampleRate2) {
1430
+ const denom = tcSeconds * sampleRate2;
1431
+ if (!(denom > 0)) return 1;
1432
+ return 1 - Math.exp(-1 / denom);
1433
+ }
1363
1434
  var NEWTON_UNPRODUCTIVE_ITERATIONS = 64;
1364
1435
  var NEWTON_UNPRODUCTIVE_PROBATION_SAMPLES = 1024;
1365
1436
  var NEWTON_RELATIVE_TOLERANCE = 1e-3;
@@ -1542,7 +1613,10 @@ var IMPLEMENTED_OPERATORS = {
1542
1613
  "clock-driver": true,
1543
1614
  comparator: true,
1544
1615
  // Linear-control VCA cell. Reads the control node, draws nothing.
1545
- "linear-vca": true
1616
+ "linear-vca": true,
1617
+ // dbx-style VCA cell (uPC1252H2). Bounded sigmoidal gain in the
1618
+ // control voltage. Reads the control node, draws nothing.
1619
+ "dbx-vca": true
1546
1620
  };
1547
1621
  function unimplementedOperators(required) {
1548
1622
  return required.filter(
@@ -5376,7 +5450,15 @@ var ReferenceRuntime = class {
5376
5450
  rhs[stamp.ledCathode] = (rhs[stamp.ledCathode] ?? 0) + ledEquivalent;
5377
5451
  const alpha = 1e3;
5378
5452
  let rLdr;
5379
- if (stamp.ldrPowerLawCoefficientOhms !== void 0 && stamp.ldrPowerLawExponent !== void 0) {
5453
+ if (stamp.ldrTableCurrentAmps !== void 0 && stamp.ldrTableOhms !== void 0 && stamp.ldrTableCurrentAmps.length === stamp.ldrTableOhms.length && stamp.ldrTableCurrentAmps.length >= 2) {
5454
+ rLdr = ldrTableResistanceOhms(
5455
+ stamp.ldrTableCurrentAmps,
5456
+ stamp.ldrTableOhms,
5457
+ ledCurrent,
5458
+ stamp.ldrMinOhms,
5459
+ stamp.ldrMaxOhms
5460
+ );
5461
+ } else if (stamp.ldrPowerLawCoefficientOhms !== void 0 && stamp.ldrPowerLawExponent !== void 0) {
5380
5462
  const powerLawCurrent = Math.max(ledCurrent, 0);
5381
5463
  const raw = stamp.ldrPowerLawCoefficientOhms * Math.pow(powerLawCurrent, stamp.ldrPowerLawExponent);
5382
5464
  rLdr = Math.min(Math.max(raw, stamp.ldrMinOhms), stamp.ldrMaxOhms);
@@ -6103,6 +6185,106 @@ var ReferenceRuntime = class {
6103
6185
  }
6104
6186
  break;
6105
6187
  }
6188
+ case "dbx-vca": {
6189
+ const dbxBaseIdx = stamp.stateIndex;
6190
+ const ctrlKey = `${block.id}_dbxc_${dbxBaseIdx}`;
6191
+ if (this.elapsedSamples !== (this.lastShiftedSample.get(ctrlKey) ?? -1)) {
6192
+ this.lastShiftedSample.set(ctrlKey, this.elapsedSamples);
6193
+ const vCtrlNow = solution[stamp.control] ?? 0;
6194
+ const ctrlPrev = state[dbxBaseIdx] ?? 0;
6195
+ const ctrlTc = vCtrlNow > ctrlPrev ? stamp.attackSeconds : stamp.releaseSeconds;
6196
+ const ctrlK = dbxVcaEnvelopeK(ctrlTc, this.sampleRate);
6197
+ state[dbxBaseIdx] = ctrlPrev + (vCtrlNow - ctrlPrev) * ctrlK;
6198
+ }
6199
+ const vCtrlHeld = state[dbxBaseIdx] ?? 0;
6200
+ const restHeld = dbxVcaRestTerms(
6201
+ vCtrlHeld,
6202
+ stamp.sustainGain,
6203
+ stamp.sustainRef
6204
+ );
6205
+ const kneeHeld = dbxVcaKneeVolts(
6206
+ vCtrlHeld,
6207
+ stamp.kneeBaseVolts,
6208
+ stamp.kneeTrack
6209
+ );
6210
+ const vInInstDbx = (solution[stamp.plus] ?? 0) - (solution[stamp.minus] ?? 0);
6211
+ const iInInstDbx = vInInstDbx * stamp.inputSiemens;
6212
+ const primeKey = `${block.id}_dbxp_${dbxBaseIdx}`;
6213
+ if (this.elapsedSamples !== (this.lastShiftedSample.get(primeKey) ?? -1)) {
6214
+ this.lastShiftedSample.set(primeKey, this.elapsedSamples);
6215
+ const primeTarget = dbxVcaPrimeTarget(
6216
+ iInInstDbx,
6217
+ stamp.primeRefCurrent
6218
+ );
6219
+ const primePrev = state[dbxBaseIdx + 2] ?? 0;
6220
+ const primeTc = primeTarget > primePrev ? stamp.primeRiseSeconds : stamp.primeFallSeconds;
6221
+ const primeK = dbxVcaEnvelopeK(primeTc, this.sampleRate);
6222
+ state[dbxBaseIdx + 2] = primePrev + (primeTarget - primePrev) * primeK;
6223
+ }
6224
+ const primeHeld = state[dbxBaseIdx + 2] ?? 0;
6225
+ const envKey = `${block.id}_dbxe_${dbxBaseIdx}`;
6226
+ if (this.elapsedSamples !== (this.lastShiftedSample.get(envKey) ?? -1)) {
6227
+ this.lastShiftedSample.set(envKey, this.elapsedSamples);
6228
+ const envTarget = dbxVcaEnvelopeTerms(
6229
+ iInInstDbx,
6230
+ stamp.envelopeSlope,
6231
+ kneeHeld
6232
+ );
6233
+ const envPrev = state[dbxBaseIdx + 1] ?? 0;
6234
+ const envDepth = Math.min(1, Math.max(0, envPrev / 0.15));
6235
+ const envTc = envTarget > envPrev ? stamp.attackSeconds : dbxVcaReleaseTc(envDepth, stamp.releaseSeconds, stamp.releaseSlowdown);
6236
+ const envK = dbxVcaEnvelopeK(envTc, this.sampleRate);
6237
+ state[dbxBaseIdx + 1] = envPrev + (envTarget - envPrev) * envK;
6238
+ }
6239
+ const envHeld = state[dbxBaseIdx + 1] ?? 0;
6240
+ const shutKey = `${block.id}_dbxs_${dbxBaseIdx}`;
6241
+ if (this.elapsedSamples !== (this.lastShiftedSample.get(shutKey) ?? -1)) {
6242
+ this.lastShiftedSample.set(shutKey, this.elapsedSamples);
6243
+ const peakTarget = dbxVcaEnvelopeTerms(
6244
+ iInInstDbx,
6245
+ stamp.envelopeSlope,
6246
+ kneeHeld
6247
+ );
6248
+ const shutTarget = dbxVcaShutdownTarget(
6249
+ peakTarget,
6250
+ stamp.shutdownEdgeVolts,
6251
+ primeHeld,
6252
+ stamp.shutdownGain
6253
+ );
6254
+ const shutPrev = state[dbxBaseIdx + 3] ?? 0;
6255
+ const shutTc = shutTarget > shutPrev ? stamp.shutdownAttackSeconds : dbxVcaVictimTc(
6256
+ primeHeld,
6257
+ stamp.shutdownFastSeconds,
6258
+ stamp.shutdownSlowSeconds
6259
+ );
6260
+ const shutK = dbxVcaEnvelopeK(shutTc, this.sampleRate);
6261
+ state[dbxBaseIdx + 3] = shutPrev + (shutTarget - shutPrev) * shutK;
6262
+ }
6263
+ const shutHeld = state[dbxBaseIdx + 3] ?? 0;
6264
+ const termsHeld = restHeld + envHeld;
6265
+ const rawDb = -termsHeld / 59e-4;
6266
+ const gainDb = Math.min(stamp.maxGainDb, Math.max(stamp.minGainDb, rawDb));
6267
+ const totalDb = Math.max(-100, gainDb - shutHeld / 59e-4);
6268
+ const gain = dbxVcaGainLinear(totalDb);
6269
+ const vPlus = solution[stamp.plus] ?? 0;
6270
+ const vMinus = solution[stamp.minus] ?? 0;
6271
+ const vIn = vPlus - vMinus;
6272
+ const iIn = vIn * stamp.inputSiemens;
6273
+ const iOut = iIn * gain;
6274
+ const gIn = gain * stamp.inputSiemens;
6275
+ const leaving = -iOut;
6276
+ const equivalent = leaving - (-gIn * vPlus + gIn * vMinus);
6277
+ if (stamp.output !== 0) {
6278
+ if (stamp.plus !== 0) {
6279
+ matrix[stamp.output][stamp.plus] += -gIn;
6280
+ }
6281
+ if (stamp.minus !== 0) {
6282
+ matrix[stamp.output][stamp.minus] += gIn;
6283
+ }
6284
+ rhs[stamp.output] = (rhs[stamp.output] ?? 0) - equivalent;
6285
+ }
6286
+ break;
6287
+ }
6106
6288
  case "spring-reverb": {
6107
6289
  const conductance = 1 / Math.max(stamp.inputOhms, MIN_STAMP_OHMS);
6108
6290
  matrix[stamp.inputPlus][stamp.inputPlus] += conductance;
package/package.json CHANGED
@@ -1,66 +1,66 @@
1
1
  {
2
- "name": "@vessel-dsp/runtime",
3
- "version": "0.6.3",
4
- "description": "Real-time MNA simulation runtime and solver console for VesselDSP Program ROMs.",
5
- "keywords": [
6
- "guitar-pedal",
7
- "electronics",
8
- "simulation",
9
- "dsp",
10
- "runtime",
11
- "solver",
12
- "mna"
13
- ],
14
- "homepage": "https://vessel-dsp.github.io/core/",
15
- "bugs": {
16
- "url": "https://github.com/vessel-dsp/core/issues"
17
- },
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/vessel-dsp/core.git"
21
- },
22
- "license": "MIT",
23
- "sideEffects": false,
24
- "type": "module",
25
- "exports": {
26
- ".": {
27
- "types": "./dist/index.d.ts",
28
- "bun": "./dist/index.js",
29
- "import": "./dist/index.js"
30
- },
31
- "./package.json": {
32
- "default": "./package.json"
33
- },
34
- "./wasm/*": {
35
- "default": "./dist/wasm/*"
36
- },
37
- "./worklet.js": {
38
- "default": "./dist/worklet/v2-audio-worklet.js"
39
- }
40
- },
41
- "main": "./dist/index.js",
42
- "module": "./dist/index.js",
43
- "types": "./dist/index.d.ts",
44
- "files": [
45
- "dist",
46
- "README.md",
47
- "LICENSE.md"
48
- ],
49
- "scripts": {
50
- "build": "tsc -p tsconfig.build.json && bun run ../../scripts/fix-dist-imports.ts dist && rm -rf dist/wasm && cp -r src/wasm dist/wasm && bun scripts/build-worklet.ts",
51
- "clean": "rm -rf dist",
52
- "prepack": "bun run --cwd ../core build && bun run --cwd ../compiler build && bun run build:wasm && bun run build",
53
- "pack:dry-run": "npm pack --dry-run",
54
- "typecheck": "tsc --noEmit -p tsconfig.json",
55
- "build:wasm": "bash scripts/build-wasm.sh",
56
- "build:worklet": "bun scripts/build-worklet.ts",
57
- "test": "bun test tests",
58
- "generate-kernels": "bun scripts/generate-kernels.ts"
59
- },
60
- "dependencies": {
61
- "@vessel-dsp/compiler": "0.8.1"
62
- },
63
- "publishConfig": {
64
- "access": "public"
65
- }
2
+ "name": "@vessel-dsp/runtime",
3
+ "version": "0.12.0",
4
+ "description": "Real-time MNA simulation runtime and solver console for VesselDSP Program ROMs.",
5
+ "keywords": [
6
+ "guitar-pedal",
7
+ "electronics",
8
+ "simulation",
9
+ "dsp",
10
+ "runtime",
11
+ "solver",
12
+ "mna"
13
+ ],
14
+ "homepage": "https://vessel-dsp.github.io/core/",
15
+ "bugs": {
16
+ "url": "https://github.com/vessel-dsp/core/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/vessel-dsp/core.git"
21
+ },
22
+ "license": "MIT",
23
+ "sideEffects": false,
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "bun": "./dist/index.js",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "./package.json": {
32
+ "default": "./package.json"
33
+ },
34
+ "./wasm/*": {
35
+ "default": "./dist/wasm/*"
36
+ },
37
+ "./worklet.js": {
38
+ "default": "./dist/worklet/v2-audio-worklet.js"
39
+ }
40
+ },
41
+ "main": "./dist/index.js",
42
+ "module": "./dist/index.js",
43
+ "types": "./dist/index.d.ts",
44
+ "files": [
45
+ "dist",
46
+ "README.md",
47
+ "LICENSE.md"
48
+ ],
49
+ "scripts": {
50
+ "build": "tsc -p tsconfig.build.json && bun run ../../scripts/fix-dist-imports.ts dist && rm -rf dist/wasm && cp -r src/wasm dist/wasm && bun scripts/build-worklet.ts",
51
+ "clean": "rm -rf dist",
52
+ "prepack": "bun run --cwd ../core build && bun run --cwd ../compiler build && bun run build:wasm && bun run build",
53
+ "pack:dry-run": "npm pack --dry-run",
54
+ "typecheck": "tsc --noEmit -p tsconfig.json",
55
+ "build:wasm": "bash scripts/build-wasm.sh",
56
+ "build:worklet": "bun scripts/build-worklet.ts",
57
+ "test": "bun test tests",
58
+ "generate-kernels": "bun scripts/generate-kernels.ts"
59
+ },
60
+ "dependencies": {
61
+ "@vessel-dsp/compiler": "0.14.0"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
65
+ }
66
66
  }