@jpdutoit/squelchy 0.0.3 → 0.0.5

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/index.js CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  tape_stop_default,
21
21
  vca_default,
22
22
  waveshaper_default
23
- } from "./chunk-MEZE4P3U.js";
23
+ } from "./chunk-N7J25N5J.js";
24
24
 
25
25
  // src/lib-runtime/core.ts
26
26
  var pendingSources = {};
@@ -28,7 +28,7 @@ var registrations = /* @__PURE__ */ new WeakMap();
28
28
  function preload(ctx, opts) {
29
29
  const existing = registrations.get(ctx);
30
30
  if (existing) return existing;
31
- const sources = opts?.allWorklets ? import("./worklets-inline-DFWINMCS.js").then((m) => m.WORKLET_SOURCES) : Promise.resolve(pendingSources);
31
+ const sources = opts?.allWorklets ? import("./worklets-inline-EF3OTQ4D.js").then((m) => m.WORKLET_SOURCES) : Promise.resolve(pendingSources);
32
32
  const promise = sources.then(
33
33
  (map) => Promise.all(Object.entries(map).map(async ([name, src]) => {
34
34
  const url = URL.createObjectURL(new Blob([src], { type: "text/javascript" }));
@@ -141,7 +141,7 @@ var Instance = class {
141
141
  // src/lib-runtime/time/transport.ts
142
142
  var EPS = 1e-9;
143
143
  function isRest(v) {
144
- return v == null || v === false || v === 0 || v === "." || v === "-";
144
+ return v == null || v === false;
145
145
  }
146
146
  function normStep(s) {
147
147
  if (isRest(s)) return null;
@@ -180,26 +180,29 @@ function createTransport(ctx, opts = {}) {
180
180
  if (at === "now") return now2;
181
181
  if (at === "beat") return Math.ceil(now2 - EPS);
182
182
  if (at === "bar") return Math.ceil(now2 / beatsPerBar - EPS) * beatsPerBar;
183
- if ("bar" in at) return at.bar * beatsPerBar;
183
+ if ("bar" in at) return at.bar * beatsPerBar + (at.beat ?? 0);
184
184
  return at.beat;
185
185
  }
186
186
  const latency = () => ctx.outputLatency || ctx.baseLatency || 0;
187
- function emitTap(hit, local) {
188
- if (!local && taps.size === 0) return;
189
- const fire = () => {
190
- local?.(hit);
191
- for (const cb of taps) cb(hit);
192
- };
187
+ function whenAudible(time, fire) {
193
188
  if (offline) {
194
189
  fire();
195
- return;
190
+ return void 0;
196
191
  }
197
- const delay = Math.max(0, (hit.time - ctx.currentTime + latency()) * 1e3);
192
+ const delay = Math.max(0, (time - ctx.currentTime + latency()) * 1e3);
198
193
  const id = setTimeout(() => {
199
194
  tapTimers.delete(id);
200
195
  fire();
201
196
  }, delay);
202
197
  tapTimers.add(id);
198
+ return id;
199
+ }
200
+ function emitTap(hit, local) {
201
+ if (!local && taps.size === 0) return;
202
+ whenAudible(hit.time, () => {
203
+ local?.(hit);
204
+ for (const cb of taps) cb(hit);
205
+ });
203
206
  }
204
207
  function horizonBeat() {
205
208
  if (offline) return beatAt(ctx.length / ctx.sampleRate);
@@ -320,16 +323,20 @@ function createTransport(ctx, opts = {}) {
320
323
  }
321
324
  };
322
325
  },
323
- at(when, fn) {
326
+ at(when, fn, o = {}) {
324
327
  const b = typeof when === "number" ? when : resolve(when);
325
328
  let fired = false;
329
+ let audibleTimer;
326
330
  const job = {
327
331
  nextBeat: () => b,
328
332
  run(until) {
329
333
  if (fired) return false;
330
334
  if (b < until) {
331
335
  fired = true;
332
- fn(timeAt(b));
336
+ const time = timeAt(b);
337
+ fn(time);
338
+ const onAudible = o.onAudible;
339
+ if (onAudible) audibleTimer = whenAudible(time, () => onAudible(time));
333
340
  return false;
334
341
  }
335
342
  return true;
@@ -340,6 +347,10 @@ function createTransport(ctx, opts = {}) {
340
347
  return () => {
341
348
  fired = true;
342
349
  jobs.delete(job);
350
+ if (audibleTimer !== void 0) {
351
+ clearTimeout(audibleTimer);
352
+ tapTimers.delete(audibleTimer);
353
+ }
343
354
  };
344
355
  },
345
356
  onHit(cb) {
@@ -356,8 +367,13 @@ function createTransport(ctx, opts = {}) {
356
367
  };
357
368
  return t;
358
369
  }
359
- function steps(s) {
360
- return [...s.replace(/[\s|]/g, "")].map((c) => c === "x" || c === "X");
370
+ function steps(s, levels = {}) {
371
+ const velocity = { X: levels.accent ?? 1, x: levels.normal ?? 0.75, o: levels.ghost ?? 0.4 };
372
+ return [...s.replace(/[\s|]/g, "")].map((c) => {
373
+ if (c === "." || c === "-") return null;
374
+ if (!(c in velocity)) throw new Error(`steps(): unknown character "${c}" in "${s}" (X accent, x hit, o ghost, . or - rest)`);
375
+ return { value: true, velocity: velocity[c] };
376
+ });
361
377
  }
362
378
  var NOTE_RE = /^([A-Ga-g])([#b]?)(-?\d+)$/;
363
379
  var PC = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 };
@@ -526,16 +542,28 @@ var asPitch = (n) => n;
526
542
 
527
543
  // src/lib/dsp/pulse.ts
528
544
  var PULSE_S = 1e-3;
529
- function pulse(target, when) {
530
- const ctx = target.context;
531
- const at = Math.max(when ?? 0, ctx.currentTime);
532
- const src = ctx.createConstantSource();
533
- src.offset.value = 1;
545
+ var pulseBuffers = /* @__PURE__ */ new WeakMap();
546
+ function filled(ctx, samples, level) {
547
+ const buf = ctx.createBuffer(1, samples, ctx.sampleRate);
548
+ buf.getChannelData(0).fill(level);
549
+ return buf;
550
+ }
551
+ function play(ctx, buf, target, when) {
552
+ const src = ctx.createBufferSource();
553
+ src.buffer = buf;
534
554
  src.connect(target);
535
- src.start(at);
536
- src.stop(at + PULSE_S);
555
+ src.start(Math.max(0, when ?? 0));
537
556
  src.onended = () => src.disconnect();
538
557
  }
558
+ function pulse(target, when) {
559
+ const ctx = target.context;
560
+ let buf = pulseBuffers.get(ctx);
561
+ if (!buf) {
562
+ buf = filled(ctx, Math.max(1, Math.ceil(ctx.sampleRate * PULSE_S)), 1);
563
+ pulseBuffers.set(ctx, buf);
564
+ }
565
+ play(ctx, buf, target, when);
566
+ }
539
567
 
540
568
  // src/lib/samples/types.ts
541
569
  var VOICE_KEYS = [
@@ -585,23 +613,31 @@ async function loadSampleSet(ctx, set, opts = {}) {
585
613
  };
586
614
  }
587
615
  var SampleVoice = {
588
- create(ctx, kit, key) {
616
+ create(ctx, kit, key, opts = {}) {
589
617
  const out = ctx.createGain();
590
- const buf = kit.buffer(key);
618
+ const root = opts.root ?? 60;
619
+ let current = kit;
620
+ let buf = kit.buffer(key);
591
621
  const trigger = (when, h) => {
592
622
  if (!buf) return;
593
623
  const src = ctx.createBufferSource();
594
624
  const g = ctx.createGain();
595
625
  src.buffer = buf;
596
- if (typeof h?.value === "number") src.playbackRate.value = 2 ** (h.value / 12);
626
+ if (typeof h?.value === "number") src.playbackRate.value = 2 ** ((h.value - root) / 12);
597
627
  g.gain.value = Math.max(0, Math.min(1, h?.velocity ?? 1));
598
628
  src.connect(g).connect(out);
599
629
  src.start(when);
600
- if (h?.duration) src.stop(when + h.duration);
601
630
  };
602
631
  return {
603
632
  output: out,
604
633
  trigger,
634
+ get kit() {
635
+ return current;
636
+ },
637
+ setKit(next) {
638
+ current = next;
639
+ buf = next.buffer(key);
640
+ },
605
641
  connect: (d) => {
606
642
  out.connect(d);
607
643
  return d;
@@ -846,7 +882,7 @@ var params = {
846
882
  resonance: { unit: "factor", default: 8, min: 0, max: 15, scale: "sqrt" },
847
883
  envMod: { unit: "factor", default: 0.7, min: 0, max: 1 },
848
884
  accent: { unit: "factor", default: 0.5, min: 0, max: 1 },
849
- decay: { node: "envSmoother", field: "decay", unit: "s", default: 0.2, min: 0.05, max: 1, scale: "log" }
885
+ decay: { node: "envSmoother", field: "decay", unit: "s", default: 0.92, min: 0.2, max: 5, scale: "log" }
850
886
  };
851
887
  var filter303Descriptor = /* @__PURE__ */ defineDescriptor({
852
888
  type: "303-filter",
@@ -858,7 +894,7 @@ var filter303Descriptor = /* @__PURE__ */ defineDescriptor({
858
894
  "modFreq": { type: "modulation", signal: "modulation", node: "_cutoffVoct" }
859
895
  },
860
896
  createModule(id, position) {
861
- return { id, type: "303-filter", position, ...paramDefaults(params) };
897
+ return { id, type: "303-filter", position, timeUnit: "t60", ...paramDefaults(params) };
862
898
  },
863
899
  createNodes(ctx, module) {
864
900
  const input = ctx.createGain();
@@ -3283,7 +3319,8 @@ function tankEnergyGain500(rt60, size = 1) {
3283
3319
  let g2 = 0;
3284
3320
  for (const ms2 of COMB_TIMES_MS) g2 += Math.pow(10, -6 * (ms2 / 1e3) * size / Math.max(rt60, 0.05));
3285
3321
  g2 /= COMB_TIMES_MS.length;
3286
- return Math.max(0.05, 0.8006 / (1 - g2) - 0.531);
3322
+ const loss = 1 - g2;
3323
+ return Math.max(0.05, 0.6422 / loss - 0.2406 - 11e-4 * loss);
3287
3324
  }
3288
3325
  function createFdnTank(ctx, opts) {
3289
3326
  const input = ctx.createGain();
@@ -3658,14 +3695,47 @@ var saturatorDescriptor = /* @__PURE__ */ defineDescriptor({
3658
3695
  // src/lib-runtime/generated/modules/saturator.wrapper.ts
3659
3696
  var Saturator = /* @__PURE__ */ defineModule(saturatorDescriptor);
3660
3697
 
3661
- // src/lib-runtime/generated/modules/shimmer.tsx
3698
+ // src/lib/dsp/shimmerRegen.ts
3662
3699
  var REGEN_HP_HZ = 300;
3663
3700
  var REGEN_HP_Q_DB = 20 * Math.log10(Math.SQRT1_2);
3664
3701
  var HP_AT_500 = 1 / Math.sqrt(1 + Math.pow(REGEN_HP_HZ / 500, 4));
3665
3702
  var SHIFTER_ENERGY = 0.75;
3666
- function shimmerRegenNorm(decay) {
3667
- return 1 / (Math.sqrt(tankEnergyGain500(decay)) * HP_AT_500 * Math.sqrt(SHIFTER_ENERGY));
3703
+ var REGEN_DELAY_S = 0.02;
3704
+ var SHIFTER_SEEDS = [5370206, 739982445];
3705
+ var UNISON_FLOOR = [
3706
+ [0.5, 0.68],
3707
+ [1, 0.75],
3708
+ [2, 0.76],
3709
+ [3, 0.8],
3710
+ [5, 0.84],
3711
+ [7, 0.75],
3712
+ [10, 0.6]
3713
+ ];
3714
+ var UNISON_PITCH_SPAN = 0.75;
3715
+ function unisonFloor(decay) {
3716
+ const x = Math.log2(Math.max(0.05, decay));
3717
+ const first = UNISON_FLOOR[0], last = UNISON_FLOOR[UNISON_FLOOR.length - 1];
3718
+ if (x <= Math.log2(first[0])) return first[1];
3719
+ if (x >= Math.log2(last[0])) return last[1];
3720
+ for (let i = 1; i < UNISON_FLOOR.length; i++) {
3721
+ const [d1, k1] = UNISON_FLOOR[i];
3722
+ if (x <= Math.log2(d1)) {
3723
+ const [d0, k0] = UNISON_FLOOR[i - 1];
3724
+ const t = (x - Math.log2(d0)) / (Math.log2(d1) - Math.log2(d0));
3725
+ return k0 + (k1 - k0) * t;
3726
+ }
3727
+ }
3728
+ return last[1];
3668
3729
  }
3730
+ function unisonCoherence(decay, pitch) {
3731
+ const a = unisonFloor(decay);
3732
+ return Math.min(1, a + (1 - a) * (pitch / UNISON_PITCH_SPAN) ** 2);
3733
+ }
3734
+ function shimmerRegenNorm(decay, pitch) {
3735
+ return unisonCoherence(decay, pitch) / (Math.sqrt(tankEnergyGain500(decay)) * HP_AT_500 * Math.sqrt(SHIFTER_ENERGY));
3736
+ }
3737
+
3738
+ // src/lib-runtime/generated/modules/shimmer.tsx
3669
3739
  var params36 = {
3670
3740
  decay: { unit: "s", default: 3, min: 0.5, max: 10, scale: "log" },
3671
3741
  pitch: { unit: "semitone", default: 12, min: -12, max: 24 },
@@ -3699,14 +3769,14 @@ var shimmerDescriptor = /* @__PURE__ */ defineDescriptor({
3699
3769
  const shifter = createPitchShifterNode(ctx, module.pitch, seed);
3700
3770
  return { hp, shifter };
3701
3771
  };
3702
- const tapL = mkRegenTap(5370206);
3703
- const tapR = mkRegenTap(739982445);
3772
+ const tapL = mkRegenTap(SHIFTER_SEEDS[0]);
3773
+ const tapR = mkRegenTap(SHIFTER_SEEDS[1]);
3704
3774
  const regenNorm = ctx.createGain();
3705
- regenNorm.gain.value = shimmerRegenNorm(module.decay);
3775
+ regenNorm.gain.value = shimmerRegenNorm(module.decay, module.pitch);
3706
3776
  const regenGain = ctx.createGain();
3707
3777
  regenGain.gain.value = module.shimmer;
3708
3778
  const regenDelay = ctx.createDelay(0.1);
3709
- regenDelay.delayTime.value = 0.02;
3779
+ regenDelay.delayTime.value = REGEN_DELAY_S;
3710
3780
  tank.output.connect(splitter);
3711
3781
  splitter.connect(tapL.hp, 0);
3712
3782
  splitter.connect(tapR.hp, 1);
@@ -3753,9 +3823,10 @@ var shimmerDescriptor = /* @__PURE__ */ defineDescriptor({
3753
3823
  for (const key of ["pitchShifterL", "pitchShifterR"]) {
3754
3824
  nodes[key].parameters.get("semitones").setTargetAtTime(value, now2, 0.02);
3755
3825
  }
3826
+ nodes.regenNorm.gain.setTargetAtTime(shimmerRegenNorm(_m.decay, value), now2, 0.02);
3756
3827
  } else if (param === "decay") {
3757
3828
  setTankDecay({ node: nodes.tankNode }, value, now2);
3758
- nodes.regenNorm.gain.setTargetAtTime(shimmerRegenNorm(value), now2, 0.02);
3829
+ nodes.regenNorm.gain.setTargetAtTime(shimmerRegenNorm(value, _m.pitch), now2, 0.02);
3759
3830
  }
3760
3831
  }
3761
3832
  });