@kidlib/web-audio 0.1.2 → 0.1.4
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 +2 -0
- package/dist/index.d.ts +29 -7
- package/dist/index.js +208 -176
- package/dist/processors/processors.js +20 -20
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -1035,15 +1035,12 @@ async function Je(e, t, n = {}) {
|
|
|
1035
1035
|
}
|
|
1036
1036
|
}
|
|
1037
1037
|
if (o?.enabled && (f = c(e, f, o.maxAmplitudePeak)), s?.enabled) {
|
|
1038
|
-
let t =
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
makeupGain: s.makeupGain ?? 1
|
|
1045
|
-
} : t.suggestedSettings, f = Ge(e, f, r.threshold, r.ratio, r.makeupGain);
|
|
1046
|
-
}
|
|
1038
|
+
let t = s.threshold !== void 0 || s.ratio !== void 0 || s.makeupGain !== void 0 ? {
|
|
1039
|
+
threshold: s.threshold ?? .5,
|
|
1040
|
+
ratio: s.ratio ?? 2,
|
|
1041
|
+
makeupGain: s.makeupGain ?? 1
|
|
1042
|
+
} : Ke(f).suggestedSettings;
|
|
1043
|
+
t && (f = Ge(e, f, t.threshold, t.ratio, t.makeupGain));
|
|
1047
1044
|
}
|
|
1048
1045
|
if (d?.detectPitch || d?.autotune || i && "auto" in i && i.auto) {
|
|
1049
1046
|
let e = await Xe(f), t = d?.targetMidiNote || 60, n = Ze(e.midiFloat, t);
|
|
@@ -3331,7 +3328,7 @@ var Z = {
|
|
|
3331
3328
|
async init() {
|
|
3332
3329
|
return this.#t ||= (async () => {
|
|
3333
3330
|
try {
|
|
3334
|
-
this.#m && this.#T(), this.#o = new ht(this.context), this.#a = new GainNode(this.context, { gain: 1 }), this.#
|
|
3331
|
+
this.#m && this.#T(), this.#o = new ht(this.context), this.#a = new GainNode(this.context, { gain: 1 }), this.#j(), this.setParam("loopStart", 0, this.now), this.setParam("loopEnd", 0, this.now), this.#w(), this.#E(), this.#P(), this.#r.port.start();
|
|
3335
3332
|
} catch (e) {
|
|
3336
3333
|
throw this.dispose(), this.#t = null, e;
|
|
3337
3334
|
}
|
|
@@ -3364,30 +3361,29 @@ var Z = {
|
|
|
3364
3361
|
});
|
|
3365
3362
|
this.#s.set("filter-env", t);
|
|
3366
3363
|
}
|
|
3367
|
-
this.#
|
|
3364
|
+
this.#N();
|
|
3368
3365
|
}
|
|
3369
3366
|
async loadBuffer(e, t) {
|
|
3370
|
-
|
|
3371
|
-
|
|
3367
|
+
return this.loadLayers([e], t);
|
|
3368
|
+
}
|
|
3369
|
+
async loadLayers(e, t) {
|
|
3370
|
+
this.#c = Z.NOT_READY;
|
|
3371
|
+
let n = e.filter((e) => e.sampleRate === this.context.sampleRate || (console.warn(`Sample rate mismatch - buffer: ${e.sampleRate}, context: ${this.context.sampleRate}`), !1));
|
|
3372
|
+
if (!n.length || n[0] !== e[0]) return console.error("SampleVoice.loadLayers: layer 0 is unusable, nothing loaded. Layer 0 sets duration and loop range for all layers."), !1;
|
|
3373
|
+
let r = n.map((e) => Array.from({ length: e.numberOfChannels }, (t, n) => e.getChannelData(n)));
|
|
3372
3374
|
return this.sendToProcessor({
|
|
3373
|
-
type: "voice:
|
|
3374
|
-
|
|
3375
|
-
durationSeconds:
|
|
3376
|
-
}), t?.length &&
|
|
3375
|
+
type: "voice:setLayers",
|
|
3376
|
+
layers: r,
|
|
3377
|
+
durationSeconds: n[0].duration
|
|
3378
|
+
}), t?.length && this.sendToProcessor({
|
|
3377
3379
|
type: "voice:setZeroCrossings",
|
|
3378
3380
|
zeroCrossings: t
|
|
3379
|
-
})
|
|
3381
|
+
}), !0;
|
|
3380
3382
|
}
|
|
3381
3383
|
freeze(e) {
|
|
3382
3384
|
return console.info(`SampleVoice: freeze(${e}) called.
|
|
3383
3385
|
Spectral freeze not implemented yet`), this;
|
|
3384
3386
|
}
|
|
3385
|
-
#D(e) {
|
|
3386
|
-
return this.sendToProcessor({
|
|
3387
|
-
type: "voice:set_zero_crossings",
|
|
3388
|
-
zeroCrossings: e
|
|
3389
|
-
}), this;
|
|
3390
|
-
}
|
|
3391
3387
|
setGlideTime(e) {
|
|
3392
3388
|
this.#p = e;
|
|
3393
3389
|
}
|
|
@@ -3396,7 +3392,7 @@ var Z = {
|
|
|
3396
3392
|
if (this.#c === Z.PLAYING || this.#c === Z.RELEASING) return console.log(`had to stop a playing voice, midinote: ${t}`), this.stop(i), null;
|
|
3397
3393
|
this.#c = Z.PLAYING, this.#d = i, this.#u = t;
|
|
3398
3394
|
let a = (e.glide?.glideTime ?? this.#p) / 8, o = 1, s = 1;
|
|
3399
|
-
if (this.#h || (o = v(t), e.glide && (s = v(e.glide.prevMidiNote)), this.#
|
|
3395
|
+
if (this.#h || (o = v(t), e.glide && (s = v(e.glide.prevMidiNote)), this.#k(o, i, { glideTime: a }), this.#A(o, i, { glideTime: a })), !this.#h && e.glide && a > 0) {
|
|
3400
3396
|
let e = this.getParam("playbackRate");
|
|
3401
3397
|
s > 0 && e.setValueAtTime(s, i), this.getParam("playbackRate").setTargetAtTime(o, i, a);
|
|
3402
3398
|
} else this.setParam("playbackRate", o, i);
|
|
@@ -3451,8 +3447,8 @@ var Z = {
|
|
|
3451
3447
|
}
|
|
3452
3448
|
});
|
|
3453
3449
|
}
|
|
3450
|
+
#D = null;
|
|
3454
3451
|
#O = null;
|
|
3455
|
-
#k = null;
|
|
3456
3452
|
release({ releaseTime: e = this.releaseTime, secondsFromNow: t = 0 }) {
|
|
3457
3453
|
if (this.#c === Z.RELEASING) return this;
|
|
3458
3454
|
if (!this.getParam("envGain")) throw Error("Cannot release - envGain parameter is null");
|
|
@@ -3472,11 +3468,11 @@ var Z = {
|
|
|
3472
3468
|
timestamp: n
|
|
3473
3469
|
});
|
|
3474
3470
|
let i = Array.from(this.#s.values()).filter((e) => e.isEnabled), a = i.length > 0 ? Math.max(...i.map((e) => e.effectiveReleaseDuration)) : e;
|
|
3475
|
-
return this.#
|
|
3471
|
+
return this.#D && clearTimeout(this.#D), this.#D = setTimeout(() => {
|
|
3476
3472
|
try {
|
|
3477
3473
|
(this.#c === Z.RELEASING || this.#c === Z.PLAYING) && this.stop();
|
|
3478
3474
|
} finally {
|
|
3479
|
-
this.#
|
|
3475
|
+
this.#D = null;
|
|
3480
3476
|
}
|
|
3481
3477
|
}, a * 1e3 + 50), this;
|
|
3482
3478
|
}
|
|
@@ -3484,35 +3480,35 @@ var Z = {
|
|
|
3484
3480
|
if (this.#c === Z.STOPPED || this.#c === Z.STOPPING) return this;
|
|
3485
3481
|
this.#c = Z.STOPPING;
|
|
3486
3482
|
let t = .005, n = Math.max(e, this.now), r = this.getParam("envGain");
|
|
3487
|
-
return r && (T(r, n), r.linearRampToValueAtTime(0, n + t)), this.#
|
|
3483
|
+
return r && (T(r, n), r.linearRampToValueAtTime(0, n + t)), this.#O && clearTimeout(this.#O), this.#O = setTimeout(() => {
|
|
3488
3484
|
this.sendToProcessor({
|
|
3489
3485
|
type: "voice:stop",
|
|
3490
3486
|
timestamp: n
|
|
3491
|
-
}), this.#
|
|
3487
|
+
}), this.#O = null;
|
|
3492
3488
|
}, Math.max(0, (n + t - this.now) * 1e3)), this;
|
|
3493
3489
|
}
|
|
3494
|
-
#
|
|
3490
|
+
#k(e, t = this.now, n = {}) {
|
|
3495
3491
|
if (!this.#u || !this.#g || this.#C <= 0) return;
|
|
3496
3492
|
let r = this.#g.frequency, { glideTime: i = 0, cancelPrevious: a = !0 } = n || {};
|
|
3497
3493
|
a && r.cancelScheduledValues(t);
|
|
3498
3494
|
let o = O(this.#v * e * this.#C, 20, this.context.sampleRate / 2 - 1e3);
|
|
3499
3495
|
i > 0 ? r.setTargetAtTime(o, t, i) : r.setValueAtTime(o, Math.max(t, this.now + .001));
|
|
3500
3496
|
}
|
|
3501
|
-
#
|
|
3497
|
+
#A(e, t = this.now, n = {}) {
|
|
3502
3498
|
if (!this.#u || !this.#_ || this.#S <= 0) return;
|
|
3503
3499
|
let r = this.#_.frequency, { glideTime: i = 0, cancelPrevious: a = !0 } = n || {};
|
|
3504
3500
|
a && r.cancelScheduledValues(t);
|
|
3505
3501
|
let o = O(this.#b * e * this.#S, 20, this.context.sampleRate / 2 - 1e3);
|
|
3506
3502
|
i > 0 ? r.setTargetAtTime(o, t, i) : r.setValueAtTime(o, Math.max(t, this.now + .001));
|
|
3507
3503
|
}
|
|
3508
|
-
#
|
|
3504
|
+
#j(e = 0, t = "square", n = {}) {
|
|
3509
3505
|
if (this.#i === null) {
|
|
3510
3506
|
if (this.#i = new J(this.context), this.#i.setWaveform(t, n), this.#i.setDepth(e), this.#i.setMusicalNote(this.#u ?? 60), this.#a) this.#i.connect(this.#a.gain);
|
|
3511
3507
|
else throw console.error("Missing gain node for AM-LFO in SampleVoice"), Error("Missing gain node for AM-LFO in SampleVoice");
|
|
3512
3508
|
} else console.debug("setupAmpModLFO: LFO already setup: ", this.#i);
|
|
3513
3509
|
return this;
|
|
3514
3510
|
}
|
|
3515
|
-
#
|
|
3511
|
+
#M() {
|
|
3516
3512
|
if (this.#i) return this.#i.dispose(), this.#i = null, this;
|
|
3517
3513
|
}
|
|
3518
3514
|
setModulationAmount(e, t) {
|
|
@@ -3520,10 +3516,10 @@ var Z = {
|
|
|
3520
3516
|
warn: !0,
|
|
3521
3517
|
name: "sampleVoice.setModulationAmount"
|
|
3522
3518
|
});
|
|
3523
|
-
return e === "AM" ? (this.#i || this.#
|
|
3519
|
+
return e === "AM" ? (this.#i || this.#j(n), this.#i?.setDepth(n)) : e === "FM" && console.warn("SampleVoice: FM modulation not implemented yet"), this;
|
|
3524
3520
|
}
|
|
3525
3521
|
setModulationWaveform(e = "AM", t = "triangle", n = {}) {
|
|
3526
|
-
return e === "AM" ? (this.#i || this.#
|
|
3522
|
+
return e === "AM" ? (this.#i || this.#j(), this.#i?.setWaveform(t, n)) : e === "FM" && console.info("SampleVoice: FM modulation not implemented yet"), this;
|
|
3527
3523
|
}
|
|
3528
3524
|
enableEnvelope = (e) => {
|
|
3529
3525
|
this.#s.get(e)?.enable();
|
|
@@ -3612,14 +3608,14 @@ var Z = {
|
|
|
3612
3608
|
disablePitch = () => {
|
|
3613
3609
|
this.#h = !0;
|
|
3614
3610
|
let e = this.now, t = .1;
|
|
3615
|
-
this.getParam("playbackRate")?.linearRampToValueAtTime(1, e + t), this.#
|
|
3611
|
+
this.getParam("playbackRate")?.linearRampToValueAtTime(1, e + t), this.#k(1, e, { glideTime: t }), this.#A(1, e, { glideTime: t });
|
|
3616
3612
|
};
|
|
3617
3613
|
enablePitch = () => {
|
|
3618
3614
|
this.#h = !1;
|
|
3619
3615
|
let e = this.now, t = .1;
|
|
3620
3616
|
if (this.#u) {
|
|
3621
3617
|
let n = v(this.#u);
|
|
3622
|
-
this.getParam("playbackRate")?.linearRampToValueAtTime(n, this.context.currentTime + .01), this.#
|
|
3618
|
+
this.getParam("playbackRate")?.linearRampToValueAtTime(n, this.context.currentTime + .01), this.#k(n, e, { glideTime: t }), this.#A(n, e, { glideTime: t });
|
|
3623
3619
|
}
|
|
3624
3620
|
};
|
|
3625
3621
|
connect(e, t, n) {
|
|
@@ -3637,7 +3633,7 @@ var Z = {
|
|
|
3637
3633
|
sendUpstreamMessage(e, t) {
|
|
3638
3634
|
return this.#e.sendMessage(e, t), this;
|
|
3639
3635
|
}
|
|
3640
|
-
#
|
|
3636
|
+
#N() {
|
|
3641
3637
|
this.#s.forEach((e, t) => {
|
|
3642
3638
|
this.#e.forwardFrom(e, [
|
|
3643
3639
|
`${t}:trigger`,
|
|
@@ -3651,7 +3647,7 @@ var Z = {
|
|
|
3651
3647
|
}));
|
|
3652
3648
|
});
|
|
3653
3649
|
}
|
|
3654
|
-
#
|
|
3650
|
+
#P() {
|
|
3655
3651
|
this.#r.port.onmessage = (e) => {
|
|
3656
3652
|
let { type: t, ...n } = e.data;
|
|
3657
3653
|
switch (t) {
|
|
@@ -3775,14 +3771,14 @@ var Z = {
|
|
|
3775
3771
|
setEnvelopeLoop = (e, t, n = "normal") => (this.#s.get(e)?.setLoopEnabled(t, n), this);
|
|
3776
3772
|
syncEnvelopeToPlaybackRate = (e, t) => (this.#s.get(e)?.syncToPlaybackRate(t), this);
|
|
3777
3773
|
setPlaybackRate(e, t = this.now, n) {
|
|
3778
|
-
return this.setParam("playbackRate", e, t, n), this.#
|
|
3774
|
+
return this.setParam("playbackRate", e, t, n), this.#k(e, t, n), this.#A(e, t, n), this;
|
|
3779
3775
|
}
|
|
3780
3776
|
setHpfCutoff(e, t = this.now, n = {}) {
|
|
3781
3777
|
let r = O(e, 20, this.context.sampleRate / 2 - 1e3);
|
|
3782
3778
|
if (this.#v = r, this.#g) {
|
|
3783
3779
|
this.setParam("hpf", r, t, { glideTime: 0 });
|
|
3784
3780
|
let e = this.getParam("playbackRate")?.value ?? 1;
|
|
3785
|
-
this.#
|
|
3781
|
+
this.#k(e, t, n);
|
|
3786
3782
|
}
|
|
3787
3783
|
return this;
|
|
3788
3784
|
}
|
|
@@ -3794,7 +3790,7 @@ var Z = {
|
|
|
3794
3790
|
cancelPrevious: !0
|
|
3795
3791
|
});
|
|
3796
3792
|
let e = this.getParam("playbackRate")?.value ?? 1;
|
|
3797
|
-
this.#
|
|
3793
|
+
this.#A(e, t, n);
|
|
3798
3794
|
}
|
|
3799
3795
|
return this;
|
|
3800
3796
|
}
|
|
@@ -3838,7 +3834,7 @@ var Z = {
|
|
|
3838
3834
|
`);
|
|
3839
3835
|
}
|
|
3840
3836
|
dispose() {
|
|
3841
|
-
this.stop(), this.disconnect(), this.#
|
|
3837
|
+
this.stop(), this.disconnect(), this.#M(), this.#s.forEach((e) => e.dispose()), this.#r.port.close(), this.#D && clearTimeout(this.#D), this.#O && clearTimeout(this.#O), B(this.nodeId);
|
|
3842
3838
|
}
|
|
3843
3839
|
getParam(e) {
|
|
3844
3840
|
if (this.#r && this.#r.parameters.has(e)) return this.#r.parameters.get(e) ?? null;
|
|
@@ -3959,8 +3955,8 @@ var xt = class {
|
|
|
3959
3955
|
type: "sample:loaded"
|
|
3960
3956
|
} : null) : e);
|
|
3961
3957
|
}
|
|
3962
|
-
|
|
3963
|
-
return this.#o.clear(), this.#a.forEach((n) => n.
|
|
3958
|
+
setLayers(e, t) {
|
|
3959
|
+
return this.#o.clear(), this.#a.forEach((n) => n.loadLayers(e, t)), this;
|
|
3964
3960
|
}
|
|
3965
3961
|
allocate(e = this.#s, t = this.#l) {
|
|
3966
3962
|
let n;
|
|
@@ -4064,7 +4060,7 @@ async function St(e, t) {
|
|
|
4064
4060
|
}
|
|
4065
4061
|
//#endregion
|
|
4066
4062
|
//#region src/nodes/instruments/Sample/SamplePlayer.ts
|
|
4067
|
-
var Ct = class {
|
|
4063
|
+
var Ct = class e {
|
|
4068
4064
|
nodeId;
|
|
4069
4065
|
nodeType = "sample-player";
|
|
4070
4066
|
context;
|
|
@@ -4077,52 +4073,54 @@ var Ct = class {
|
|
|
4077
4073
|
#o = /* @__PURE__ */ new Set();
|
|
4078
4074
|
#s = /* @__PURE__ */ new Set();
|
|
4079
4075
|
#c = null;
|
|
4080
|
-
#l =
|
|
4081
|
-
|
|
4076
|
+
#l = [];
|
|
4077
|
+
static MAX_LAYERS = 4;
|
|
4078
|
+
#u = 0;
|
|
4082
4079
|
#d = !1;
|
|
4083
4080
|
#f = !1;
|
|
4084
4081
|
#p = !1;
|
|
4085
4082
|
#m = !1;
|
|
4086
|
-
#h;
|
|
4083
|
+
#h = !1;
|
|
4087
4084
|
#g;
|
|
4088
4085
|
#_;
|
|
4089
|
-
#v
|
|
4086
|
+
#v;
|
|
4090
4087
|
#y = null;
|
|
4091
|
-
#b =
|
|
4092
|
-
#x =
|
|
4093
|
-
#S =
|
|
4094
|
-
#C = q.
|
|
4095
|
-
#w = q.
|
|
4096
|
-
#T = q.
|
|
4097
|
-
#E = q.
|
|
4098
|
-
#D =
|
|
4099
|
-
#O =
|
|
4100
|
-
#k =
|
|
4101
|
-
#A =
|
|
4088
|
+
#b = null;
|
|
4089
|
+
#x = 0;
|
|
4090
|
+
#S = 120;
|
|
4091
|
+
#C = q.glide.defaultValue;
|
|
4092
|
+
#w = q.loopRampDuration.defaultValue;
|
|
4093
|
+
#T = q.keytrackLoop.defaultValue;
|
|
4094
|
+
#E = q.highpassFilter.defaultValue;
|
|
4095
|
+
#D = q.lowpassFilter.defaultValue;
|
|
4096
|
+
#O = !1;
|
|
4097
|
+
#k = 300;
|
|
4098
|
+
#A = 20;
|
|
4102
4099
|
#j = !1;
|
|
4103
|
-
#M =
|
|
4104
|
-
#N =
|
|
4100
|
+
#M = !1;
|
|
4101
|
+
#N = [];
|
|
4105
4102
|
#P = !0;
|
|
4103
|
+
#F = !0;
|
|
4106
4104
|
randomizeVelocity = !1;
|
|
4107
4105
|
voicePool;
|
|
4108
4106
|
outBus;
|
|
4109
|
-
#
|
|
4107
|
+
#I = /* @__PURE__ */ new Set();
|
|
4110
4108
|
constructor(e, t = 16, n) {
|
|
4111
|
-
this.nodeId = z("sample-player", this), this.context = e, this.#e = V(this.nodeId), this.#
|
|
4109
|
+
this.nodeId = z("sample-player", this), this.context = e, this.#e = V(this.nodeId), this.#g = new GainNode(this.context, { gain: .5 }), this.#_ = new ot(this.context, 0), this.#v = new ot(this.context, 0), this.#i = t, this.#a = n || null;
|
|
4112
4110
|
}
|
|
4113
4111
|
async init() {
|
|
4114
4112
|
if (!this.#t) return this.#n ||= (async () => {
|
|
4115
4113
|
try {
|
|
4116
|
-
this.outBus = await _t(this.context), this.voicePool = await St(this.context, this.#i), this.#
|
|
4114
|
+
this.outBus = await _t(this.context), this.voicePool = await St(this.context, this.#i), this.#B(), this.#L(), this.#z(), this.#V(), this.#R(), this.#a && await this.loadSample(this.#a, void 0, { skipPreProcessing: !0 }), this.#t = !0;
|
|
4117
4115
|
} catch (e) {
|
|
4118
|
-
this.voicePool?.dispose(), this.#
|
|
4116
|
+
this.voicePool?.dispose(), this.#_?.dispose(), this.#v?.dispose();
|
|
4119
4117
|
let t = e instanceof Error ? e.message : String(e);
|
|
4120
4118
|
throw Error(`Failed to initialize SamplePlayer: ${t}`);
|
|
4121
4119
|
}
|
|
4122
4120
|
})(), this.#n;
|
|
4123
4121
|
}
|
|
4124
|
-
#
|
|
4125
|
-
this.voicePool.connect(this.outBus.input), this.outBus.connect(this.#
|
|
4122
|
+
#L() {
|
|
4123
|
+
this.voicePool.connect(this.outBus.input), this.outBus.connect(this.#g), this.#g.connect(this.context.destination);
|
|
4126
4124
|
}
|
|
4127
4125
|
onMessage(e, t) {
|
|
4128
4126
|
return this.#e.onMessage(e, t);
|
|
@@ -4132,13 +4130,13 @@ var Ct = class {
|
|
|
4132
4130
|
}
|
|
4133
4131
|
connect(e) {
|
|
4134
4132
|
let t = "input" in e && e.input ? e.input : e;
|
|
4135
|
-
this.#
|
|
4133
|
+
this.#g.connect(t), "nodeId" in e && (this.#o.add(e.nodeId), e.addIncoming?.(this.nodeId));
|
|
4136
4134
|
}
|
|
4137
4135
|
disconnect(e) {
|
|
4138
4136
|
if (e) {
|
|
4139
4137
|
let t = "input" in e ? e.input : e;
|
|
4140
|
-
this.#
|
|
4141
|
-
} else this.#
|
|
4138
|
+
this.#g.disconnect(t), "nodeId" in e && (this.#o.delete(e.nodeId), e.removeIncoming?.(this.nodeId));
|
|
4139
|
+
} else this.#g.disconnect(), this.#o.clear();
|
|
4142
4140
|
}
|
|
4143
4141
|
addIncoming(e) {
|
|
4144
4142
|
this.#s.add(e.nodeId);
|
|
@@ -4153,13 +4151,13 @@ var Ct = class {
|
|
|
4153
4151
|
};
|
|
4154
4152
|
}
|
|
4155
4153
|
get audioNode() {
|
|
4156
|
-
return this.#
|
|
4154
|
+
return this.#g;
|
|
4157
4155
|
}
|
|
4158
4156
|
get input() {
|
|
4159
4157
|
return this.outBus.input;
|
|
4160
4158
|
}
|
|
4161
4159
|
get output() {
|
|
4162
|
-
return this.#
|
|
4160
|
+
return this.#g;
|
|
4163
4161
|
}
|
|
4164
4162
|
get now() {
|
|
4165
4163
|
return this.context.currentTime;
|
|
@@ -4167,7 +4165,7 @@ var Ct = class {
|
|
|
4167
4165
|
get initialized() {
|
|
4168
4166
|
return this.#t;
|
|
4169
4167
|
}
|
|
4170
|
-
#
|
|
4168
|
+
#R() {
|
|
4171
4169
|
return this.voicePool.onMessage("sample:loaded", (e) => {
|
|
4172
4170
|
this.#r = !0;
|
|
4173
4171
|
}), this.voicePool.onMessage("voice-pool:initialized", () => {
|
|
@@ -4194,26 +4192,26 @@ var Ct = class {
|
|
|
4194
4192
|
}
|
|
4195
4193
|
getMacrosAudioParam(e) {
|
|
4196
4194
|
switch (e) {
|
|
4197
|
-
case "loopStart": return this.#
|
|
4198
|
-
case "loopEnd": return this.#
|
|
4195
|
+
case "loopStart": return this.#_.audioParam;
|
|
4196
|
+
case "loopEnd": return this.#v.audioParam;
|
|
4199
4197
|
default: throw Error(`Unknown macro parameter: ${e}`);
|
|
4200
4198
|
}
|
|
4201
4199
|
}
|
|
4202
4200
|
getMacro(e) {
|
|
4203
4201
|
switch (e) {
|
|
4204
|
-
case "loopStart": return this.#
|
|
4205
|
-
case "loopEnd": return this.#
|
|
4202
|
+
case "loopStart": return this.#_;
|
|
4203
|
+
case "loopEnd": return this.#v;
|
|
4206
4204
|
default: throw Error(`Unknown macro parameter: ${e}`);
|
|
4207
4205
|
}
|
|
4208
4206
|
}
|
|
4209
|
-
#
|
|
4207
|
+
#z() {
|
|
4210
4208
|
return this.voicePool.allVoices.forEach((e, t) => {
|
|
4211
4209
|
let n = e.getParam("loopStart"), r = e.getParam("loopEnd");
|
|
4212
|
-
n ? this.#
|
|
4210
|
+
n ? this.#_.addTarget(n, "loopStart") : console.error("loopStart param is null!"), r ? this.#v.addTarget(r, "loopEnd") : console.error("loopEnd param is null!");
|
|
4213
4211
|
}), this;
|
|
4214
4212
|
}
|
|
4215
|
-
#
|
|
4216
|
-
return this.#
|
|
4213
|
+
#B() {
|
|
4214
|
+
return this.#_.setValue(0), this.#v.setValue(this.#u), this;
|
|
4217
4215
|
}
|
|
4218
4216
|
setModulationAmount = (e, t) => this.voicePool.applyToAllVoices((n) => n.setModulationAmount(e, t));
|
|
4219
4217
|
setModulationWaveform(e = "AM", t = "triangle", n = {}) {
|
|
@@ -4221,14 +4219,6 @@ var Ct = class {
|
|
|
4221
4219
|
}
|
|
4222
4220
|
syncLFOsToNoteFreq(e, t) {
|
|
4223
4221
|
if (e === "gain-lfo") {
|
|
4224
|
-
if (t === !0) this.#v?.storeCurrentValues();
|
|
4225
|
-
else {
|
|
4226
|
-
let e = this.#v?.getStoredValues();
|
|
4227
|
-
e && this.#v?.setFrequency(e.rate);
|
|
4228
|
-
}
|
|
4229
|
-
this.#A = t;
|
|
4230
|
-
}
|
|
4231
|
-
if (e === "pitch-lfo") {
|
|
4232
4222
|
if (t === !0) this.#y?.storeCurrentValues();
|
|
4233
4223
|
else {
|
|
4234
4224
|
let e = this.#y?.getStoredValues();
|
|
@@ -4236,13 +4226,21 @@ var Ct = class {
|
|
|
4236
4226
|
}
|
|
4237
4227
|
this.#j = t;
|
|
4238
4228
|
}
|
|
4229
|
+
if (e === "pitch-lfo") {
|
|
4230
|
+
if (t === !0) this.#b?.storeCurrentValues();
|
|
4231
|
+
else {
|
|
4232
|
+
let e = this.#b?.getStoredValues();
|
|
4233
|
+
e && this.#b?.setFrequency(e.rate);
|
|
4234
|
+
}
|
|
4235
|
+
this.#M = t;
|
|
4236
|
+
}
|
|
4239
4237
|
}
|
|
4240
|
-
#
|
|
4241
|
-
this.#
|
|
4242
|
-
let e = this.#
|
|
4243
|
-
this.#
|
|
4238
|
+
#V() {
|
|
4239
|
+
this.#y = new J(this.context), this.#y.setWaveform("sine"), this.#b = new J(this.context);
|
|
4240
|
+
let e = this.#b.getPitchWobbleWaveform();
|
|
4241
|
+
this.#b.setWaveform(e), this.#H(this.#b, "playbackRate"), this.#y.connect(this.outBus.input.gain);
|
|
4244
4242
|
}
|
|
4245
|
-
#
|
|
4243
|
+
#H(e, t) {
|
|
4246
4244
|
this.voicePool.applyToAllVoices((n) => {
|
|
4247
4245
|
let r = n.getParam(t);
|
|
4248
4246
|
r && e.connect(r);
|
|
@@ -4251,32 +4249,65 @@ var Ct = class {
|
|
|
4251
4249
|
freezeActiveVoices(e) {
|
|
4252
4250
|
return console.info(`SamplePlayer: freezeActiveVoices(${e}). Spectral freeze not implemented yet`), this;
|
|
4253
4251
|
}
|
|
4254
|
-
#
|
|
4252
|
+
#U = !1;
|
|
4255
4253
|
async loadSample(e, t, n) {
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4254
|
+
return (await this.loadLayers([e], t, n))?.[0] ?? null;
|
|
4255
|
+
}
|
|
4256
|
+
async loadLayers(t, n, r) {
|
|
4257
|
+
if (this.#U) throw Error("A sample load is already in progress");
|
|
4258
|
+
this.#U = !0;
|
|
4259
|
+
let i;
|
|
4259
4260
|
try {
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4261
|
+
t.length > e.MAX_LAYERS && (console.warn(`Ignoring layers past ${e.MAX_LAYERS}; got ${t.length}`), t = t.slice(0, e.MAX_LAYERS));
|
|
4262
|
+
let a = [];
|
|
4263
|
+
for (let [e, n] of t.entries()) {
|
|
4264
|
+
let t = n;
|
|
4265
|
+
if (t instanceof ArrayBuffer) try {
|
|
4266
|
+
t = await this.context.decodeAudioData(t.slice(0));
|
|
4267
|
+
} catch (t) {
|
|
4268
|
+
if (e === 0) throw t;
|
|
4269
|
+
console.warn(`Failed to decode layer ${e}; skipping`, t);
|
|
4270
|
+
continue;
|
|
4271
|
+
}
|
|
4272
|
+
if (!o(t)) {
|
|
4273
|
+
if (console.error(`Invalid AudioBuffer provided for layer ${e}`), e === 0) return null;
|
|
4274
|
+
continue;
|
|
4275
|
+
}
|
|
4276
|
+
if (t.sampleRate !== this.context.sampleRate) {
|
|
4277
|
+
let n = `Sample rate mismatch: layer ${e} rate ${t.sampleRate}, context rate ${this.context.sampleRate}`;
|
|
4278
|
+
if (e === 0) throw RangeError(n);
|
|
4279
|
+
console.warn(n);
|
|
4280
|
+
continue;
|
|
4281
|
+
}
|
|
4282
|
+
a.push(t);
|
|
4283
|
+
}
|
|
4284
|
+
if (!a.length) return null;
|
|
4285
|
+
n && this.context.sampleRate !== n && console.warn(`Sample rate mismatch: context rate ${this.context.sampleRate}, requested rate ${n}`);
|
|
4286
|
+
let s = [], c = [];
|
|
4287
|
+
for (let [e, t] of a.entries()) {
|
|
4288
|
+
if (!this.#F) {
|
|
4289
|
+
s.push(t);
|
|
4290
|
+
continue;
|
|
4291
|
+
}
|
|
4292
|
+
let n = await Je(this.context, t, r);
|
|
4293
|
+
s.push(n.audiobuffer), e === 0 && this.#P && n.zeroCrossings && (c = n.zeroCrossings);
|
|
4294
|
+
}
|
|
4295
|
+
this.releaseAll(0), this.transposeSemitones = 0, this.#r = !1, this.#l = s, this.#c = s[0], this.#u = s[0].duration, this.#N = c;
|
|
4296
|
+
let l = new Promise((e) => {
|
|
4297
|
+
i = this.voicePool.onMessage("sample:loaded", () => {
|
|
4267
4298
|
e();
|
|
4268
4299
|
});
|
|
4269
4300
|
});
|
|
4270
|
-
return this.voicePool.
|
|
4301
|
+
return this.voicePool.setLayers(s, c), this.#B(), this.setScale({
|
|
4271
4302
|
rootNote: "C",
|
|
4272
4303
|
scale: [0],
|
|
4273
4304
|
lowestOctave: 0,
|
|
4274
4305
|
highestOctave: 5,
|
|
4275
4306
|
tuningOffset: 0,
|
|
4276
4307
|
normalize: !1
|
|
4277
|
-
}), await
|
|
4308
|
+
}), await l, [...s];
|
|
4278
4309
|
} finally {
|
|
4279
|
-
|
|
4310
|
+
i?.(), this.#U = !1;
|
|
4280
4311
|
}
|
|
4281
4312
|
}
|
|
4282
4313
|
async cropSample(e = this.getStartPoint(), t = this.getEndPoint(), n = 4) {
|
|
@@ -4311,39 +4342,39 @@ var Ct = class {
|
|
|
4311
4342
|
return n;
|
|
4312
4343
|
}
|
|
4313
4344
|
play(e, t = 100, n = this.getGlideTime()) {
|
|
4314
|
-
let r = he(t) ? t : 100, i = e + this.#
|
|
4315
|
-
return he(i) ? (this.#
|
|
4345
|
+
let r = he(t) ? t : 100, i = e + this.#x;
|
|
4346
|
+
return he(i) ? (this.#j && this.#y?.setMusicalNote(i), this.#M && this.#b?.setMusicalNote(i, { divisor: 4 }), this.outBus.noteOn(i, r, 0, n), this.voicePool.noteOn(i, r, 0, n)) : (console.warn(`Invalid midiNote: ${i}`), null);
|
|
4316
4347
|
}
|
|
4317
4348
|
release(e) {
|
|
4318
|
-
if (this.holdEnabled || this.#
|
|
4319
|
-
let t = e + this.#
|
|
4320
|
-
return this.#
|
|
4349
|
+
if (this.holdEnabled || this.#m) return this;
|
|
4350
|
+
let t = e + this.#x;
|
|
4351
|
+
return this.#h ? (this.#I.add(t), this) : (this.#I.delete(t), this.voicePool.noteOff(t), this.sendUpstreamMessage("note:off", { transposedMidiNote: t }), this);
|
|
4321
4352
|
}
|
|
4322
4353
|
releaseAll(e) {
|
|
4323
|
-
return this.#
|
|
4354
|
+
return this.#I.clear(), this.voicePool?.allNotesOff(e), this;
|
|
4324
4355
|
}
|
|
4325
4356
|
panic = (e) => this.releaseAll(e);
|
|
4326
4357
|
get transposedBySemitones() {
|
|
4327
|
-
return this.#
|
|
4358
|
+
return this.#x;
|
|
4328
4359
|
}
|
|
4329
4360
|
set transposeSemitones(e) {
|
|
4330
|
-
this.#
|
|
4361
|
+
this.#x !== e && (this.#x = e);
|
|
4331
4362
|
}
|
|
4332
4363
|
setScale(e) {
|
|
4333
|
-
return this.#
|
|
4334
|
-
snapToZeroCrossings: this.#
|
|
4364
|
+
return this.#_.setScale({
|
|
4365
|
+
snapToZeroCrossings: this.#N,
|
|
4335
4366
|
...e
|
|
4336
|
-
}), this.#
|
|
4337
|
-
snapToZeroCrossings: this.#
|
|
4367
|
+
}), this.#v.setScale({
|
|
4368
|
+
snapToZeroCrossings: this.#N,
|
|
4338
4369
|
...e
|
|
4339
4370
|
}), this;
|
|
4340
4371
|
}
|
|
4341
4372
|
setRootNote(e) {
|
|
4342
4373
|
let t = b[e], n = t === 0 ? 0 : t - 12;
|
|
4343
|
-
return this.transposedBySemitones === n ? this : (this.transposeSemitones = n, this.#
|
|
4374
|
+
return this.transposedBySemitones === n ? this : (this.transposeSemitones = n, this.#v.setRootNote(e), this.#_.setRootNote(e), this);
|
|
4344
4375
|
}
|
|
4345
4376
|
setVolume(e) {
|
|
4346
|
-
return e = O(e, 0, 1), this.#
|
|
4377
|
+
return e = O(e, 0, 1), this.#g.gain.setValueAtTime(e, this.now), this;
|
|
4347
4378
|
}
|
|
4348
4379
|
setSampleStartPoint(e) {
|
|
4349
4380
|
return this.voicePool.applyToAllVoices((t) => t.setStartPoint(e)), this.sendUpstreamMessage("start-point:updated", { startPoint: e }), this;
|
|
@@ -4352,29 +4383,29 @@ var Ct = class {
|
|
|
4352
4383
|
return this.voicePool.applyToAllVoices((t) => t.setEndPoint(e)), this.sendUpstreamMessage("end-point:updated", { endPoint: e }), this;
|
|
4353
4384
|
}
|
|
4354
4385
|
setLoopRampDuration(e) {
|
|
4355
|
-
return this.#
|
|
4386
|
+
return this.#w = e, this;
|
|
4356
4387
|
}
|
|
4357
4388
|
setGlideTime(e) {
|
|
4358
|
-
return this.#
|
|
4389
|
+
return this.#C = e, this;
|
|
4359
4390
|
}
|
|
4360
4391
|
setLoopEnabled(e) {
|
|
4361
|
-
return this.#
|
|
4392
|
+
return this.#d === e || this.#f && !e ? this : (this.voicePool.allVoices.forEach((t) => t.setLoopEnabled(e)), this.#d = e, this.sendUpstreamMessage("loop:enabled", { enabled: e }), this);
|
|
4362
4393
|
}
|
|
4363
4394
|
setLoopLocked(e) {
|
|
4364
|
-
return this.#
|
|
4395
|
+
return this.#f === e ? this : (this.#f = e, this.setLoopEnabled(e), this.sendUpstreamMessage("loop:locked", { locked: e }), this);
|
|
4365
4396
|
}
|
|
4366
4397
|
setHoldEnabled(e) {
|
|
4367
|
-
return this.#
|
|
4398
|
+
return this.#p === e || this.#m && !e ? this : (this.#p = e, e || this.releaseAll(.1), this.sendUpstreamMessage("hold:enabled", { enabled: e }), this);
|
|
4368
4399
|
}
|
|
4369
4400
|
setHoldLocked(e) {
|
|
4370
|
-
return this.#
|
|
4401
|
+
return this.#m === e ? this : (this.#m = e, e === !1 && this.releaseAll(), this.sendUpstreamMessage("hold:locked", { locked: e }), this);
|
|
4371
4402
|
}
|
|
4372
|
-
#
|
|
4403
|
+
#W = !1;
|
|
4373
4404
|
setSustainPedal(e) {
|
|
4374
|
-
if (this.#
|
|
4375
|
-
if (this.#
|
|
4376
|
-
for (let e of this.#
|
|
4377
|
-
this.#
|
|
4405
|
+
if (this.#h === e) return this;
|
|
4406
|
+
if (this.#h = e, this.#f || (this.#W && !e ? (this.#W = !1, this.setLoopEnabled(!1)) : e && !this.#d && (this.setLoopEnabled(!0), this.#W = !0)), this.#m || this.setHoldEnabled(e), !e) {
|
|
4407
|
+
for (let e of this.#I) this.voicePool.noteOff(e), this.sendUpstreamMessage("note:off", { transposedMidiNote: e });
|
|
4408
|
+
this.#I.clear();
|
|
4378
4409
|
}
|
|
4379
4410
|
return this;
|
|
4380
4411
|
}
|
|
@@ -4395,37 +4426,37 @@ var Ct = class {
|
|
|
4395
4426
|
setLoopDuration = (e, t = this.getLoopRampDuration()) => this.setLoopPoint("end", this.loopStart, this.loopStart + e, t);
|
|
4396
4427
|
debugcounter = 0;
|
|
4397
4428
|
setTempo(e) {
|
|
4398
|
-
if (!(e < this.#
|
|
4429
|
+
if (!(e < this.#A || e > this.#k)) return this.#S = e, this.voicePool.applyToAllVoices((t) => t.setTempo(e)), this.sendUpstreamMessage("tempo:updated", { bpm: e }), this;
|
|
4399
4430
|
}
|
|
4400
4431
|
syncLoopToTempo(e) {
|
|
4401
4432
|
return this.voicePool.applyToAllVoices((t) => t.syncLoopToTempo(e)), this;
|
|
4402
4433
|
}
|
|
4403
4434
|
setKeytrackLoopAmount(e) {
|
|
4404
4435
|
let t = Math.max(0, Math.min(1, e));
|
|
4405
|
-
return this.#
|
|
4436
|
+
return this.#T = t, this.voicePool.applyToAllVoices((e) => e.setKeytrackLoopAmount(t)), this;
|
|
4406
4437
|
}
|
|
4407
|
-
getKeytrackLoopAmount = () => this.#
|
|
4438
|
+
getKeytrackLoopAmount = () => this.#T;
|
|
4408
4439
|
get tempo() {
|
|
4409
|
-
return this.#
|
|
4440
|
+
return this.#S;
|
|
4410
4441
|
}
|
|
4411
4442
|
setLoopPoint(e, t, n, r = this.getLoopRampDuration()) {
|
|
4412
4443
|
let i = e === "start" ? O(t, this.MIN_LOOP_DURATION_SECONDS / 2, n) : t;
|
|
4413
4444
|
if (e === "start" && i === this.loopStart) return this;
|
|
4414
|
-
let a = O(n, i, this.#
|
|
4445
|
+
let a = O(n, i, this.#u - this.MIN_LOOP_DURATION_SECONDS / 2);
|
|
4415
4446
|
if (e === "end" && a === this.loopEnd) return this;
|
|
4416
4447
|
let o = a - i, s = r * 1;
|
|
4417
4448
|
if (e === "start" && i !== this.loopStart) {
|
|
4418
|
-
if (this.#
|
|
4419
|
-
let e = 60 / this.#
|
|
4449
|
+
if (this.#O) {
|
|
4450
|
+
let e = 60 / this.#S, t = Math.round(o / e);
|
|
4420
4451
|
i = a - t * e;
|
|
4421
4452
|
}
|
|
4422
|
-
o < this.MIN_LOOP_DURATION_SECONDS && (i = a - this.MIN_LOOP_DURATION_SECONDS), this.#
|
|
4453
|
+
o < this.MIN_LOOP_DURATION_SECONDS && (i = a - this.MIN_LOOP_DURATION_SECONDS), this.#_.ramp(i, s, a);
|
|
4423
4454
|
} else if (e === "end" && a !== this.loopEnd) {
|
|
4424
|
-
if (this.#
|
|
4425
|
-
let e = 60 / this.#
|
|
4455
|
+
if (this.#O) {
|
|
4456
|
+
let e = 60 / this.#S, t = Math.round(o / e);
|
|
4426
4457
|
a = i + t * e;
|
|
4427
4458
|
}
|
|
4428
|
-
o < this.MIN_LOOP_DURATION_SECONDS && (a = i + this.MIN_LOOP_DURATION_SECONDS), this.#
|
|
4459
|
+
o < this.MIN_LOOP_DURATION_SECONDS && (a = i + this.MIN_LOOP_DURATION_SECONDS), this.#v.ramp(a, s, i);
|
|
4429
4460
|
}
|
|
4430
4461
|
return this.sendUpstreamMessage("loop-points:updated", {
|
|
4431
4462
|
loopStart: this.loopStart,
|
|
@@ -4434,7 +4465,7 @@ var Ct = class {
|
|
|
4434
4465
|
}
|
|
4435
4466
|
scrollLoopPoints(e, t) {
|
|
4436
4467
|
let n = this.context.currentTime;
|
|
4437
|
-
return this.#
|
|
4468
|
+
return this.#_.setValue(e, n), this.#v.setValue(t, n), this.sendUpstreamMessage("loop-points:updated", {
|
|
4438
4469
|
loopStart: this.loopStart,
|
|
4439
4470
|
loopEnd: this.loopEnd
|
|
4440
4471
|
}), this;
|
|
@@ -4471,8 +4502,8 @@ var Ct = class {
|
|
|
4471
4502
|
}
|
|
4472
4503
|
getAudioParam(e) {
|
|
4473
4504
|
switch (e) {
|
|
4474
|
-
case "loopStart": return this.#
|
|
4475
|
-
case "loopEnd": return this.#
|
|
4505
|
+
case "loopStart": return this.#_.audioParam;
|
|
4506
|
+
case "loopEnd": return this.#v.audioParam;
|
|
4476
4507
|
default: return console.warn(`Parameter '${e}' not found on SamplePlayer`), null;
|
|
4477
4508
|
}
|
|
4478
4509
|
}
|
|
@@ -4483,13 +4514,13 @@ var Ct = class {
|
|
|
4483
4514
|
return this.voicePool?.allVoices[0]?.endPoint ?? this.sampleDuration;
|
|
4484
4515
|
}
|
|
4485
4516
|
getLoopRampDuration() {
|
|
4486
|
-
return this.#
|
|
4517
|
+
return this.#w;
|
|
4487
4518
|
}
|
|
4488
4519
|
getGlideTime() {
|
|
4489
|
-
return this.#
|
|
4520
|
+
return this.#C;
|
|
4490
4521
|
}
|
|
4491
|
-
getHpfCutoff = () => this.#
|
|
4492
|
-
getLpfCutoff = () => this.#
|
|
4522
|
+
getHpfCutoff = () => this.#E;
|
|
4523
|
+
getLpfCutoff = () => this.#D;
|
|
4493
4524
|
getParameterValue(e) {
|
|
4494
4525
|
switch (e) {
|
|
4495
4526
|
case "loopStart": return this.loopStart;
|
|
@@ -4554,12 +4585,12 @@ var Ct = class {
|
|
|
4554
4585
|
this.outBus.setSendAmount(e, t);
|
|
4555
4586
|
};
|
|
4556
4587
|
setLpfCutoff = (e, t = "pre") => {
|
|
4557
|
-
this.#
|
|
4588
|
+
this.#D = e, t === "pre" ? this.voicePool.applyToAllVoices((t) => {
|
|
4558
4589
|
t.setLpfCutoff(e);
|
|
4559
4590
|
}) : t === "post" && this.outBus.setLpfCutoff(e);
|
|
4560
4591
|
};
|
|
4561
4592
|
setHpfCutoff = (e, t = "pre") => {
|
|
4562
|
-
this.#
|
|
4593
|
+
this.#E = e, t === "pre" ? this.voicePool.applyToAllVoices((t) => {
|
|
4563
4594
|
t.setHpfCutoff(e);
|
|
4564
4595
|
}) : t === "post" && this.outBus.setHpfCutoff(e);
|
|
4565
4596
|
};
|
|
@@ -4577,13 +4608,13 @@ var Ct = class {
|
|
|
4577
4608
|
});
|
|
4578
4609
|
}
|
|
4579
4610
|
setFeedbackAmount = (e) => {
|
|
4580
|
-
e = O(e, 0, 1), (this.#
|
|
4611
|
+
e = O(e, 0, 1), (this.#G === "monophonic" || this.#G === "double-trouble") && this.outBus.setFeedbackAmount(e), (this.#G === "polyphonic" || this.#G === "double-trouble") && this.voicePool.applyToAllVoices((t) => {
|
|
4581
4612
|
t.feedback?.setAmountMacro(e);
|
|
4582
4613
|
});
|
|
4583
4614
|
};
|
|
4584
|
-
#
|
|
4615
|
+
#G = "monophonic";
|
|
4585
4616
|
setFeedbackMode(e) {
|
|
4586
|
-
if (this.#
|
|
4617
|
+
if (this.#G = e, e === "monophonic") {
|
|
4587
4618
|
let e = this.voicePool.allVoices[0].feedback?.currentAmount ?? 0;
|
|
4588
4619
|
this.voicePool.applyToAllVoices((e) => {
|
|
4589
4620
|
e.feedback?.setAmountMacro(0);
|
|
@@ -4601,37 +4632,37 @@ var Ct = class {
|
|
|
4601
4632
|
});
|
|
4602
4633
|
}
|
|
4603
4634
|
get mainOut() {
|
|
4604
|
-
return this.#
|
|
4635
|
+
return this.#g;
|
|
4605
4636
|
}
|
|
4606
4637
|
get outputBus() {
|
|
4607
4638
|
return this.outBus;
|
|
4608
4639
|
}
|
|
4609
4640
|
get sampleDuration() {
|
|
4610
|
-
return this.#
|
|
4641
|
+
return this.#u;
|
|
4611
4642
|
}
|
|
4612
4643
|
get volume() {
|
|
4613
|
-
return this.#
|
|
4644
|
+
return this.#g.gain.value;
|
|
4614
4645
|
}
|
|
4615
4646
|
set volume(e) {
|
|
4616
|
-
this.#
|
|
4647
|
+
this.#g.gain.setValueAtTime(e, this.context.currentTime);
|
|
4617
4648
|
}
|
|
4618
4649
|
get loopEnabled() {
|
|
4619
|
-
return this.#
|
|
4650
|
+
return this.#d;
|
|
4620
4651
|
}
|
|
4621
4652
|
get holdEnabled() {
|
|
4622
|
-
return this.#
|
|
4653
|
+
return this.#p;
|
|
4623
4654
|
}
|
|
4624
4655
|
get gainLFO() {
|
|
4625
|
-
return this.#
|
|
4656
|
+
return this.#y;
|
|
4626
4657
|
}
|
|
4627
4658
|
get pitchLFO() {
|
|
4628
|
-
return this.#
|
|
4659
|
+
return this.#b;
|
|
4629
4660
|
}
|
|
4630
4661
|
get loopStart() {
|
|
4631
|
-
return this.#
|
|
4662
|
+
return this.#_.targetValue;
|
|
4632
4663
|
}
|
|
4633
4664
|
get loopEnd() {
|
|
4634
|
-
return this.#
|
|
4665
|
+
return this.#v.targetValue;
|
|
4635
4666
|
}
|
|
4636
4667
|
get isLoaded() {
|
|
4637
4668
|
return this.#r;
|
|
@@ -4639,14 +4670,17 @@ var Ct = class {
|
|
|
4639
4670
|
get audiobuffer() {
|
|
4640
4671
|
return this.#c;
|
|
4641
4672
|
}
|
|
4673
|
+
get layers() {
|
|
4674
|
+
return [...this.#l];
|
|
4675
|
+
}
|
|
4642
4676
|
dispose() {
|
|
4643
4677
|
try {
|
|
4644
|
-
this.releaseAll(), this.#
|
|
4678
|
+
this.releaseAll(), this.#I.clear(), this.voicePool &&= (this.voicePool.dispose(), null), this.outBus &&= (this.outBus.dispose(), null), this.#_?.dispose(), this.#v?.dispose(), this.#_ = null, this.#v = null, this.#y?.dispose(), this.#b?.dispose(), this.disconnect(), this.#u = 0, this.#c = null, this.#l = [], this.#t = !1, this.#r = !1, this.#N = [], this.#P = !1, this.#d = !1, B(this.nodeId);
|
|
4645
4679
|
} catch (e) {
|
|
4646
4680
|
console.error(`Error disposing Sampler ${this.nodeId}:`, e);
|
|
4647
4681
|
}
|
|
4648
4682
|
}
|
|
4649
|
-
}, wt = "//#region src/utils/search/findClosest.ts\n/**\n* Generic binary search that finds the closest element using a custom comparison function\n* @param sortedArray - Array sorted according to the compareValue function\n* @param target - Target value to search for\n* @param getValue - Function to extract comparison value from array elements (defaults to identity for number arrays)\n* @param getDistance - Optional function to calculate distance (defaults to absolute difference)\n* @returns The array index of the element which value is closest to the target value\n*/\nfunction findClosestIdx(sortedArray, target, direction = \"any\", getValue = (x) => x, getDistance = (a, b) => Math.abs(a - b)) {\n if (sortedArray.length === 0) throw new Error(\"Array cannot be empty\");\n if (sortedArray.length === 1) return 0;\n const targetValue = target;\n const firstValue = getValue(sortedArray[0]);\n const lastValue = getValue(sortedArray[sortedArray.length - 1]);\n if (targetValue <= firstValue) return 0;\n if (targetValue >= lastValue) return sortedArray.length - 1;\n let left = 0;\n let right = sortedArray.length - 1;\n while (left < right - 1) {\n const mid = Math.floor((left + right) / 2);\n const midValue = getValue(sortedArray[mid]);\n if (midValue === targetValue) return mid;\n else if (midValue < targetValue) left = mid;\n else right = mid;\n }\n if (direction === \"left\") return left;\n if (direction === \"right\") return right;\n return getDistance(getValue(sortedArray[left]), targetValue) <= getDistance(getValue(sortedArray[right]), targetValue) ? left : right;\n}\n/**\n* Generic binary search that finds the closest element using a custom comparison function\n* @param sortedArray - Array sorted according to the compareValue function\n* @param target - Target value to search for\n* @param getValue - Function to extract comparison value from array elements (defaults to identity for number arrays)\n* @param getDistance - Optional function to calculate distance (defaults to absolute difference)\n* @returns The array element which value is closest to the target value\n*/\nfunction findClosest(sortedArray, target, direction = \"any\", getValue = (x) => x, getDistance = (a, b) => Math.abs(a - b)) {\n return sortedArray[findClosestIdx(sortedArray, target, direction, getValue, getDistance)];\n}\nvar SAMPLE_PLAYER_WORKLET_AUDIOPARAM_DESCRIPTORS = Object.values({\n masterGain: {\n name: \"masterGain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 2,\n automationRate: \"k-rate\"\n },\n envGain: {\n name: \"envGain\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\"\n },\n velocity: {\n name: \"velocity\",\n defaultValue: 100,\n minValue: 0,\n maxValue: 127,\n automationRate: \"k-rate\"\n },\n pan: {\n name: \"pan\",\n defaultValue: 0,\n minValue: -1,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n playbackRate: {\n name: \"playbackRate\",\n defaultValue: 1,\n minValue: .1,\n maxValue: 24,\n automationRate: \"a-rate\"\n },\n loopStart: {\n name: \"loopStart\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 99999,\n automationRate: \"k-rate\"\n },\n loopEnd: {\n name: \"loopEnd\",\n defaultValue: 99999,\n minValue: 0,\n maxValue: 99999,\n automationRate: \"k-rate\"\n },\n startPoint: {\n name: \"startPoint\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 9999,\n automationRate: \"k-rate\"\n },\n endPoint: {\n name: \"endPoint\",\n defaultValue: 9999,\n minValue: 0,\n maxValue: 9999,\n automationRate: \"k-rate\"\n },\n playbackPosition: {\n name: \"playbackPosition\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 99999,\n automationRate: \"k-rate\"\n },\n loopDurationDriftAmount: {\n name: \"loopDurationDriftAmount\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n maxLoopCount: {\n name: \"maxLoopCount\",\n defaultValue: 999999,\n minValue: 1,\n maxValue: 999999,\n automationRate: \"k-rate\"\n },\n tempo: {\n name: \"tempo\",\n defaultValue: 120,\n minValue: 20,\n maxValue: 300,\n automationRate: \"k-rate\"\n }\n});\n//#endregion\n//#region src/worklets/processors/play/sample-player-processor.js\nvar SamplePlayerProcessor = class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return SAMPLE_PLAYER_WORKLET_AUDIOPARAM_DESCRIPTORS;\n }\n constructor() {\n super();\n this.buffer = null;\n this.minZeroCrossing = 0;\n this.maxZeroCrossing = 0;\n this.usePlaybackPosition = false;\n this.enableLoopSmoothing = true;\n this.enableAdaptiveDrift = true;\n this.enableAmplitudeCompensation = true;\n this.syncLoopToTempo = false;\n this.keytrackLoopAmount = 0;\n this.durationPreservation = {\n enabled: false,\n maxDriftSamples: Math.floor(sampleRate * .04),\n timelinePosition: 0,\n resetPending: false\n };\n this.PITCH_PRESERVATION_THRESHOLD = Math.floor(sampleRate * .061);\n this.AMPLITUDE_COMPENSATION_THRESHOLD = Math.floor(sampleRate / 16.35);\n this.port.onmessage = this.#handleMessage.bind(this);\n this.#resetState();\n this.port.postMessage({ type: \"initialized\" });\n }\n #handleMessage(event) {\n const { type, value, buffer, timestamp, durationSeconds, zeroCrossings, semitones, allowedPeriods, playbackDirection } = event.data;\n switch (type) {\n case \"voice:reset\":\n this.#resetState();\n this.port.postMessage({ type: \"voice:reset\" });\n break;\n case \"voice:setBuffer\":\n this.#resetState();\n this.zeroCrossings = [];\n this.minZeroCrossing = 0;\n this.maxZeroCrossing = 0;\n this.buffer = null;\n this.buffer = buffer;\n this.port.postMessage({\n type: \"voice:loaded\",\n durationSeconds,\n time: currentTime\n });\n break;\n case \"transpose\":\n this.transpositionPlaybackrate = Math.pow(2, semitones / 12);\n this.port.postMessage({\n type: \"voice:transposed\",\n semitones,\n time: currentTime\n });\n break;\n case \"voice:setZeroCrossings\":\n this.zeroCrossings = (zeroCrossings || []).map((timeSec) => timeSec * sampleRate);\n if (this.zeroCrossings.length > 0) {\n this.minZeroCrossing = this.zeroCrossings[0];\n this.maxZeroCrossing = this.zeroCrossings[this.zeroCrossings.length - 1];\n }\n break;\n case \"voice:start\":\n this.isReleasing = false;\n this.isPlaying = true;\n this.loopCount = 0;\n this.playbackPosition = 0;\n this.port.postMessage({\n type: \"voice:started\",\n time: timestamp || currentTime\n });\n break;\n case \"voice:release\":\n this.isReleasing = true;\n this.port.postMessage({\n type: \"voice:releasing\",\n time: currentTime\n });\n break;\n case \"voice:stop\":\n this.#stop();\n break;\n case \"setLoopEnabled\":\n this.loopEnabled = value;\n this.port.postMessage({\n type: \"loop:enabled\",\n enabled: value\n });\n break;\n case \"setPanDriftEnabled\":\n this.panDriftEnabled = value;\n break;\n case \"voice:setPlaybackDirection\": {\n const reverse = playbackDirection === \"reverse\";\n if (reverse !== this.reversePlayback && this.playbackPosition > 0) this.playbackPosition += reverse ? 1 : -1;\n this.reversePlayback = reverse;\n this.port.postMessage({\n type: \"voice:playbackDirectionChange\",\n playbackDirection\n });\n break;\n }\n case \"voice:usePlaybackPosition\":\n this.usePlaybackPosition = value;\n break;\n case \"syncLoopToTempo\":\n this.syncLoopToTempo = value;\n this.port.postMessage({\n type: \"loop:syncToTempo\",\n enabled: value\n });\n break;\n case \"setKeytrackLoopAmount\":\n this.keytrackLoopAmount = Math.max(0, Math.min(1, value));\n break;\n case \"setPreserveDuration\":\n this.durationPreservation.enabled = Boolean(value);\n this.#resetDurationPreservation(this.playbackPosition);\n }\n }\n #resetState() {\n this.isPlaying = false;\n this.isReleasing = false;\n this.loopEnabled = false;\n this.transpositionPlaybackrate = 1;\n this.velocitySensitivity = 1;\n this.reversePlayback = false;\n this.playbackPosition = 0;\n this.debugCounter = 0;\n this.loopCount = 0;\n this.applyClickCompensation = false;\n this.loopClickCompensation = 0;\n this.driftUpdateCounter = 0;\n this.currentLoopDrift = 0;\n this.currentPanDrift = 0;\n this.panDriftEnabled = true;\n this.nextDriftGenerated = false;\n this.loopAmplitudeGain = 1;\n this.lastAnalyzedLoopStart = -1;\n this.lastAnalyzedLoopEnd = -1;\n this.#resetDurationPreservation();\n }\n #stop() {\n this.isPlaying = false;\n this.isReleasing = false;\n this.playbackPosition = 0;\n this.port.postMessage({ type: \"voice:stopped\" });\n }\n #smoothLoopWrap(lastLoopSample, newFirstSample) {\n const discontinuity = lastLoopSample - newFirstSample;\n if (this.enableLoopSmoothing && Math.abs(discontinuity) > .01) {\n this.loopClickCompensation = discontinuity * .5;\n this.compensationDecay = .9;\n this.applyClickCompensation = true;\n }\n }\n #clamp = (value, min, max) => Math.max(min, Math.min(max, value));\n #clampZeroCrossing = (value) => this.#clamp(value, this.minZeroCrossing, this.maxZeroCrossing);\n #findNearestZeroCrossing(position, direction = \"any\", maxDistance = null) {\n if (!this.zeroCrossings || this.zeroCrossings.length === 0) return position;\n const closestValue = findClosest(this.zeroCrossings, position, direction);\n if (maxDistance !== null && Math.abs(closestValue - position) > maxDistance) return position;\n return closestValue;\n }\n /**\n * Convert normalized position (0-1) to sample index\n * @param {number} normalizedPosition - Position as 0-1 value\n * @returns {number} - Sample index\n */\n #normalizedToSamples(normalizedPosition) {\n if (!this.buffer || !this.buffer[0]) return 0;\n return normalizedPosition * this.buffer[0].length;\n }\n /**\n * Convert sample index to normalized position (0-1)\n * @param {number} sampleIndex - Sample index\n * @returns {number} - Normalized position 0-1\n */\n #samplesToNormalized(sampleIndex) {\n if (!this.buffer || !this.buffer[0]) return 0;\n return sampleIndex / this.buffer[0].length;\n }\n /**\n * Convert MIDI velocity (0-127) to gain multiplier (0-1)\n * @param {number} midiVelocity - MIDI velocity 0-127\n * @returns {number} - Gain multiplier 0-1\n */\n #midiVelocityToGain(midiVelocity) {\n return Math.max(0, Math.min(1, midiVelocity / 127));\n }\n /**\n * Get buffer duration in seconds\n * @returns {number} - Buffer duration in seconds\n */\n #getBufferDurationSeconds() {\n return (this.buffer?.[0]?.length || 0) / sampleRate;\n }\n /**\n * Calculate musical note durations in samples for given tempo\n * @param {number} tempo - BPM\n * @returns {Object} - Musical note durations in samples\n */\n #getMusicalNoteDurations(tempo) {\n const beatsPerSecond = tempo / 60;\n const samplesPerBeat = sampleRate / beatsPerSecond;\n return {\n whole: samplesPerBeat * 4,\n half: samplesPerBeat * 2,\n quarter: samplesPerBeat,\n eighth: samplesPerBeat / 2,\n sixteenth: samplesPerBeat / 4,\n thirtySecond: samplesPerBeat / 8,\n quarterTriplet: samplesPerBeat * 2 / 3,\n eighthTriplet: samplesPerBeat / 2 * 2 / 3,\n sixteenthTriplet: samplesPerBeat / 4 * 2 / 3\n };\n }\n /**\n * Quantize loop duration to nearest musical interval (skips if below the smallest quantize option)\n * @param {number} loopDurationSamples - Current loop duration in samples\n * @param {number} tempo - Current tempo in BPM\n * @param {number} playbackRate - Current playback rate\n * @returns {number} - Quantized loop duration in samples\n */\n #quantizeLoopDuration(loopDurationSamples, tempo, playbackRate) {\n if (!this.syncLoopToTempo) return loopDurationSamples;\n const noteDurations = this.#getMusicalNoteDurations(tempo);\n const effectiveDuration = loopDurationSamples / Math.abs(playbackRate);\n if (effectiveDuration < noteDurations.thirtySecond) return loopDurationSamples;\n const intervals = Object.values(noteDurations);\n let closestInterval = intervals[0];\n let smallestDiff = Math.abs(effectiveDuration - closestInterval);\n for (const interval of intervals) {\n const diff = Math.abs(effectiveDuration - interval);\n if (diff < smallestDiff) {\n smallestDiff = diff;\n closestInterval = interval;\n }\n }\n return Math.floor(closestInterval * Math.abs(playbackRate));\n }\n /**\n * Extract and convert all position parameters from seconds to samples\n * @param {Object} parameters - AudioWorkletProcessor parameters\n * @returns {Object} - Converted parameters in samples\n */\n #extractPositionParams(parameters) {\n return {\n startPointSamples: Math.floor(parameters.startPoint[0] * sampleRate),\n endPointSamples: Math.floor(parameters.endPoint[0] * sampleRate),\n loopStartSamples: Math.floor(parameters.loopStart[0] * sampleRate),\n loopEndSamples: Math.floor(parameters.loopEnd[0] * sampleRate)\n };\n }\n /**\n * Calculate effective playback range in samples\n * @param {Object} params - Position parameters from #extractPositionParams\n * @returns {Object} - Effective start and end positions\n */\n #calculatePlaybackRange(params) {\n const bufferLength = this.buffer?.[0]?.length || 0;\n const start = Math.max(0, params.startPointSamples);\n const end = params.endPointSamples > start ? Math.min(bufferLength, params.endPointSamples) : bufferLength;\n const snappedStart = this.#findNearestZeroCrossing(start, \"right\");\n const snappedEnd = this.#findNearestZeroCrossing(end, \"left\");\n return {\n startSamples: snappedStart,\n endSamples: snappedEnd,\n durationSamples: snappedEnd - snappedStart\n };\n }\n /**\n * Calculate effective loop range in samples with optional drift\n * @param {Object} params - Position parameters from #extractPositionParams\n * @param {Object} playbackRange - Range from #calculatePlaybackRange\n * @param {number} driftAmount - Loop duration drift amount (0-1)\n * @param {number} tempo - Current tempo in BPM\n * @param {number} playbackRate - Current playback rate\n * @returns {Object} - Effective loop start and end positions with drift applied\n */\n #calculateLoopRange(params, playbackRange, driftAmount = 0, tempo = 120, playbackRate = 1) {\n const lpStart = params.loopStartSamples;\n const lpEnd = params.loopEndSamples;\n let calcLoopStart = lpStart < lpEnd && lpStart >= 0 ? lpStart : playbackRange.startSamples;\n let calcLoopEnd = lpEnd > lpStart && lpEnd <= playbackRange.endSamples ? lpEnd : playbackRange.endSamples;\n let baseDuration = calcLoopEnd - calcLoopStart;\n if (this.syncLoopToTempo) {\n const quantizedDuration = this.#quantizeLoopDuration(baseDuration, tempo, playbackRate);\n calcLoopEnd = calcLoopStart + quantizedDuration;\n calcLoopEnd = Math.min(calcLoopEnd, playbackRange.endSamples);\n }\n if (baseDuration > this.PITCH_PRESERVATION_THRESHOLD && this.keytrackLoopAmount > 0 && !this.syncLoopToTempo) {\n const scale = 1 + this.keytrackLoopAmount * (Math.abs(playbackRate) - 1);\n baseDuration = Math.max(1, Math.floor(baseDuration * scale));\n calcLoopEnd = calcLoopStart + baseDuration;\n }\n baseDuration = calcLoopEnd - calcLoopStart;\n if (baseDuration > this.PITCH_PRESERVATION_THRESHOLD) calcLoopStart = this.#findNearestZeroCrossing(calcLoopStart, \"right\");\n if (driftAmount > 0 && this.loopEnabled) {\n if (!this.nextDriftGenerated || this.loopCount === 0) {\n const updateInterval = baseDuration <= this.PITCH_PRESERVATION_THRESHOLD ? Math.max(1, Math.floor(this.PITCH_PRESERVATION_THRESHOLD / baseDuration)) : 1;\n if (this.driftUpdateCounter % updateInterval === 0) {\n this.currentLoopDrift = this.#generateLoopDrift(driftAmount, baseDuration);\n if (this.panDriftEnabled && driftAmount > 0 && this.loopCount > 0) {\n const panDriftAmountScalar = 1e-4;\n this.currentPanDrift = this.currentLoopDrift * panDriftAmountScalar;\n } else this.currentPanDrift = 0;\n }\n this.driftUpdateCounter++;\n this.nextDriftGenerated = true;\n }\n const driftedLoopEnd = calcLoopEnd + this.currentLoopDrift;\n const minLoopDuration = Math.max(1, Math.floor(baseDuration * .1));\n const maxLoopEnd = Math.max(playbackRange.endSamples, calcLoopEnd);\n calcLoopEnd = Math.max(calcLoopStart + minLoopDuration, Math.min(maxLoopEnd, driftedLoopEnd));\n } else this.currentPanDrift = 0;\n if (baseDuration > this.PITCH_PRESERVATION_THRESHOLD && calcLoopEnd <= playbackRange.endSamples) calcLoopEnd = Math.max(calcLoopStart + 1, this.#findNearestZeroCrossing(calcLoopEnd, \"left\"));\n const loopDuration = calcLoopEnd - calcLoopStart;\n return {\n loopStartSamples: calcLoopStart,\n loopEndSamples: calcLoopEnd,\n loopDurationSamples: loopDuration\n };\n }\n #getSafeParam(paramArray, index, isConstant) {\n return isConstant ? paramArray[0] : paramArray[Math.min(index, paramArray.length - 1)];\n }\n #getConstantFlags(parameters) {\n this.constantFlags ??= {\n envGain: true,\n playbackRate: true\n };\n this.constantFlags.envGain = parameters.envGain.length === 1;\n this.constantFlags.playbackRate = parameters.playbackRate.length === 1;\n return this.constantFlags;\n }\n #resetDurationPreservation(position = 0) {\n this.durationPreservation.timelinePosition = position;\n this.durationPreservation.resetPending = false;\n }\n #isDurationPreservationActive(loopRange) {\n return this.durationPreservation.enabled && Boolean(this.zeroCrossings?.length) && (!this.loopEnabled || loopRange.loopDurationSamples > this.PITCH_PRESERVATION_THRESHOLD);\n }\n #prepareDurationPreservingSample(playbackRate, loopRange) {\n const state = this.durationPreservation;\n if (!this.#isDurationPreservationActive(loopRange)) return null;\n if (Math.abs(this.playbackPosition - state.timelinePosition) > state.maxDriftSamples) state.resetPending = true;\n if (!state.resetPending) return null;\n const direction = playbackRate < 0 ? \"left\" : \"right\";\n const outgoingZero = this.#findNearestZeroCrossing(this.playbackPosition, direction);\n if (Math.abs(outgoingZero - this.playbackPosition) > Math.abs(playbackRate)) return null;\n this.playbackPosition = outgoingZero;\n state.resetPending = false;\n return this.#findNearestZeroCrossing(state.timelinePosition, \"any\", state.maxDriftSamples);\n }\n #advanceDurationPreservingPlayback(playbackRate, resetTarget, loopRange, canWrapLoop) {\n const state = this.durationPreservation;\n this.playbackPosition = resetTarget === null ? this.playbackPosition + playbackRate : resetTarget;\n if (this.#isDurationPreservationActive(loopRange)) {\n state.timelinePosition += playbackRate < 0 ? -1 : 1;\n if (canWrapLoop && playbackRate >= 0 && state.timelinePosition >= loopRange.loopEndSamples) state.timelinePosition = loopRange.loopStartSamples;\n else if (canWrapLoop && playbackRate < 0 && state.timelinePosition <= loopRange.loopStartSamples) state.timelinePosition = loopRange.loopEndSamples - 1;\n } else this.#resetDurationPreservation(this.playbackPosition);\n }\n /**\n * Generate a new drift amount for the current loop iteration\n * @param {number} driftAmount - Maximum drift amount (0-1)\n * @param {number} baseDuration - Base loop duration in samples\n * @returns {number} - Drift amount in samples\n */\n #generateLoopDrift(driftAmount, baseDuration) {\n if (driftAmount <= 0) return 0;\n const randomFactor = (Math.random() - .5) * 2;\n let effectiveDriftAmount = driftAmount;\n if (this.enableAdaptiveDrift) {\n const shortThreshold = 1024;\n const longThreshold = 8192;\n if (baseDuration < shortThreshold) effectiveDriftAmount *= .1;\n else if (baseDuration < longThreshold) {\n const scaleFactor = .1 + .9 * (baseDuration - shortThreshold) / 7168;\n effectiveDriftAmount *= scaleFactor;\n }\n }\n const maxDriftSamples = effectiveDriftAmount * baseDuration;\n return Math.floor(randomFactor * maxDriftSamples);\n }\n /**\n * Analyze loop amplitude and calculate makeup gain for short loops\n * @param {number} loopStart - Loop start position in samples\n * @param {number} loopEnd - Loop end position in samples\n * @returns {number} - Makeup gain multiplier (1.0 = no change)\n */\n #analyzeLoopAmplitude(loopStart, loopEnd) {\n if (!this.enableAmplitudeCompensation || !this.buffer || !this.buffer[0]) return 1;\n if (loopEnd - loopStart >= this.AMPLITUDE_COMPENSATION_THRESHOLD) return 1;\n if (loopStart === this.lastAnalyzedLoopStart && loopEnd === this.lastAnalyzedLoopEnd) return this.loopAmplitudeGain;\n let sumSquares = 0;\n let sampleCount = 0;\n const channel = this.buffer[0];\n const startIndex = Math.floor(loopStart);\n const endIndex = Math.floor(loopEnd);\n for (let i = startIndex; i < endIndex && i < channel.length; i++) {\n const sample = channel[i];\n sumSquares += sample * sample;\n sampleCount++;\n }\n if (sampleCount === 0) return 1;\n const rmsAmplitude = Math.sqrt(sumSquares / sampleCount);\n const targetAmplitude = .3;\n let makeupGain = 1;\n if (rmsAmplitude < targetAmplitude) {\n makeupGain = targetAmplitude / Math.max(rmsAmplitude, .001);\n makeupGain = Math.min(2, makeupGain);\n }\n this.lastAnalyzedLoopStart = loopStart;\n this.lastAnalyzedLoopEnd = loopEnd;\n this.loopAmplitudeGain = makeupGain;\n return makeupGain;\n }\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n this.debugCounter++;\n if (!output || !this.isPlaying || !this.buffer?.[0]?.length) return true;\n const masterGain = parameters.masterGain[0];\n const positionParams = this.#extractPositionParams(parameters);\n const playbackRange = this.#calculatePlaybackRange(positionParams);\n const effectivePlaybackRate = parameters.playbackRate[0] * this.transpositionPlaybackrate;\n const tempo = parameters.tempo[0];\n const loopRange = this.#calculateLoopRange(positionParams, playbackRange, parameters.loopDurationDriftAmount[0], tempo, effectivePlaybackRate);\n const amplitudeGain = this.#analyzeLoopAmplitude(loopRange.loopStartSamples, loopRange.loopEndSamples);\n const velocityGain = this.#midiVelocityToGain(parameters.velocity[0]) * this.velocitySensitivity;\n const basePan = parameters.pan[0];\n const effectivePan = this.panDriftEnabled ? Math.max(-1, Math.min(1, basePan + this.currentPanDrift)) : basePan;\n let outputChannels;\n if (output instanceof Float32Array) outputChannels = [output];\n else if (Array.isArray(output) && output.every((ch) => ch instanceof Float32Array)) outputChannels = output;\n else {\n console.error(\"Unexpected output structure:\", {\n outputType: typeof output,\n isArray: Array.isArray(output),\n constructor: output?.constructor?.name,\n length: output?.length\n });\n return true;\n }\n const numChannels = outputChannels.length;\n const isConstant = this.#getConstantFlags(parameters);\n const silencePadTail = loopRange.loopEndSamples > playbackRange.endSamples;\n const TAIL_FADE_SAMPLES = 64;\n if (this.playbackPosition === 0) {\n this.playbackPosition = this.reversePlayback ? playbackRange.endSamples - 1 : playbackRange.startSamples;\n this.#resetDurationPreservation(this.playbackPosition);\n }\n for (let sample = 0; sample < outputChannels[0].length; sample++) {\n const envelopeGain = this.#getSafeParam(parameters.envGain, sample, isConstant.envGain);\n const baseRate = this.#getSafeParam(parameters.playbackRate, sample, isConstant.playbackRate);\n const playbackStep = (this.reversePlayback ? -Math.abs(baseRate) : Math.abs(baseRate)) * this.transpositionPlaybackrate;\n const canWrapLoop = this.loopEnabled && this.loopCount < parameters.maxLoopCount[0];\n if (canWrapLoop) {\n if (!this.reversePlayback && this.playbackPosition >= loopRange.loopEndSamples) {\n this.#smoothLoopWrap(silencePadTail ? 0 : this.buffer[0][Math.floor(this.playbackPosition - 1)] || 0, this.buffer[0][Math.floor(loopRange.loopStartSamples)] || 0);\n this.playbackPosition = loopRange.loopStartSamples;\n this.loopCount++;\n this.nextDriftGenerated = false;\n } else if (this.reversePlayback && this.playbackPosition <= loopRange.loopStartSamples) {\n this.#smoothLoopWrap(this.buffer[0][Math.floor(loopRange.loopStartSamples)] || 0, silencePadTail ? 0 : this.buffer[0][Math.floor(loopRange.loopEndSamples) - 1] || 0);\n this.playbackPosition = loopRange.loopEndSamples;\n this.loopCount++;\n this.nextDriftGenerated = false;\n }\n }\n const durationResetTarget = this.#prepareDurationPreservingSample(playbackStep, loopRange);\n const shouldStopForward = !this.reversePlayback && (this.#isDurationPreservationActive(loopRange) ? this.durationPreservation.timelinePosition : this.playbackPosition) >= playbackRange.endSamples;\n const shouldStopReverse = this.reversePlayback && (this.#isDurationPreservationActive(loopRange) ? this.durationPreservation.timelinePosition : this.playbackPosition) <= playbackRange.startSamples;\n const isWithinLoop = this.loopEnabled && this.playbackPosition >= loopRange.loopStartSamples && this.playbackPosition <= loopRange.loopEndSamples;\n if ((shouldStopForward || shouldStopReverse) && !(this.loopEnabled && isWithinLoop)) {\n this.#stop();\n return true;\n }\n let tailGain = 1;\n if (silencePadTail) {\n const distToEnd = playbackRange.endSamples - this.playbackPosition;\n if (distToEnd < TAIL_FADE_SAMPLES) tailGain = Math.max(0, distToEnd / TAIL_FADE_SAMPLES);\n }\n const currentPosition = Math.floor(this.playbackPosition);\n const positionOffset = this.playbackPosition - currentPosition;\n let nextPosition, interpWeight;\n if (this.reversePlayback) {\n nextPosition = Math.max(currentPosition - 1, playbackRange.startSamples);\n interpWeight = 1 - positionOffset;\n } else {\n nextPosition = Math.min(currentPosition + 1, playbackRange.endSamples - 1);\n interpWeight = positionOffset;\n }\n for (let channel = 0; channel < numChannels; channel++) {\n if (!outputChannels[channel]) {\n console.warn(`Output channel ${channel} does not exist. Available channels:`, outputChannels.length);\n continue;\n }\n const bufferChannelIndex = Math.min(channel, this.buffer.length - 1);\n const bufferChannel = this.buffer[bufferChannelIndex];\n const currentSample = bufferChannel[currentPosition] || 0;\n const nextSample = bufferChannel[nextPosition] || 0;\n let interpolatedSample = currentSample + interpWeight * (nextSample - currentSample);\n if (this.applyClickCompensation) {\n interpolatedSample += this.loopClickCompensation;\n if (this.compensationDecay) {\n this.loopClickCompensation *= this.compensationDecay;\n if (Math.abs(this.loopClickCompensation) < .001) this.applyClickCompensation = false;\n } else this.applyClickCompensation = false;\n }\n const finalSample = interpolatedSample * velocityGain * envelopeGain * masterGain * amplitudeGain * tailGain;\n let panAdjustedSample = finalSample;\n if (outputChannels.length === 2) {\n if (channel === 0) panAdjustedSample = finalSample * (1 - Math.max(0, effectivePan));\n else if (channel === 1) panAdjustedSample = finalSample * (1 - Math.max(0, -effectivePan));\n }\n outputChannels[channel][sample] = Math.max(-1, Math.min(1, isFinite(panAdjustedSample) ? panAdjustedSample : 0));\n }\n this.#advanceDurationPreservingPlayback(playbackStep, durationResetTarget, loopRange, canWrapLoop);\n }\n if (this.usePlaybackPosition) {\n const normalizedPosition = this.#samplesToNormalized(this.playbackPosition);\n this.port.postMessage({\n type: \"voice:position\",\n position: normalizedPosition\n });\n }\n return true;\n }\n};\nregisterProcessor(\"sample-player-processor\", SamplePlayerProcessor);\n//#endregion\n//#region src/worklets/processors/noise/random-noise-processor.js\nvar RandomNoiseProcessor = class extends AudioWorkletProcessor {\n constructor() {\n super();\n this.previousNoise = 0;\n this.previousFiltered = 0;\n this.hpfHz = 150;\n this.alpha = this.hpfHz / (this.hpfHz + sampleRate / (2 * Math.PI));\n this.port.onmessage = (event) => {\n if (event.data.type === \"setHpfHz\") {\n this.hpfHz = event.data.value;\n this.alpha = this.calculateAlpha(this.hpfHz);\n }\n };\n this.port.postMessage({ type: \"initialized\" });\n }\n calculateAlpha(frequency) {\n return frequency / (frequency + sampleRate / (2 * Math.PI));\n }\n process(inputs, outputs, parameters) {\n outputs[0].forEach((channel) => {\n for (let i = 0; i < channel.length; i++) {\n const noise = Math.random() * 2 - 1;\n const filtered = this.alpha * (noise - this.previousNoise) + this.previousFiltered;\n this.previousNoise = noise;\n this.previousFiltered = filtered;\n channel[i] = filtered;\n }\n });\n return true;\n }\n};\nregisterProcessor(\"random-noise-processor\", RandomNoiseProcessor);\n//#endregion\n//#region src/worklets/shared/utils/compress-utils.ts\nvar cheapSoftClipSingleSample = (sample, max = .9) => {\n const a = Math.abs(sample);\n if (a <= max) return sample;\n const x = a / max;\n const compressed = x / (1 + x);\n return Math.sign(sample) * max * compressed;\n};\n/**\n* Basic attenuation compressor for single sample\n* Note: No validation since optimized for real time use\n*/\nvar compressSingleSample = (input, threshold = .75, ratio = 4, limiter = {\n enabled: true,\n type: \"soft\",\n outputRange: {\n min: -1,\n max: 1\n }\n}) => {\n const { min, max } = limiter.outputRange;\n let x = input;\n if (Math.abs(x) > threshold) x = Math.sign(x) * (threshold + (Math.abs(x) - threshold) / ratio);\n if (limiter.enabled) {\n if (limiter.type === \"soft\") x = cheapSoftClipSingleSample(x, Math.abs(max));\n else if (limiter.type === \"hard\") x = Math.max(min, Math.min(max, x));\n }\n return x;\n};\n//#endregion\n//#region src/worklets/processors/delay/DelayBuffer.js\nvar DelayBuffer = class {\n constructor(maxDelaySamples) {\n this.buffer = new Float32Array(maxDelaySamples);\n this.writePtr = 0;\n this.readPtr = 0;\n }\n write(sample) {\n this.buffer[this.writePtr] = sample;\n }\n read() {\n return this.buffer[this.readPtr];\n }\n updatePointers(delaySamples) {\n this.writePtr = (this.writePtr + 1) % this.buffer.length;\n this.readPtr = (this.writePtr - delaySamples + this.buffer.length) % this.buffer.length;\n }\n};\n//#endregion\n//#region src/worklets/processors/delay/FeedbackDelay.js\nvar AUTO_GAIN_THRESHOLD = .8;\nvar SAFETY_GAIN_COMPENSATION = .2;\nvar FeedbackDelay = class {\n constructor(sampleRate) {\n this.sampleRate = sampleRate;\n this.buffers = [];\n this.initialized = false;\n this.autoGainEnabled = false;\n this.gainCompensation = SAFETY_GAIN_COMPENSATION;\n this.lowpassStates = [];\n this.highpassStates = [];\n this.highpassInputStates = [];\n }\n initializeBuffers(channelCount) {\n this.buffers = [];\n this.lowpassStates = [];\n this.highpassStates = [];\n this.highpassInputStates = [];\n const maxSamples = Math.floor(this.sampleRate * 2);\n for (let c = 0; c < channelCount; c++) {\n this.buffers[c] = new DelayBuffer(maxSamples);\n this.lowpassStates[c] = 0;\n this.highpassStates[c] = 0;\n this.highpassInputStates[c] = 0;\n }\n this.initialized = true;\n }\n /** Simple one-pole lowpass filter */\n lowpass(input, cutoffFreq, channelIndex) {\n if (cutoffFreq >= this.sampleRate * .4) return input;\n const omega = 2 * Math.PI * cutoffFreq / this.sampleRate;\n const alpha = Math.max(0, Math.min(.99, Math.sin(omega) / (Math.sin(omega) + Math.cos(omega))));\n this.lowpassStates[channelIndex] = alpha * input + (1 - alpha) * this.lowpassStates[channelIndex];\n return this.lowpassStates[channelIndex];\n }\n /** Simple one-pole highpass filter */\n highpass(input, cutoffFreq, channelIndex) {\n if (cutoffFreq < 5) return input;\n const omega = 2 * Math.PI * cutoffFreq / this.sampleRate;\n const alpha = Math.max(0, Math.min(.99, Math.sin(omega) / (Math.sin(omega) + Math.cos(omega))));\n const lowpassOutput = alpha * input + (1 - alpha) * this.highpassStates[channelIndex];\n const highpassOutput = input - lowpassOutput;\n this.highpassStates[channelIndex] = lowpassOutput;\n return highpassOutput;\n }\n process(inputSample, channelIndex, feedbackAmount, delayTime, lowpassFreq = 1e4, highpassFreq = 100) {\n if (!this.initialized) return inputSample;\n const buffer = this.buffers[channelIndex] || this.buffers[0];\n const delaySamples = Math.floor(this.sampleRate * delayTime);\n const delayedSample = buffer.read();\n let filteredDelay = this.highpass(delayedSample, highpassFreq, channelIndex);\n filteredDelay = this.lowpass(filteredDelay, lowpassFreq, channelIndex);\n const feedbackSample = feedbackAmount * filteredDelay + inputSample;\n let outputSample = feedbackSample;\n const compressedFeedback = compressSingleSample(feedbackSample, .5, 4, {\n enabled: true,\n outputRange: {\n min: -.99,\n max: .99\n },\n type: \"soft\"\n });\n if (this.autoGainEnabled && feedbackAmount > AUTO_GAIN_THRESHOLD) outputSample = compressedFeedback * (1 - (feedbackAmount - AUTO_GAIN_THRESHOLD) * this.gainCompensation);\n return {\n outputSample,\n feedbackSample: compressedFeedback,\n delaySamples\n };\n }\n updateBuffer(channelIndex, sample, delaySamples) {\n const buffer = this.buffers[channelIndex] || this.buffers[0];\n buffer.write(sample);\n buffer.updatePointers(delaySamples);\n }\n setAutoGain(enabled, compensation = SAFETY_GAIN_COMPENSATION) {\n this.autoGainEnabled = enabled;\n this.gainCompensation = compensation;\n }\n};\n//#endregion\n//#region src/worklets/processors/delay/feedback-delay-processor.js\nregisterProcessor(\"feedback-delay-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"feedbackAmount\",\n defaultValue: .5,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n {\n name: \"delayTime\",\n defaultValue: .5,\n minValue: .00012656238799684143,\n maxValue: 2,\n automationRate: \"k-rate\"\n },\n {\n name: \"decay\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n {\n name: \"lowpass\",\n defaultValue: 1e4,\n minValue: 100,\n maxValue: 16e3,\n automationRate: \"k-rate\"\n }\n ];\n }\n constructor() {\n super();\n this.feedbackDelay = new FeedbackDelay(sampleRate);\n this.decayStartTime = null;\n this.decayActive = false;\n this.baseFeedbackAmount = .5;\n this.setupMessageHandling();\n this.port.postMessage({ type: \"initialized\" });\n }\n setupMessageHandling() {\n this.port.onmessage = (event) => {\n switch (event.data.type) {\n case \"setAutoGain\":\n this.feedbackDelay.setAutoGain(event.data.enabled, event.data.amount);\n break;\n case \"triggerDecay\":\n this.decayStartTime = currentTime;\n this.decayActive = true;\n this.baseFeedbackAmount = event.data.baseFeedbackAmount || .5;\n break;\n case \"stopDecay\":\n this.decayActive = false;\n this.decayStartTime = null;\n }\n };\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n if (!this.feedbackDelay.initialized || this.feedbackDelay.buffers.length !== input.length) this.feedbackDelay.initializeBuffers(input.length);\n const baseFeedbackAmount = parameters.feedbackAmount[0];\n const delayTime = parameters.delayTime[0];\n const decay = parameters.decay[0];\n const lowpassFreq = parameters.lowpass[0];\n const channelCount = Math.min(input.length, output.length);\n const frameCount = output[0].length;\n for (let i = 0; i < frameCount; ++i) {\n let effectiveFeedbackAmount = baseFeedbackAmount;\n if (this.decayActive && this.decayStartTime !== null) {\n const elapsedTime = currentTime - this.decayStartTime + i / sampleRate;\n const delayCompensation = Math.min(100, .5 / delayTime);\n const timeConstant = Math.pow(decay, 5) * 1e3 * delayCompensation + .5;\n effectiveFeedbackAmount = baseFeedbackAmount * Math.exp(-elapsedTime / timeConstant);\n if (effectiveFeedbackAmount < .01) {\n this.decayActive = false;\n effectiveFeedbackAmount = 0;\n }\n }\n for (let c = 0; c < channelCount; c++) {\n const processed = this.feedbackDelay.process(input[c][i], c, effectiveFeedbackAmount, delayTime, lowpassFreq);\n output[c][i] = processed.outputSample;\n this.feedbackDelay.updateBuffer(c, processed.feedbackSample, processed.delaySamples);\n }\n }\n return true;\n }\n});\n//#endregion\n//#region src/worklets/processors/delay/delay-processor.js\nvar DEFAULT_DELAY_CONFIG = {\n CHARACTER: [\"filtered\"],\n SMOOTHING_FACTOR: {\n slowest: 1e-4,\n slow: 25e-5,\n medium: 35e-5,\n fast: 5e-4,\n veryFast: .001,\n superFast: .1,\n none: 1\n }\n};\nvar DEFAULT_CHARACTER_CONFIG = {\n bitCrushed: {\n bits: 11,\n downsample: 3\n },\n filtered: {\n freq: 900,\n Q: .15\n }\n};\nregisterProcessor(\"delay-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [{\n name: \"delayTime\",\n defaultValue: .5,\n minValue: .001,\n maxValue: 2,\n automationRate: \"k-rate\"\n }, {\n name: \"feedbackAmount\",\n defaultValue: 0,\n minValue: 0,\n maxValue: .99,\n automationRate: \"k-rate\"\n }];\n }\n constructor() {\n super();\n this.buffers = [];\n this.smoothedDelaySamples = [];\n this.smoothingFactor = DEFAULT_DELAY_CONFIG.SMOOTHING_FACTOR.slowest;\n this.characterModes = [...DEFAULT_DELAY_CONFIG.CHARACTER];\n this._bpState = [];\n this._bpFreq = DEFAULT_CHARACTER_CONFIG.filtered.freq;\n this._bpQ = DEFAULT_CHARACTER_CONFIG.filtered.Q;\n this._bpCoeffs = null;\n this._lastBpFreq = -1;\n this._lastBpQ = -1;\n this.lofiBits = DEFAULT_CHARACTER_CONFIG[\"bitCrushed\"].bits;\n this.lofiDownsample = DEFAULT_CHARACTER_CONFIG[\"bitCrushed\"].downsample;\n this._lofiSampleHold = [];\n this._lofiSampleCount = [];\n this.initialized = false;\n this.port.onmessage = (event) => {\n if (event.data && event.data.type === \"setCharacter\" && Array.isArray(event.data.modes)) this.characterModes = [...event.data.modes];\n if (event.data && event.data.type === \"setBandpassFreq\" && typeof event.data.hz === \"number\") this.setBandpassFreq(event.data.hz);\n if (event.data && event.data.type === \"trigger\") {}\n };\n this.port.postMessage({ type: \"initialized\" });\n }\n setBandpassFreq(hz) {\n this._bpFreq = hz;\n this._lastBpFreq = -1;\n }\n _updateBandpassCoeffs() {\n if (this._lastBpFreq === this._bpFreq && this._lastBpQ === this._bpQ) return;\n const bpFreq = this._bpFreq;\n const bpQ = this._bpQ;\n const omega = 2 * Math.PI * bpFreq / sampleRate;\n const alpha = Math.sin(omega) / (2 * bpQ);\n const cosw = Math.cos(omega);\n const b0 = alpha;\n const b1 = 0;\n const b2 = -alpha;\n const a0 = 1 + alpha;\n const a1 = -2 * cosw;\n const a2 = 1 - alpha;\n this._bpCoeffs = {\n b0: b0 / a0,\n b1: b1 / a0,\n b2: b2 / a0,\n a1: a1 / a0,\n a2: a2 / a0\n };\n this._lastBpFreq = bpFreq;\n this._lastBpQ = bpQ;\n }\n initializeBuffers(channelCount) {\n const maxSamples = Math.floor(sampleRate * 2);\n this.buffers = [];\n this.smoothedDelaySamples = [];\n this._lofiSampleHold = [];\n this._lofiSampleCount = [];\n for (let c = 0; c < channelCount; c++) {\n this.buffers[c] = new DelayBuffer(maxSamples);\n this.smoothedDelaySamples[c] = Math.floor(sampleRate * .5);\n this._lofiSampleHold[c] = 0;\n this._lofiSampleCount[c] = 0;\n }\n this.initialized = true;\n }\n _processLoFi(delayed, c) {\n if (this._lofiSampleCount[c] % this.lofiDownsample === 0) {\n const levels = Math.pow(2, this.lofiBits);\n delayed = Math.round(delayed * levels) / levels;\n this._lofiSampleHold[c] = delayed;\n } else delayed = this._lofiSampleHold[c];\n this._lofiSampleCount[c]++;\n return delayed;\n }\n _processBandpass(delayed, c) {\n if (!this._bpState) this._bpState = [];\n if (!this._bpState[c]) this._bpState[c] = {\n x1: 0,\n x2: 0,\n y1: 0,\n y2: 0\n };\n this._updateBandpassCoeffs();\n if (!this._bpCoeffs) return delayed;\n const { b0, b1, b2, a1, a2 } = this._bpCoeffs;\n const s = this._bpState[c];\n const y = b0 * delayed + b1 * s.x1 + b2 * s.x2 - a1 * s.y1 - a2 * s.y2;\n s.x2 = s.x1;\n s.x1 = delayed;\n s.y2 = s.y1;\n s.y1 = y;\n return y;\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output || input.length === 0 || output.length === 0) return true;\n if (!input[0] || !output[0] || input[0].length === 0 || output[0].length === 0) return true;\n if (!this.initialized || this.buffers.length !== input.length) this.initializeBuffers(input.length);\n const delayTime = parameters.delayTime[0];\n const feedbackAmount = parameters.feedbackAmount[0];\n const targetDelaySamples = sampleRate * delayTime;\n const channelCount = Math.min(input.length, output.length);\n const frameCount = output[0].length;\n const smoothing = this.smoothingFactor;\n for (let i = 0; i < frameCount; ++i) for (let c = 0; c < channelCount; c++) {\n const buf = this.buffers[c];\n if (!buf) continue;\n this.smoothedDelaySamples[c] += (targetDelaySamples - this.smoothedDelaySamples[c]) * smoothing;\n const smoothedDelay = this.smoothedDelaySamples[c];\n const intDelay = Math.floor(smoothedDelay);\n const frac = smoothedDelay - intDelay;\n const readPtrA = (buf.writePtr - intDelay + buf.buffer.length) % buf.buffer.length;\n const readPtrB = (readPtrA - 1 + buf.buffer.length) % buf.buffer.length;\n const sampleA = buf.buffer[readPtrA];\n const sampleB = buf.buffer[readPtrB];\n let delayed = sampleA * (1 - frac) + sampleB * frac;\n for (const mode of this.characterModes) if (mode === \"bitCrushed\") delayed = this._processLoFi(delayed, c);\n else if (mode === \"filtered\") delayed = this._processBandpass(delayed, c);\n output[c][i] = compressSingleSample(delayed, .75, 4, {\n enabled: true,\n type: \"soft\",\n outputRange: {\n min: -.9,\n max: .9\n }\n });\n const inputSample = input[c] && input[c][i] !== void 0 ? input[c][i] : 0;\n buf.write(inputSample + delayed * feedbackAmount);\n buf.updatePointers(intDelay);\n }\n return true;\n }\n});\n//#endregion\n//#region src/worklets/processors/reverb/dattorro-reverb-processor.js\nvar DattorroReverb = class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n [\n \"preDelay\",\n 0,\n 0,\n sampleRate - 1,\n \"k-rate\"\n ],\n [\n \"bandwidth\",\n .9999,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"inputDiffusion1\",\n .75,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"inputDiffusion2\",\n .625,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"decay\",\n .5,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"decayDiffusion1\",\n .7,\n 0,\n .999999,\n \"k-rate\"\n ],\n [\n \"decayDiffusion2\",\n .5,\n 0,\n .999999,\n \"k-rate\"\n ],\n [\n \"damping\",\n .005,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"excursionRate\",\n .5,\n 0,\n 2,\n \"k-rate\"\n ],\n [\n \"excursionDepth\",\n .7,\n 0,\n 2,\n \"k-rate\"\n ],\n [\n \"wet\",\n .3,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"dry\",\n .6,\n 0,\n 1,\n \"k-rate\"\n ]\n ].map((x) => /* @__PURE__ */ new Object({\n name: x[0],\n defaultValue: x[1],\n minValue: x[2],\n maxValue: x[3],\n automationRate: x[4]\n }));\n }\n constructor(options) {\n super(options);\n this._Delays = [];\n this._pDLength = sampleRate + (128 - sampleRate % 128);\n this._preDelay = new Float32Array(this._pDLength);\n this._pDWrite = 0;\n this._lp1 = 0;\n this._lp2 = 0;\n this._lp3 = 0;\n this._excPhase = 0;\n const SHORT_DELAY_SCALE = .5;\n [\n .004771345,\n .003595309,\n .012734787,\n .009307483,\n .022579886,\n .149625349,\n .060481839,\n .1249958,\n .030509727,\n .141695508,\n .089244313,\n .106280031\n ].map((x) => x * SHORT_DELAY_SCALE).forEach((x) => this.makeDelay(x));\n this._taps = Int16Array.from([\n .008937872,\n .099929438,\n .064278754,\n .067067639,\n .066866033,\n .006283391,\n .035818689,\n .011861161,\n .121870905,\n .041262054,\n .08981553,\n .070931756,\n .011256342,\n .004065724\n ], (x) => Math.round(x * sampleRate));\n this.port.postMessage({ type: \"initialized\" });\n }\n makeDelay(length) {\n let len = Math.round(length * sampleRate);\n let nextPow2 = 2 ** Math.ceil(Math.log2(len));\n this._Delays.push([\n new Float32Array(nextPow2),\n len - 1,\n 0,\n nextPow2 - 1\n ]);\n }\n writeDelay(index, data) {\n return this._Delays[index][0][this._Delays[index][1]] = data;\n }\n readDelay(index) {\n return this._Delays[index][0][this._Delays[index][2]];\n }\n readDelayAt(index, i) {\n let d = this._Delays[index];\n return d[0][d[2] + i & d[3]];\n }\n readDelayCAt(index, i) {\n let d = this._Delays[index], frac = i - ~~i, int = ~~i + d[2] - 1, mask = d[3];\n let x0 = d[0][int++ & mask], x1 = d[0][int++ & mask], x2 = d[0][int++ & mask], x3 = d[0][int & mask];\n let a = (3 * (x1 - x2) - x0 + x3) / 2, b = 2 * x2 + x0 - (5 * x1 + x3) / 2, c = (x2 - x0) / 2;\n return ((a * frac + b) * frac + c) * frac + x1;\n }\n process(inputs, outputs, parameters) {\n const TWO_PI = 6.283185307179586;\n const TWO_PI_DETUNE = 6.284702653297906;\n const pd = ~~parameters.preDelay[0], bw = parameters.bandwidth[0], fi = parameters.inputDiffusion1[0], si = parameters.inputDiffusion2[0], dc = parameters.decay[0], ft = parameters.decayDiffusion1[0], st = parameters.decayDiffusion2[0], dp = 1 - parameters.damping[0], ex = parameters.excursionRate[0] / sampleRate, ed = parameters.excursionDepth[0] * sampleRate / 1e3, we = parameters.wet[0] * .6, dr = parameters.dry[0];\n if (inputs[0].length == 2) for (let i = 127; i >= 0; i--) {\n this._preDelay[this._pDWrite + i] = (inputs[0][0][i] + inputs[0][1][i]) * .5;\n outputs[0][0][i] = inputs[0][0][i] * dr;\n outputs[0][1][i] = inputs[0][1][i] * dr;\n }\n else if (inputs[0].length > 0) {\n this._preDelay.set(inputs[0][0], this._pDWrite);\n for (let i = 127; i >= 0; i--) outputs[0][0][i] = outputs[0][1][i] = inputs[0][0][i] * dr;\n } else this._preDelay.set(/* @__PURE__ */ new Float32Array(128), this._pDWrite);\n let i = 0;\n while (i < 128) {\n let lo = 0, ro = 0;\n this._lp1 += bw * (this._preDelay[(this._pDLength + this._pDWrite - pd + i) % this._pDLength] - this._lp1);\n let pre = this.writeDelay(0, this._lp1 - fi * this.readDelay(0));\n pre = this.writeDelay(1, fi * (pre - this.readDelay(1)) + this.readDelay(0));\n pre = this.writeDelay(2, fi * pre + this.readDelay(1) - si * this.readDelay(2));\n pre = this.writeDelay(3, si * (pre - this.readDelay(3)) + this.readDelay(2));\n let split = si * pre + this.readDelay(3);\n let exc = ed * (1 + Math.cos(this._excPhase * TWO_PI));\n let exc2 = ed * (1 + Math.sin(this._excPhase * TWO_PI_DETUNE));\n let temp = this.writeDelay(4, split + dc * this.readDelay(11) + ft * this.readDelayCAt(4, exc));\n this.writeDelay(5, this.readDelayCAt(4, exc) - ft * temp);\n this._lp2 += dp * (this.readDelay(5) - this._lp2);\n temp = this.writeDelay(6, dc * this._lp2 - st * this.readDelay(6));\n this.writeDelay(7, this.readDelay(6) + st * temp);\n temp = this.writeDelay(8, split + dc * this.readDelay(7) + ft * this.readDelayCAt(8, exc2));\n this.writeDelay(9, this.readDelayCAt(8, exc2) - ft * temp);\n this._lp3 += dp * (this.readDelay(9) - this._lp3);\n temp = this.writeDelay(10, dc * this._lp3 - st * this.readDelay(10));\n this.writeDelay(11, this.readDelay(10) + st * temp);\n lo = this.readDelayAt(9, this._taps[0]) + this.readDelayAt(9, this._taps[1]) - this.readDelayAt(10, this._taps[2]) + this.readDelayAt(11, this._taps[3]) - this.readDelayAt(5, this._taps[4]) - this.readDelayAt(6, this._taps[5]) - this.readDelayAt(7, this._taps[6]);\n ro = this.readDelayAt(5, this._taps[7]) + this.readDelayAt(5, this._taps[8]) - this.readDelayAt(6, this._taps[9]) + this.readDelayAt(7, this._taps[10]) - this.readDelayAt(9, this._taps[11]) - this.readDelayAt(10, this._taps[12]) - this.readDelayAt(11, this._taps[13]);\n outputs[0][0][i] += lo * we;\n outputs[0][1][i] += ro * we;\n this._excPhase += ex;\n if (this._excPhase >= 1) this._excPhase -= 1;\n i++;\n const delays = this._Delays;\n for (let j = 0; j < delays.length; j++) {\n const d = delays[j];\n d[1] = d[1] + 1 & d[3];\n d[2] = d[2] + 1 & d[3];\n }\n }\n this._pDWrite = (this._pDWrite + 128) % this._pDLength;\n return true;\n }\n};\nregisterProcessor(\"dattorro-reverb-processor\", DattorroReverb);\n//#endregion\n//#region src/worklets/processors/distortion/distortion-processor.js\nvar Distortion = class {\n constructor() {\n this.limitingMode = \"hard-clipping\";\n }\n applyDrive(sample, driveAmount) {\n if (driveAmount <= 0) return sample;\n return sample * (1 + driveAmount * 3);\n }\n applyClipping(sample, clippingAmount, clipThreshold) {\n if (clippingAmount <= 0) return sample;\n let clippedSample;\n switch (this.limitingMode) {\n case \"soft-clipping\":\n clippedSample = clipThreshold * Math.tanh(sample / clipThreshold);\n break;\n case \"hard-clipping\":\n clippedSample = Math.max(-clipThreshold, Math.min(clipThreshold, sample));\n break;\n default: clippedSample = sample;\n }\n if (clipThreshold < .08) {\n const makeupGain = Math.min(2, Math.pow(.1 / clipThreshold, .5));\n clippedSample *= makeupGain;\n }\n return sample * (1 - clippingAmount) + clippedSample * clippingAmount;\n }\n setLimitingMode(mode) {\n this.limitingMode = mode;\n }\n};\nregisterProcessor(\"distortion-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"distortionDrive\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\"\n },\n {\n name: \"clippingAmount\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\"\n },\n {\n name: \"clippingThreshold\",\n defaultValue: .5,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n }\n ];\n }\n constructor() {\n super();\n this.distortion = new Distortion();\n this.setupMessageHandling();\n this.port.postMessage({ type: \"initialized\" });\n }\n setupMessageHandling() {\n this.port.onmessage = (event) => {\n switch (event.data.type) {\n case \"setLimitingMode\":\n this.distortion.setLimitingMode(event.data.mode);\n break;\n default: console.warn(\"distortion-processor: Unsupported message\");\n }\n };\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n const clipThreshold = parameters.clippingThreshold[0];\n for (let i = 0; i < output[0].length; ++i) {\n const distortionDrive = parameters.distortionDrive[Math.min(i, parameters.distortionDrive.length - 1)];\n const clippingAmount = parameters.clippingAmount[Math.min(i, parameters.clippingAmount.length - 1)];\n for (let c = 0; c < Math.min(input.length, output.length); c++) {\n let sample = input[c][i];\n sample = this.distortion.applyDrive(sample, distortionDrive);\n sample = this.distortion.applyClipping(sample, clippingAmount, clipThreshold);\n output[c][i] = Math.max(-.999, Math.min(.999, sample));\n }\n }\n return true;\n }\n});\n//#endregion\n//#region src/worklets/processors/follower/envelope-follower-processor.js\nregisterProcessor(\"envelope-follower-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"inputGain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 10,\n automationRate: \"k-rate\"\n },\n {\n name: \"outputGain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 10,\n automationRate: \"k-rate\"\n },\n {\n name: \"attack\",\n defaultValue: .003,\n minValue: .001,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n {\n name: \"release\",\n defaultValue: .05,\n minValue: .001,\n maxValue: 5,\n automationRate: \"k-rate\"\n }\n ];\n }\n constructor() {\n super();\n this.envelope = 0;\n this.gateThreshold = .005;\n this.debugCounter = 0;\n this.port.postMessage({ type: \"initialized\" });\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n const channel = inputs[0][0];\n if (!input || !output || !channel || input.length === 0 || output.length === 0 || channel.length === 0) return true;\n const inChannel = input[0];\n if (!inChannel || inChannel.length === 0) return true;\n const attack = parameters.attack[0];\n const release = parameters.release[0];\n const inputGain = parameters.inputGain[0];\n const outputGain = parameters.outputGain[0];\n const attackCoeff = Math.exp(-1 / (attack * sampleRate));\n const releaseCoeff = Math.exp(-1 / (release * sampleRate));\n for (let sample = 0; sample < output[0].length; sample++) {\n const inputLevel = Math.abs((input[0][sample] || 0) * inputGain);\n if (inputLevel > 1e-6) {\n if (inputLevel > this.envelope) this.envelope = inputLevel + (this.envelope - inputLevel) * attackCoeff;\n else this.envelope = inputLevel + (this.envelope - inputLevel) * releaseCoeff;\n } else this.envelope *= releaseCoeff;\n if (this.envelope < this.gateThreshold) this.envelope = 0;\n const finalOutput = this.envelope * outputGain;\n for (let channel = 0; channel < output.length; channel++) output[channel][sample] = finalOutput;\n }\n return true;\n }\n});\n//#endregion\n", Tt = !1;
|
|
4683
|
+
}, wt = "//#region src/utils/search/findClosest.ts\n/**\n* Generic binary search that finds the closest element using a custom comparison function\n* @param sortedArray - Array sorted according to the compareValue function\n* @param target - Target value to search for\n* @param getValue - Function to extract comparison value from array elements (defaults to identity for number arrays)\n* @param getDistance - Optional function to calculate distance (defaults to absolute difference)\n* @returns The array index of the element which value is closest to the target value\n*/\nfunction findClosestIdx(sortedArray, target, direction = \"any\", getValue = (x) => x, getDistance = (a, b) => Math.abs(a - b)) {\n if (sortedArray.length === 0) throw new Error(\"Array cannot be empty\");\n if (sortedArray.length === 1) return 0;\n const targetValue = target;\n const firstValue = getValue(sortedArray[0]);\n const lastValue = getValue(sortedArray[sortedArray.length - 1]);\n if (targetValue <= firstValue) return 0;\n if (targetValue >= lastValue) return sortedArray.length - 1;\n let left = 0;\n let right = sortedArray.length - 1;\n while (left < right - 1) {\n const mid = Math.floor((left + right) / 2);\n const midValue = getValue(sortedArray[mid]);\n if (midValue === targetValue) return mid;\n else if (midValue < targetValue) left = mid;\n else right = mid;\n }\n if (direction === \"left\") return left;\n if (direction === \"right\") return right;\n return getDistance(getValue(sortedArray[left]), targetValue) <= getDistance(getValue(sortedArray[right]), targetValue) ? left : right;\n}\n/**\n* Generic binary search that finds the closest element using a custom comparison function\n* @param sortedArray - Array sorted according to the compareValue function\n* @param target - Target value to search for\n* @param getValue - Function to extract comparison value from array elements (defaults to identity for number arrays)\n* @param getDistance - Optional function to calculate distance (defaults to absolute difference)\n* @returns The array element which value is closest to the target value\n*/\nfunction findClosest(sortedArray, target, direction = \"any\", getValue = (x) => x, getDistance = (a, b) => Math.abs(a - b)) {\n return sortedArray[findClosestIdx(sortedArray, target, direction, getValue, getDistance)];\n}\nvar SAMPLE_PLAYER_WORKLET_AUDIOPARAM_DESCRIPTORS = Object.values({\n masterGain: {\n name: \"masterGain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 2,\n automationRate: \"k-rate\"\n },\n envGain: {\n name: \"envGain\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\"\n },\n velocity: {\n name: \"velocity\",\n defaultValue: 100,\n minValue: 0,\n maxValue: 127,\n automationRate: \"k-rate\"\n },\n pan: {\n name: \"pan\",\n defaultValue: 0,\n minValue: -1,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n playbackRate: {\n name: \"playbackRate\",\n defaultValue: 1,\n minValue: .1,\n maxValue: 24,\n automationRate: \"a-rate\"\n },\n loopStart: {\n name: \"loopStart\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 99999,\n automationRate: \"k-rate\"\n },\n loopEnd: {\n name: \"loopEnd\",\n defaultValue: 99999,\n minValue: 0,\n maxValue: 99999,\n automationRate: \"k-rate\"\n },\n startPoint: {\n name: \"startPoint\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 9999,\n automationRate: \"k-rate\"\n },\n endPoint: {\n name: \"endPoint\",\n defaultValue: 9999,\n minValue: 0,\n maxValue: 9999,\n automationRate: \"k-rate\"\n },\n playbackPosition: {\n name: \"playbackPosition\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 99999,\n automationRate: \"k-rate\"\n },\n loopDurationDriftAmount: {\n name: \"loopDurationDriftAmount\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n maxLoopCount: {\n name: \"maxLoopCount\",\n defaultValue: 999999,\n minValue: 1,\n maxValue: 999999,\n automationRate: \"k-rate\"\n },\n tempo: {\n name: \"tempo\",\n defaultValue: 120,\n minValue: 20,\n maxValue: 300,\n automationRate: \"k-rate\"\n }\n});\n//#endregion\n//#region src/worklets/processors/play/sample-player-processor.js\nvar SamplePlayerProcessor = class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return SAMPLE_PLAYER_WORKLET_AUDIOPARAM_DESCRIPTORS;\n }\n constructor() {\n super();\n this.layers = [];\n this.layerGain = 1;\n this.minZeroCrossing = 0;\n this.maxZeroCrossing = 0;\n this.usePlaybackPosition = false;\n this.enableLoopSmoothing = true;\n this.enableAdaptiveDrift = true;\n this.enableAmplitudeCompensation = true;\n this.syncLoopToTempo = false;\n this.keytrackLoopAmount = 0;\n this.durationPreservation = {\n enabled: false,\n maxDriftSamples: Math.floor(sampleRate * .04),\n timelinePosition: 0,\n resetPending: false\n };\n this.PITCH_PRESERVATION_THRESHOLD = Math.floor(sampleRate * .061);\n this.AMPLITUDE_COMPENSATION_THRESHOLD = Math.floor(sampleRate / 16.35);\n this.port.onmessage = this.#handleMessage.bind(this);\n this.#resetState();\n this.port.postMessage({ type: \"initialized\" });\n }\n /** Authority layer. All range and duration math reads through this. */\n get buffer() {\n return this.layers[0] ?? null;\n }\n #handleMessage(event) {\n const { type, value, buffer, layers, timestamp, durationSeconds, zeroCrossings, allowedPeriods, playbackDirection } = event.data;\n switch (type) {\n case \"voice:reset\":\n this.#resetState();\n this.port.postMessage({ type: \"voice:reset\" });\n break;\n case \"voice:setBuffer\":\n case \"voice:setLayers\":\n this.#resetState();\n this.zeroCrossings = [];\n this.minZeroCrossing = 0;\n this.maxZeroCrossing = 0;\n this.layers = (layers ?? (buffer ? [buffer] : [])).filter(Boolean);\n this.layerGain = this.layers.length ? 1 / this.layers.length : 1;\n this.port.postMessage({\n type: \"voice:loaded\",\n durationSeconds,\n time: currentTime\n });\n break;\n case \"voice:setZeroCrossings\":\n this.zeroCrossings = (zeroCrossings || []).map((timeSec) => timeSec * sampleRate);\n if (this.zeroCrossings.length > 0) {\n this.minZeroCrossing = this.zeroCrossings[0];\n this.maxZeroCrossing = this.zeroCrossings[this.zeroCrossings.length - 1];\n }\n break;\n case \"voice:start\":\n this.isReleasing = false;\n this.isPlaying = true;\n this.loopCount = 0;\n this.playbackPosition = 0;\n this.port.postMessage({\n type: \"voice:started\",\n time: timestamp || currentTime\n });\n break;\n case \"voice:release\":\n this.isReleasing = true;\n this.port.postMessage({\n type: \"voice:releasing\",\n time: currentTime\n });\n break;\n case \"voice:stop\":\n this.#stop();\n break;\n case \"setLoopEnabled\":\n this.loopEnabled = value;\n this.port.postMessage({\n type: \"loop:enabled\",\n enabled: value\n });\n break;\n case \"setPanDriftEnabled\":\n this.panDriftEnabled = value;\n break;\n case \"voice:setPlaybackDirection\": {\n const reverse = playbackDirection === \"reverse\";\n if (reverse !== this.reversePlayback && this.playbackPosition > 0) this.playbackPosition += reverse ? 1 : -1;\n this.reversePlayback = reverse;\n this.port.postMessage({\n type: \"voice:playbackDirectionChange\",\n playbackDirection\n });\n break;\n }\n case \"voice:usePlaybackPosition\":\n this.usePlaybackPosition = value;\n break;\n case \"syncLoopToTempo\":\n this.syncLoopToTempo = value;\n this.port.postMessage({\n type: \"loop:syncToTempo\",\n enabled: value\n });\n break;\n case \"setKeytrackLoopAmount\":\n this.keytrackLoopAmount = Math.max(0, Math.min(1, value));\n break;\n case \"setPreserveDuration\":\n this.durationPreservation.enabled = Boolean(value);\n this.#resetDurationPreservation(this.playbackPosition);\n }\n }\n #resetState() {\n this.isPlaying = false;\n this.isReleasing = false;\n this.loopEnabled = false;\n this.velocitySensitivity = 1;\n this.reversePlayback = false;\n this.playbackPosition = 0;\n this.debugCounter = 0;\n this.loopCount = 0;\n this.applyClickCompensation = false;\n this.loopClickCompensation = 0;\n this.driftUpdateCounter = 0;\n this.currentLoopDrift = 0;\n this.currentPanDrift = 0;\n this.panDriftEnabled = true;\n this.nextDriftGenerated = false;\n this.loopAmplitudeGain = 1;\n this.lastAnalyzedLoopStart = -1;\n this.lastAnalyzedLoopEnd = -1;\n this.#resetDurationPreservation();\n }\n #stop() {\n this.isPlaying = false;\n this.isReleasing = false;\n this.playbackPosition = 0;\n this.port.postMessage({ type: \"voice:stopped\" });\n }\n #smoothLoopWrap(lastLoopSample, newFirstSample) {\n const discontinuity = lastLoopSample - newFirstSample;\n if (this.enableLoopSmoothing && Math.abs(discontinuity) > .01) {\n this.loopClickCompensation = discontinuity * .5;\n this.compensationDecay = .9;\n this.applyClickCompensation = true;\n }\n }\n #clamp = (value, min, max) => Math.max(min, Math.min(max, value));\n #clampZeroCrossing = (value) => this.#clamp(value, this.minZeroCrossing, this.maxZeroCrossing);\n #findNearestZeroCrossing(position, direction = \"any\", maxDistance = null) {\n if (!this.zeroCrossings || this.zeroCrossings.length === 0) return position;\n const closestValue = findClosest(this.zeroCrossings, position, direction);\n if (maxDistance !== null && Math.abs(closestValue - position) > maxDistance) return position;\n return closestValue;\n }\n /**\n * Convert normalized position (0-1) to sample index\n * @param {number} normalizedPosition - Position as 0-1 value\n * @returns {number} - Sample index\n */\n #normalizedToSamples(normalizedPosition) {\n if (!this.buffer || !this.buffer[0]) return 0;\n return normalizedPosition * this.buffer[0].length;\n }\n /**\n * Convert sample index to normalized position (0-1)\n * @param {number} sampleIndex - Sample index\n * @returns {number} - Normalized position 0-1\n */\n #samplesToNormalized(sampleIndex) {\n if (!this.buffer || !this.buffer[0]) return 0;\n return sampleIndex / this.buffer[0].length;\n }\n /**\n * Convert MIDI velocity (0-127) to gain multiplier (0-1)\n * @param {number} midiVelocity - MIDI velocity 0-127\n * @returns {number} - Gain multiplier 0-1\n */\n #midiVelocityToGain(midiVelocity) {\n return Math.max(0, Math.min(1, midiVelocity / 127));\n }\n /**\n * Get buffer duration in seconds\n * @returns {number} - Buffer duration in seconds\n */\n #getBufferDurationSeconds() {\n return (this.buffer?.[0]?.length || 0) / sampleRate;\n }\n /**\n * Calculate musical note durations in samples for given tempo\n * @param {number} tempo - BPM\n * @returns {Object} - Musical note durations in samples\n */\n #getMusicalNoteDurations(tempo) {\n const beatsPerSecond = tempo / 60;\n const samplesPerBeat = sampleRate / beatsPerSecond;\n return {\n whole: samplesPerBeat * 4,\n half: samplesPerBeat * 2,\n quarter: samplesPerBeat,\n eighth: samplesPerBeat / 2,\n sixteenth: samplesPerBeat / 4,\n thirtySecond: samplesPerBeat / 8,\n quarterTriplet: samplesPerBeat * 2 / 3,\n eighthTriplet: samplesPerBeat / 2 * 2 / 3,\n sixteenthTriplet: samplesPerBeat / 4 * 2 / 3\n };\n }\n /**\n * Quantize loop duration to nearest musical interval (skips if below the smallest quantize option)\n * @param {number} loopDurationSamples - Current loop duration in samples\n * @param {number} tempo - Current tempo in BPM\n * @param {number} playbackRate - Current playback rate\n * @returns {number} - Quantized loop duration in samples\n */\n #quantizeLoopDuration(loopDurationSamples, tempo, playbackRate) {\n if (!this.syncLoopToTempo) return loopDurationSamples;\n const noteDurations = this.#getMusicalNoteDurations(tempo);\n const effectiveDuration = loopDurationSamples / Math.abs(playbackRate);\n if (effectiveDuration < noteDurations.thirtySecond) return loopDurationSamples;\n const intervals = Object.values(noteDurations);\n let closestInterval = intervals[0];\n let smallestDiff = Math.abs(effectiveDuration - closestInterval);\n for (const interval of intervals) {\n const diff = Math.abs(effectiveDuration - interval);\n if (diff < smallestDiff) {\n smallestDiff = diff;\n closestInterval = interval;\n }\n }\n return Math.floor(closestInterval * Math.abs(playbackRate));\n }\n /**\n * Extract and convert all position parameters from seconds to samples\n * @param {Object} parameters - AudioWorkletProcessor parameters\n * @returns {Object} - Converted parameters in samples\n */\n #extractPositionParams(parameters) {\n return {\n startPointSamples: Math.floor(parameters.startPoint[0] * sampleRate),\n endPointSamples: Math.floor(parameters.endPoint[0] * sampleRate),\n loopStartSamples: Math.floor(parameters.loopStart[0] * sampleRate),\n loopEndSamples: Math.floor(parameters.loopEnd[0] * sampleRate)\n };\n }\n /**\n * Calculate effective playback range in samples\n * @param {Object} params - Position parameters from #extractPositionParams\n * @returns {Object} - Effective start and end positions\n */\n #calculatePlaybackRange(params) {\n const bufferLength = this.buffer?.[0]?.length || 0;\n const start = Math.max(0, params.startPointSamples);\n const end = params.endPointSamples > start ? Math.min(bufferLength, params.endPointSamples) : bufferLength;\n const snappedStart = this.#findNearestZeroCrossing(start, \"right\");\n const snappedEnd = this.#findNearestZeroCrossing(end, \"left\");\n return {\n startSamples: snappedStart,\n endSamples: snappedEnd,\n durationSamples: snappedEnd - snappedStart\n };\n }\n /**\n * Calculate effective loop range in samples with optional drift\n * @param {Object} params - Position parameters from #extractPositionParams\n * @param {Object} playbackRange - Range from #calculatePlaybackRange\n * @param {number} driftAmount - Loop duration drift amount (0-1)\n * @param {number} tempo - Current tempo in BPM\n * @param {number} playbackRate - Current playback rate\n * @returns {Object} - Effective loop start and end positions with drift applied\n */\n #calculateLoopRange(params, playbackRange, driftAmount = 0, tempo = 120, playbackRate = 1) {\n const lpStart = params.loopStartSamples;\n const lpEnd = params.loopEndSamples;\n let calcLoopStart = lpStart < lpEnd && lpStart >= 0 ? lpStart : playbackRange.startSamples;\n let calcLoopEnd = lpEnd > lpStart && lpEnd <= playbackRange.endSamples ? lpEnd : playbackRange.endSamples;\n let baseDuration = calcLoopEnd - calcLoopStart;\n if (this.syncLoopToTempo) {\n const quantizedDuration = this.#quantizeLoopDuration(baseDuration, tempo, playbackRate);\n calcLoopEnd = calcLoopStart + quantizedDuration;\n calcLoopEnd = Math.min(calcLoopEnd, playbackRange.endSamples);\n }\n if (baseDuration > this.PITCH_PRESERVATION_THRESHOLD && this.keytrackLoopAmount > 0 && !this.syncLoopToTempo) {\n const scale = 1 + this.keytrackLoopAmount * (Math.abs(playbackRate) - 1);\n baseDuration = Math.max(1, Math.floor(baseDuration * scale));\n calcLoopEnd = calcLoopStart + baseDuration;\n }\n baseDuration = calcLoopEnd - calcLoopStart;\n if (baseDuration > this.PITCH_PRESERVATION_THRESHOLD) calcLoopStart = this.#findNearestZeroCrossing(calcLoopStart, \"right\");\n if (driftAmount > 0 && this.loopEnabled) {\n if (!this.nextDriftGenerated || this.loopCount === 0) {\n const updateInterval = baseDuration <= this.PITCH_PRESERVATION_THRESHOLD ? Math.max(1, Math.floor(this.PITCH_PRESERVATION_THRESHOLD / baseDuration)) : 1;\n if (this.driftUpdateCounter % updateInterval === 0) {\n this.currentLoopDrift = this.#generateLoopDrift(driftAmount, baseDuration);\n if (this.panDriftEnabled && driftAmount > 0 && this.loopCount > 0) {\n const panDriftAmountScalar = 1e-4;\n this.currentPanDrift = this.currentLoopDrift * panDriftAmountScalar;\n } else this.currentPanDrift = 0;\n }\n this.driftUpdateCounter++;\n this.nextDriftGenerated = true;\n }\n const driftedLoopEnd = calcLoopEnd + this.currentLoopDrift;\n const minLoopDuration = Math.max(1, Math.floor(baseDuration * .1));\n const maxLoopEnd = Math.max(playbackRange.endSamples, calcLoopEnd);\n calcLoopEnd = Math.max(calcLoopStart + minLoopDuration, Math.min(maxLoopEnd, driftedLoopEnd));\n } else this.currentPanDrift = 0;\n if (baseDuration > this.PITCH_PRESERVATION_THRESHOLD && calcLoopEnd <= playbackRange.endSamples) calcLoopEnd = Math.max(calcLoopStart + 1, this.#findNearestZeroCrossing(calcLoopEnd, \"left\"));\n const loopDuration = calcLoopEnd - calcLoopStart;\n return {\n loopStartSamples: calcLoopStart,\n loopEndSamples: calcLoopEnd,\n loopDurationSamples: loopDuration\n };\n }\n #getSafeParam(paramArray, index, isConstant) {\n return isConstant ? paramArray[0] : paramArray[Math.min(index, paramArray.length - 1)];\n }\n #getConstantFlags(parameters) {\n this.constantFlags ??= {\n envGain: true,\n playbackRate: true\n };\n this.constantFlags.envGain = parameters.envGain.length === 1;\n this.constantFlags.playbackRate = parameters.playbackRate.length === 1;\n return this.constantFlags;\n }\n #resetDurationPreservation(position = 0) {\n this.durationPreservation.timelinePosition = position;\n this.durationPreservation.resetPending = false;\n }\n #isDurationPreservationActive(loopRange) {\n return this.durationPreservation.enabled && Boolean(this.zeroCrossings?.length) && (!this.loopEnabled || loopRange.loopDurationSamples > this.PITCH_PRESERVATION_THRESHOLD);\n }\n #prepareDurationPreservingSample(playbackRate, loopRange) {\n const state = this.durationPreservation;\n if (!this.#isDurationPreservationActive(loopRange)) return null;\n if (Math.abs(this.playbackPosition - state.timelinePosition) > state.maxDriftSamples) state.resetPending = true;\n if (!state.resetPending) return null;\n const direction = playbackRate < 0 ? \"left\" : \"right\";\n const outgoingZero = this.#findNearestZeroCrossing(this.playbackPosition, direction);\n if (Math.abs(outgoingZero - this.playbackPosition) > Math.abs(playbackRate)) return null;\n this.playbackPosition = outgoingZero;\n state.resetPending = false;\n return this.#findNearestZeroCrossing(state.timelinePosition, \"any\", state.maxDriftSamples);\n }\n #advanceDurationPreservingPlayback(playbackRate, resetTarget, loopRange, canWrapLoop) {\n const state = this.durationPreservation;\n this.playbackPosition = resetTarget === null ? this.playbackPosition + playbackRate : resetTarget;\n if (this.#isDurationPreservationActive(loopRange)) {\n state.timelinePosition += playbackRate < 0 ? -1 : 1;\n if (canWrapLoop && playbackRate >= 0 && state.timelinePosition >= loopRange.loopEndSamples) state.timelinePosition = loopRange.loopStartSamples;\n else if (canWrapLoop && playbackRate < 0 && state.timelinePosition <= loopRange.loopStartSamples) state.timelinePosition = loopRange.loopEndSamples - 1;\n } else this.#resetDurationPreservation(this.playbackPosition);\n }\n /**\n * Generate a new drift amount for the current loop iteration\n * @param {number} driftAmount - Maximum drift amount (0-1)\n * @param {number} baseDuration - Base loop duration in samples\n * @returns {number} - Drift amount in samples\n */\n #generateLoopDrift(driftAmount, baseDuration) {\n if (driftAmount <= 0) return 0;\n const randomFactor = (Math.random() - .5) * 2;\n let effectiveDriftAmount = driftAmount;\n if (this.enableAdaptiveDrift) {\n const shortThreshold = 1024;\n const longThreshold = 8192;\n if (baseDuration < shortThreshold) effectiveDriftAmount *= .1;\n else if (baseDuration < longThreshold) {\n const scaleFactor = .1 + .9 * (baseDuration - shortThreshold) / 7168;\n effectiveDriftAmount *= scaleFactor;\n }\n }\n const maxDriftSamples = effectiveDriftAmount * baseDuration;\n return Math.floor(randomFactor * maxDriftSamples);\n }\n /**\n * Analyze loop amplitude and calculate makeup gain for short loops\n * @param {number} loopStart - Loop start position in samples\n * @param {number} loopEnd - Loop end position in samples\n * @returns {number} - Makeup gain multiplier (1.0 = no change)\n */\n #analyzeLoopAmplitude(loopStart, loopEnd) {\n if (!this.enableAmplitudeCompensation || !this.buffer || !this.buffer[0]) return 1;\n if (loopEnd - loopStart >= this.AMPLITUDE_COMPENSATION_THRESHOLD) return 1;\n if (loopStart === this.lastAnalyzedLoopStart && loopEnd === this.lastAnalyzedLoopEnd) return this.loopAmplitudeGain;\n let sumSquares = 0;\n let sampleCount = 0;\n const channel = this.buffer[0];\n const startIndex = Math.floor(loopStart);\n const endIndex = Math.floor(loopEnd);\n for (let i = startIndex; i < endIndex && i < channel.length; i++) {\n const sample = channel[i];\n sumSquares += sample * sample;\n sampleCount++;\n }\n if (sampleCount === 0) return 1;\n const rmsAmplitude = Math.sqrt(sumSquares / sampleCount);\n const targetAmplitude = .3;\n let makeupGain = 1;\n if (rmsAmplitude < targetAmplitude) {\n makeupGain = targetAmplitude / Math.max(rmsAmplitude, .001);\n makeupGain = Math.min(2, makeupGain);\n }\n this.lastAnalyzedLoopStart = loopStart;\n this.lastAnalyzedLoopEnd = loopEnd;\n this.loopAmplitudeGain = makeupGain;\n return makeupGain;\n }\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n this.debugCounter++;\n if (!output || !this.isPlaying || !this.buffer?.[0]?.length) return true;\n const masterGain = parameters.masterGain[0];\n const positionParams = this.#extractPositionParams(parameters);\n const playbackRange = this.#calculatePlaybackRange(positionParams);\n const effectivePlaybackRate = parameters.playbackRate[0];\n const tempo = parameters.tempo[0];\n const loopRange = this.#calculateLoopRange(positionParams, playbackRange, parameters.loopDurationDriftAmount[0], tempo, effectivePlaybackRate);\n const amplitudeGain = this.#analyzeLoopAmplitude(loopRange.loopStartSamples, loopRange.loopEndSamples);\n const velocityGain = this.#midiVelocityToGain(parameters.velocity[0]) * this.velocitySensitivity;\n const basePan = parameters.pan[0];\n const effectivePan = this.panDriftEnabled ? Math.max(-1, Math.min(1, basePan + this.currentPanDrift)) : basePan;\n let outputChannels;\n if (output instanceof Float32Array) outputChannels = [output];\n else if (Array.isArray(output) && output.every((ch) => ch instanceof Float32Array)) outputChannels = output;\n else {\n console.error(\"Unexpected output structure:\", {\n outputType: typeof output,\n isArray: Array.isArray(output),\n constructor: output?.constructor?.name,\n length: output?.length\n });\n return true;\n }\n const numChannels = outputChannels.length;\n const isConstant = this.#getConstantFlags(parameters);\n const silencePadTail = loopRange.loopEndSamples > playbackRange.endSamples;\n const TAIL_FADE_SAMPLES = 64;\n if (this.playbackPosition === 0) {\n this.playbackPosition = this.reversePlayback ? playbackRange.endSamples - 1 : playbackRange.startSamples;\n this.#resetDurationPreservation(this.playbackPosition);\n }\n for (let sample = 0; sample < outputChannels[0].length; sample++) {\n const envelopeGain = this.#getSafeParam(parameters.envGain, sample, isConstant.envGain);\n const baseRate = this.#getSafeParam(parameters.playbackRate, sample, isConstant.playbackRate);\n const playbackStep = this.reversePlayback ? -Math.abs(baseRate) : Math.abs(baseRate);\n const canWrapLoop = this.loopEnabled && this.loopCount < parameters.maxLoopCount[0];\n if (canWrapLoop) {\n if (!this.reversePlayback && this.playbackPosition >= loopRange.loopEndSamples) {\n this.#smoothLoopWrap(silencePadTail ? 0 : this.buffer[0][Math.floor(this.playbackPosition - 1)] || 0, this.buffer[0][Math.floor(loopRange.loopStartSamples)] || 0);\n this.playbackPosition = loopRange.loopStartSamples;\n this.loopCount++;\n this.nextDriftGenerated = false;\n } else if (this.reversePlayback && this.playbackPosition <= loopRange.loopStartSamples) {\n this.#smoothLoopWrap(this.buffer[0][Math.floor(loopRange.loopStartSamples)] || 0, silencePadTail ? 0 : this.buffer[0][Math.floor(loopRange.loopEndSamples) - 1] || 0);\n this.playbackPosition = loopRange.loopEndSamples;\n this.loopCount++;\n this.nextDriftGenerated = false;\n }\n }\n const durationResetTarget = this.#prepareDurationPreservingSample(playbackStep, loopRange);\n const shouldStopForward = !this.reversePlayback && (this.#isDurationPreservationActive(loopRange) ? this.durationPreservation.timelinePosition : this.playbackPosition) >= playbackRange.endSamples;\n const shouldStopReverse = this.reversePlayback && (this.#isDurationPreservationActive(loopRange) ? this.durationPreservation.timelinePosition : this.playbackPosition) <= playbackRange.startSamples;\n const isWithinLoop = this.loopEnabled && this.playbackPosition >= loopRange.loopStartSamples && this.playbackPosition <= loopRange.loopEndSamples;\n if ((shouldStopForward || shouldStopReverse) && !(this.loopEnabled && isWithinLoop)) {\n this.#stop();\n return true;\n }\n let tailGain = 1;\n if (silencePadTail) {\n const distToEnd = playbackRange.endSamples - this.playbackPosition;\n if (distToEnd < TAIL_FADE_SAMPLES) tailGain = Math.max(0, distToEnd / TAIL_FADE_SAMPLES);\n }\n const currentPosition = Math.floor(this.playbackPosition);\n const positionOffset = this.playbackPosition - currentPosition;\n let nextPosition, interpWeight;\n if (this.reversePlayback) {\n nextPosition = Math.max(currentPosition - 1, playbackRange.startSamples);\n interpWeight = 1 - positionOffset;\n } else {\n nextPosition = Math.min(currentPosition + 1, playbackRange.endSamples - 1);\n interpWeight = positionOffset;\n }\n for (let channel = 0; channel < numChannels; channel++) {\n if (!outputChannels[channel]) {\n console.warn(`Output channel ${channel} does not exist. Available channels:`, outputChannels.length);\n continue;\n }\n let interpolatedSample = 0;\n for (let l = 0; l < this.layers.length; l++) {\n const layer = this.layers[l];\n const layerChannel = layer[Math.min(channel, layer.length - 1)];\n const currentSample = layerChannel[currentPosition] || 0;\n const nextSample = layerChannel[nextPosition] || 0;\n interpolatedSample += (currentSample + interpWeight * (nextSample - currentSample)) * this.layerGain;\n }\n if (this.applyClickCompensation) {\n interpolatedSample += this.loopClickCompensation;\n if (this.compensationDecay) {\n this.loopClickCompensation *= this.compensationDecay;\n if (Math.abs(this.loopClickCompensation) < .001) this.applyClickCompensation = false;\n } else this.applyClickCompensation = false;\n }\n const finalSample = interpolatedSample * velocityGain * envelopeGain * masterGain * amplitudeGain * tailGain;\n let panAdjustedSample = finalSample;\n if (outputChannels.length === 2) {\n if (channel === 0) panAdjustedSample = finalSample * (1 - Math.max(0, effectivePan));\n else if (channel === 1) panAdjustedSample = finalSample * (1 - Math.max(0, -effectivePan));\n }\n outputChannels[channel][sample] = Math.max(-1, Math.min(1, isFinite(panAdjustedSample) ? panAdjustedSample : 0));\n }\n this.#advanceDurationPreservingPlayback(playbackStep, durationResetTarget, loopRange, canWrapLoop);\n }\n if (this.usePlaybackPosition) {\n const normalizedPosition = this.#samplesToNormalized(this.playbackPosition);\n this.port.postMessage({\n type: \"voice:position\",\n position: normalizedPosition\n });\n }\n return true;\n }\n};\nregisterProcessor(\"sample-player-processor\", SamplePlayerProcessor);\n//#endregion\n//#region src/worklets/processors/noise/random-noise-processor.js\nvar RandomNoiseProcessor = class extends AudioWorkletProcessor {\n constructor() {\n super();\n this.previousNoise = 0;\n this.previousFiltered = 0;\n this.hpfHz = 150;\n this.alpha = this.hpfHz / (this.hpfHz + sampleRate / (2 * Math.PI));\n this.port.onmessage = (event) => {\n if (event.data.type === \"setHpfHz\") {\n this.hpfHz = event.data.value;\n this.alpha = this.calculateAlpha(this.hpfHz);\n }\n };\n this.port.postMessage({ type: \"initialized\" });\n }\n calculateAlpha(frequency) {\n return frequency / (frequency + sampleRate / (2 * Math.PI));\n }\n process(inputs, outputs, parameters) {\n outputs[0].forEach((channel) => {\n for (let i = 0; i < channel.length; i++) {\n const noise = Math.random() * 2 - 1;\n const filtered = this.alpha * (noise - this.previousNoise) + this.previousFiltered;\n this.previousNoise = noise;\n this.previousFiltered = filtered;\n channel[i] = filtered;\n }\n });\n return true;\n }\n};\nregisterProcessor(\"random-noise-processor\", RandomNoiseProcessor);\n//#endregion\n//#region src/worklets/shared/utils/compress-utils.ts\nvar cheapSoftClipSingleSample = (sample, max = .9) => {\n const a = Math.abs(sample);\n if (a <= max) return sample;\n const x = a / max;\n const compressed = x / (1 + x);\n return Math.sign(sample) * max * compressed;\n};\n/**\n* Basic attenuation compressor for single sample\n* Note: No validation since optimized for real time use\n*/\nvar compressSingleSample = (input, threshold = .75, ratio = 4, limiter = {\n enabled: true,\n type: \"soft\",\n outputRange: {\n min: -1,\n max: 1\n }\n}) => {\n const { min, max } = limiter.outputRange;\n let x = input;\n if (Math.abs(x) > threshold) x = Math.sign(x) * (threshold + (Math.abs(x) - threshold) / ratio);\n if (limiter.enabled) {\n if (limiter.type === \"soft\") x = cheapSoftClipSingleSample(x, Math.abs(max));\n else if (limiter.type === \"hard\") x = Math.max(min, Math.min(max, x));\n }\n return x;\n};\n//#endregion\n//#region src/worklets/processors/delay/DelayBuffer.js\nvar DelayBuffer = class {\n constructor(maxDelaySamples) {\n this.buffer = new Float32Array(maxDelaySamples);\n this.writePtr = 0;\n this.readPtr = 0;\n }\n write(sample) {\n this.buffer[this.writePtr] = sample;\n }\n read() {\n return this.buffer[this.readPtr];\n }\n updatePointers(delaySamples) {\n this.writePtr = (this.writePtr + 1) % this.buffer.length;\n this.readPtr = (this.writePtr - delaySamples + this.buffer.length) % this.buffer.length;\n }\n};\n//#endregion\n//#region src/worklets/processors/delay/FeedbackDelay.js\nvar AUTO_GAIN_THRESHOLD = .8;\nvar SAFETY_GAIN_COMPENSATION = .2;\nvar FeedbackDelay = class {\n constructor(sampleRate) {\n this.sampleRate = sampleRate;\n this.buffers = [];\n this.initialized = false;\n this.autoGainEnabled = false;\n this.gainCompensation = SAFETY_GAIN_COMPENSATION;\n this.lowpassStates = [];\n this.highpassStates = [];\n this.highpassInputStates = [];\n }\n initializeBuffers(channelCount) {\n this.buffers = [];\n this.lowpassStates = [];\n this.highpassStates = [];\n this.highpassInputStates = [];\n const maxSamples = Math.floor(this.sampleRate * 2);\n for (let c = 0; c < channelCount; c++) {\n this.buffers[c] = new DelayBuffer(maxSamples);\n this.lowpassStates[c] = 0;\n this.highpassStates[c] = 0;\n this.highpassInputStates[c] = 0;\n }\n this.initialized = true;\n }\n /** Simple one-pole lowpass filter */\n lowpass(input, cutoffFreq, channelIndex) {\n if (cutoffFreq >= this.sampleRate * .4) return input;\n const omega = 2 * Math.PI * cutoffFreq / this.sampleRate;\n const alpha = Math.max(0, Math.min(.99, Math.sin(omega) / (Math.sin(omega) + Math.cos(omega))));\n this.lowpassStates[channelIndex] = alpha * input + (1 - alpha) * this.lowpassStates[channelIndex];\n return this.lowpassStates[channelIndex];\n }\n /** Simple one-pole highpass filter */\n highpass(input, cutoffFreq, channelIndex) {\n if (cutoffFreq < 5) return input;\n const omega = 2 * Math.PI * cutoffFreq / this.sampleRate;\n const alpha = Math.max(0, Math.min(.99, Math.sin(omega) / (Math.sin(omega) + Math.cos(omega))));\n const lowpassOutput = alpha * input + (1 - alpha) * this.highpassStates[channelIndex];\n const highpassOutput = input - lowpassOutput;\n this.highpassStates[channelIndex] = lowpassOutput;\n return highpassOutput;\n }\n process(inputSample, channelIndex, feedbackAmount, delayTime, lowpassFreq = 1e4, highpassFreq = 100) {\n if (!this.initialized) return inputSample;\n const buffer = this.buffers[channelIndex] || this.buffers[0];\n const delaySamples = Math.floor(this.sampleRate * delayTime);\n const delayedSample = buffer.read();\n let filteredDelay = this.highpass(delayedSample, highpassFreq, channelIndex);\n filteredDelay = this.lowpass(filteredDelay, lowpassFreq, channelIndex);\n const feedbackSample = feedbackAmount * filteredDelay + inputSample;\n let outputSample = feedbackSample;\n const compressedFeedback = compressSingleSample(feedbackSample, .5, 4, {\n enabled: true,\n outputRange: {\n min: -.99,\n max: .99\n },\n type: \"soft\"\n });\n if (this.autoGainEnabled && feedbackAmount > AUTO_GAIN_THRESHOLD) outputSample = compressedFeedback * (1 - (feedbackAmount - AUTO_GAIN_THRESHOLD) * this.gainCompensation);\n return {\n outputSample,\n feedbackSample: compressedFeedback,\n delaySamples\n };\n }\n updateBuffer(channelIndex, sample, delaySamples) {\n const buffer = this.buffers[channelIndex] || this.buffers[0];\n buffer.write(sample);\n buffer.updatePointers(delaySamples);\n }\n setAutoGain(enabled, compensation = SAFETY_GAIN_COMPENSATION) {\n this.autoGainEnabled = enabled;\n this.gainCompensation = compensation;\n }\n};\n//#endregion\n//#region src/worklets/processors/delay/feedback-delay-processor.js\nregisterProcessor(\"feedback-delay-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"feedbackAmount\",\n defaultValue: .5,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n {\n name: \"delayTime\",\n defaultValue: .5,\n minValue: .00012656238799684143,\n maxValue: 2,\n automationRate: \"k-rate\"\n },\n {\n name: \"decay\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n {\n name: \"lowpass\",\n defaultValue: 1e4,\n minValue: 100,\n maxValue: 16e3,\n automationRate: \"k-rate\"\n }\n ];\n }\n constructor() {\n super();\n this.feedbackDelay = new FeedbackDelay(sampleRate);\n this.decayStartTime = null;\n this.decayActive = false;\n this.baseFeedbackAmount = .5;\n this.setupMessageHandling();\n this.port.postMessage({ type: \"initialized\" });\n }\n setupMessageHandling() {\n this.port.onmessage = (event) => {\n switch (event.data.type) {\n case \"setAutoGain\":\n this.feedbackDelay.setAutoGain(event.data.enabled, event.data.amount);\n break;\n case \"triggerDecay\":\n this.decayStartTime = currentTime;\n this.decayActive = true;\n this.baseFeedbackAmount = event.data.baseFeedbackAmount || .5;\n break;\n case \"stopDecay\":\n this.decayActive = false;\n this.decayStartTime = null;\n }\n };\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n if (!this.feedbackDelay.initialized || this.feedbackDelay.buffers.length !== input.length) this.feedbackDelay.initializeBuffers(input.length);\n const baseFeedbackAmount = parameters.feedbackAmount[0];\n const delayTime = parameters.delayTime[0];\n const decay = parameters.decay[0];\n const lowpassFreq = parameters.lowpass[0];\n const channelCount = Math.min(input.length, output.length);\n const frameCount = output[0].length;\n for (let i = 0; i < frameCount; ++i) {\n let effectiveFeedbackAmount = baseFeedbackAmount;\n if (this.decayActive && this.decayStartTime !== null) {\n const elapsedTime = currentTime - this.decayStartTime + i / sampleRate;\n const delayCompensation = Math.min(100, .5 / delayTime);\n const timeConstant = Math.pow(decay, 5) * 1e3 * delayCompensation + .5;\n effectiveFeedbackAmount = baseFeedbackAmount * Math.exp(-elapsedTime / timeConstant);\n if (effectiveFeedbackAmount < .01) {\n this.decayActive = false;\n effectiveFeedbackAmount = 0;\n }\n }\n for (let c = 0; c < channelCount; c++) {\n const processed = this.feedbackDelay.process(input[c][i], c, effectiveFeedbackAmount, delayTime, lowpassFreq);\n output[c][i] = processed.outputSample;\n this.feedbackDelay.updateBuffer(c, processed.feedbackSample, processed.delaySamples);\n }\n }\n return true;\n }\n});\n//#endregion\n//#region src/worklets/processors/delay/delay-processor.js\nvar DEFAULT_DELAY_CONFIG = {\n CHARACTER: [\"filtered\"],\n SMOOTHING_FACTOR: {\n slowest: 1e-4,\n slow: 25e-5,\n medium: 35e-5,\n fast: 5e-4,\n veryFast: .001,\n superFast: .1,\n none: 1\n }\n};\nvar DEFAULT_CHARACTER_CONFIG = {\n bitCrushed: {\n bits: 11,\n downsample: 3\n },\n filtered: {\n freq: 900,\n Q: .15\n }\n};\nregisterProcessor(\"delay-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [{\n name: \"delayTime\",\n defaultValue: .5,\n minValue: .001,\n maxValue: 2,\n automationRate: \"k-rate\"\n }, {\n name: \"feedbackAmount\",\n defaultValue: 0,\n minValue: 0,\n maxValue: .99,\n automationRate: \"k-rate\"\n }];\n }\n constructor() {\n super();\n this.buffers = [];\n this.smoothedDelaySamples = [];\n this.smoothingFactor = DEFAULT_DELAY_CONFIG.SMOOTHING_FACTOR.slowest;\n this.characterModes = [...DEFAULT_DELAY_CONFIG.CHARACTER];\n this._bpState = [];\n this._bpFreq = DEFAULT_CHARACTER_CONFIG.filtered.freq;\n this._bpQ = DEFAULT_CHARACTER_CONFIG.filtered.Q;\n this._bpCoeffs = null;\n this._lastBpFreq = -1;\n this._lastBpQ = -1;\n this.lofiBits = DEFAULT_CHARACTER_CONFIG[\"bitCrushed\"].bits;\n this.lofiDownsample = DEFAULT_CHARACTER_CONFIG[\"bitCrushed\"].downsample;\n this._lofiSampleHold = [];\n this._lofiSampleCount = [];\n this.initialized = false;\n this.port.onmessage = (event) => {\n if (event.data && event.data.type === \"setCharacter\" && Array.isArray(event.data.modes)) this.characterModes = [...event.data.modes];\n if (event.data && event.data.type === \"setBandpassFreq\" && typeof event.data.hz === \"number\") this.setBandpassFreq(event.data.hz);\n if (event.data && event.data.type === \"trigger\") {}\n };\n this.port.postMessage({ type: \"initialized\" });\n }\n setBandpassFreq(hz) {\n this._bpFreq = hz;\n this._lastBpFreq = -1;\n }\n _updateBandpassCoeffs() {\n if (this._lastBpFreq === this._bpFreq && this._lastBpQ === this._bpQ) return;\n const bpFreq = this._bpFreq;\n const bpQ = this._bpQ;\n const omega = 2 * Math.PI * bpFreq / sampleRate;\n const alpha = Math.sin(omega) / (2 * bpQ);\n const cosw = Math.cos(omega);\n const b0 = alpha;\n const b1 = 0;\n const b2 = -alpha;\n const a0 = 1 + alpha;\n const a1 = -2 * cosw;\n const a2 = 1 - alpha;\n this._bpCoeffs = {\n b0: b0 / a0,\n b1: b1 / a0,\n b2: b2 / a0,\n a1: a1 / a0,\n a2: a2 / a0\n };\n this._lastBpFreq = bpFreq;\n this._lastBpQ = bpQ;\n }\n initializeBuffers(channelCount) {\n const maxSamples = Math.floor(sampleRate * 2);\n this.buffers = [];\n this.smoothedDelaySamples = [];\n this._lofiSampleHold = [];\n this._lofiSampleCount = [];\n for (let c = 0; c < channelCount; c++) {\n this.buffers[c] = new DelayBuffer(maxSamples);\n this.smoothedDelaySamples[c] = Math.floor(sampleRate * .5);\n this._lofiSampleHold[c] = 0;\n this._lofiSampleCount[c] = 0;\n }\n this.initialized = true;\n }\n _processLoFi(delayed, c) {\n if (this._lofiSampleCount[c] % this.lofiDownsample === 0) {\n const levels = Math.pow(2, this.lofiBits);\n delayed = Math.round(delayed * levels) / levels;\n this._lofiSampleHold[c] = delayed;\n } else delayed = this._lofiSampleHold[c];\n this._lofiSampleCount[c]++;\n return delayed;\n }\n _processBandpass(delayed, c) {\n if (!this._bpState) this._bpState = [];\n if (!this._bpState[c]) this._bpState[c] = {\n x1: 0,\n x2: 0,\n y1: 0,\n y2: 0\n };\n this._updateBandpassCoeffs();\n if (!this._bpCoeffs) return delayed;\n const { b0, b1, b2, a1, a2 } = this._bpCoeffs;\n const s = this._bpState[c];\n const y = b0 * delayed + b1 * s.x1 + b2 * s.x2 - a1 * s.y1 - a2 * s.y2;\n s.x2 = s.x1;\n s.x1 = delayed;\n s.y2 = s.y1;\n s.y1 = y;\n return y;\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output || input.length === 0 || output.length === 0) return true;\n if (!input[0] || !output[0] || input[0].length === 0 || output[0].length === 0) return true;\n if (!this.initialized || this.buffers.length !== input.length) this.initializeBuffers(input.length);\n const delayTime = parameters.delayTime[0];\n const feedbackAmount = parameters.feedbackAmount[0];\n const targetDelaySamples = sampleRate * delayTime;\n const channelCount = Math.min(input.length, output.length);\n const frameCount = output[0].length;\n const smoothing = this.smoothingFactor;\n for (let i = 0; i < frameCount; ++i) for (let c = 0; c < channelCount; c++) {\n const buf = this.buffers[c];\n if (!buf) continue;\n this.smoothedDelaySamples[c] += (targetDelaySamples - this.smoothedDelaySamples[c]) * smoothing;\n const smoothedDelay = this.smoothedDelaySamples[c];\n const intDelay = Math.floor(smoothedDelay);\n const frac = smoothedDelay - intDelay;\n const readPtrA = (buf.writePtr - intDelay + buf.buffer.length) % buf.buffer.length;\n const readPtrB = (readPtrA - 1 + buf.buffer.length) % buf.buffer.length;\n const sampleA = buf.buffer[readPtrA];\n const sampleB = buf.buffer[readPtrB];\n let delayed = sampleA * (1 - frac) + sampleB * frac;\n for (const mode of this.characterModes) if (mode === \"bitCrushed\") delayed = this._processLoFi(delayed, c);\n else if (mode === \"filtered\") delayed = this._processBandpass(delayed, c);\n output[c][i] = compressSingleSample(delayed, .75, 4, {\n enabled: true,\n type: \"soft\",\n outputRange: {\n min: -.9,\n max: .9\n }\n });\n const inputSample = input[c] && input[c][i] !== void 0 ? input[c][i] : 0;\n buf.write(inputSample + delayed * feedbackAmount);\n buf.updatePointers(intDelay);\n }\n return true;\n }\n});\n//#endregion\n//#region src/worklets/processors/reverb/dattorro-reverb-processor.js\nvar DattorroReverb = class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n [\n \"preDelay\",\n 0,\n 0,\n sampleRate - 1,\n \"k-rate\"\n ],\n [\n \"bandwidth\",\n .9999,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"inputDiffusion1\",\n .75,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"inputDiffusion2\",\n .625,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"decay\",\n .5,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"decayDiffusion1\",\n .7,\n 0,\n .999999,\n \"k-rate\"\n ],\n [\n \"decayDiffusion2\",\n .5,\n 0,\n .999999,\n \"k-rate\"\n ],\n [\n \"damping\",\n .005,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"excursionRate\",\n .5,\n 0,\n 2,\n \"k-rate\"\n ],\n [\n \"excursionDepth\",\n .7,\n 0,\n 2,\n \"k-rate\"\n ],\n [\n \"wet\",\n .3,\n 0,\n 1,\n \"k-rate\"\n ],\n [\n \"dry\",\n .6,\n 0,\n 1,\n \"k-rate\"\n ]\n ].map((x) => /* @__PURE__ */ new Object({\n name: x[0],\n defaultValue: x[1],\n minValue: x[2],\n maxValue: x[3],\n automationRate: x[4]\n }));\n }\n constructor(options) {\n super(options);\n this._Delays = [];\n this._pDLength = sampleRate + (128 - sampleRate % 128);\n this._preDelay = new Float32Array(this._pDLength);\n this._pDWrite = 0;\n this._lp1 = 0;\n this._lp2 = 0;\n this._lp3 = 0;\n this._excPhase = 0;\n const SHORT_DELAY_SCALE = .5;\n [\n .004771345,\n .003595309,\n .012734787,\n .009307483,\n .022579886,\n .149625349,\n .060481839,\n .1249958,\n .030509727,\n .141695508,\n .089244313,\n .106280031\n ].map((x) => x * SHORT_DELAY_SCALE).forEach((x) => this.makeDelay(x));\n this._taps = Int16Array.from([\n .008937872,\n .099929438,\n .064278754,\n .067067639,\n .066866033,\n .006283391,\n .035818689,\n .011861161,\n .121870905,\n .041262054,\n .08981553,\n .070931756,\n .011256342,\n .004065724\n ], (x) => Math.round(x * sampleRate));\n this.port.postMessage({ type: \"initialized\" });\n }\n makeDelay(length) {\n let len = Math.round(length * sampleRate);\n let nextPow2 = 2 ** Math.ceil(Math.log2(len));\n this._Delays.push([\n new Float32Array(nextPow2),\n len - 1,\n 0,\n nextPow2 - 1\n ]);\n }\n writeDelay(index, data) {\n return this._Delays[index][0][this._Delays[index][1]] = data;\n }\n readDelay(index) {\n return this._Delays[index][0][this._Delays[index][2]];\n }\n readDelayAt(index, i) {\n let d = this._Delays[index];\n return d[0][d[2] + i & d[3]];\n }\n readDelayCAt(index, i) {\n let d = this._Delays[index], frac = i - ~~i, int = ~~i + d[2] - 1, mask = d[3];\n let x0 = d[0][int++ & mask], x1 = d[0][int++ & mask], x2 = d[0][int++ & mask], x3 = d[0][int & mask];\n let a = (3 * (x1 - x2) - x0 + x3) / 2, b = 2 * x2 + x0 - (5 * x1 + x3) / 2, c = (x2 - x0) / 2;\n return ((a * frac + b) * frac + c) * frac + x1;\n }\n process(inputs, outputs, parameters) {\n const TWO_PI = 6.283185307179586;\n const TWO_PI_DETUNE = 6.284702653297906;\n const pd = ~~parameters.preDelay[0], bw = parameters.bandwidth[0], fi = parameters.inputDiffusion1[0], si = parameters.inputDiffusion2[0], dc = parameters.decay[0], ft = parameters.decayDiffusion1[0], st = parameters.decayDiffusion2[0], dp = 1 - parameters.damping[0], ex = parameters.excursionRate[0] / sampleRate, ed = parameters.excursionDepth[0] * sampleRate / 1e3, we = parameters.wet[0] * .6, dr = parameters.dry[0];\n if (inputs[0].length == 2) for (let i = 127; i >= 0; i--) {\n this._preDelay[this._pDWrite + i] = (inputs[0][0][i] + inputs[0][1][i]) * .5;\n outputs[0][0][i] = inputs[0][0][i] * dr;\n outputs[0][1][i] = inputs[0][1][i] * dr;\n }\n else if (inputs[0].length > 0) {\n this._preDelay.set(inputs[0][0], this._pDWrite);\n for (let i = 127; i >= 0; i--) outputs[0][0][i] = outputs[0][1][i] = inputs[0][0][i] * dr;\n } else this._preDelay.set(/* @__PURE__ */ new Float32Array(128), this._pDWrite);\n let i = 0;\n while (i < 128) {\n let lo = 0, ro = 0;\n this._lp1 += bw * (this._preDelay[(this._pDLength + this._pDWrite - pd + i) % this._pDLength] - this._lp1);\n let pre = this.writeDelay(0, this._lp1 - fi * this.readDelay(0));\n pre = this.writeDelay(1, fi * (pre - this.readDelay(1)) + this.readDelay(0));\n pre = this.writeDelay(2, fi * pre + this.readDelay(1) - si * this.readDelay(2));\n pre = this.writeDelay(3, si * (pre - this.readDelay(3)) + this.readDelay(2));\n let split = si * pre + this.readDelay(3);\n let exc = ed * (1 + Math.cos(this._excPhase * TWO_PI));\n let exc2 = ed * (1 + Math.sin(this._excPhase * TWO_PI_DETUNE));\n let temp = this.writeDelay(4, split + dc * this.readDelay(11) + ft * this.readDelayCAt(4, exc));\n this.writeDelay(5, this.readDelayCAt(4, exc) - ft * temp);\n this._lp2 += dp * (this.readDelay(5) - this._lp2);\n temp = this.writeDelay(6, dc * this._lp2 - st * this.readDelay(6));\n this.writeDelay(7, this.readDelay(6) + st * temp);\n temp = this.writeDelay(8, split + dc * this.readDelay(7) + ft * this.readDelayCAt(8, exc2));\n this.writeDelay(9, this.readDelayCAt(8, exc2) - ft * temp);\n this._lp3 += dp * (this.readDelay(9) - this._lp3);\n temp = this.writeDelay(10, dc * this._lp3 - st * this.readDelay(10));\n this.writeDelay(11, this.readDelay(10) + st * temp);\n lo = this.readDelayAt(9, this._taps[0]) + this.readDelayAt(9, this._taps[1]) - this.readDelayAt(10, this._taps[2]) + this.readDelayAt(11, this._taps[3]) - this.readDelayAt(5, this._taps[4]) - this.readDelayAt(6, this._taps[5]) - this.readDelayAt(7, this._taps[6]);\n ro = this.readDelayAt(5, this._taps[7]) + this.readDelayAt(5, this._taps[8]) - this.readDelayAt(6, this._taps[9]) + this.readDelayAt(7, this._taps[10]) - this.readDelayAt(9, this._taps[11]) - this.readDelayAt(10, this._taps[12]) - this.readDelayAt(11, this._taps[13]);\n outputs[0][0][i] += lo * we;\n outputs[0][1][i] += ro * we;\n this._excPhase += ex;\n if (this._excPhase >= 1) this._excPhase -= 1;\n i++;\n const delays = this._Delays;\n for (let j = 0; j < delays.length; j++) {\n const d = delays[j];\n d[1] = d[1] + 1 & d[3];\n d[2] = d[2] + 1 & d[3];\n }\n }\n this._pDWrite = (this._pDWrite + 128) % this._pDLength;\n return true;\n }\n};\nregisterProcessor(\"dattorro-reverb-processor\", DattorroReverb);\n//#endregion\n//#region src/worklets/processors/distortion/distortion-processor.js\nvar Distortion = class {\n constructor() {\n this.limitingMode = \"hard-clipping\";\n }\n applyDrive(sample, driveAmount) {\n if (driveAmount <= 0) return sample;\n return sample * (1 + driveAmount * 3);\n }\n applyClipping(sample, clippingAmount, clipThreshold) {\n if (clippingAmount <= 0) return sample;\n let clippedSample;\n switch (this.limitingMode) {\n case \"soft-clipping\":\n clippedSample = clipThreshold * Math.tanh(sample / clipThreshold);\n break;\n case \"hard-clipping\":\n clippedSample = Math.max(-clipThreshold, Math.min(clipThreshold, sample));\n break;\n default: clippedSample = sample;\n }\n if (clipThreshold < .08) {\n const makeupGain = Math.min(2, Math.pow(.1 / clipThreshold, .5));\n clippedSample *= makeupGain;\n }\n return sample * (1 - clippingAmount) + clippedSample * clippingAmount;\n }\n setLimitingMode(mode) {\n this.limitingMode = mode;\n }\n};\nregisterProcessor(\"distortion-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"distortionDrive\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\"\n },\n {\n name: \"clippingAmount\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\"\n },\n {\n name: \"clippingThreshold\",\n defaultValue: .5,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\"\n }\n ];\n }\n constructor() {\n super();\n this.distortion = new Distortion();\n this.setupMessageHandling();\n this.port.postMessage({ type: \"initialized\" });\n }\n setupMessageHandling() {\n this.port.onmessage = (event) => {\n switch (event.data.type) {\n case \"setLimitingMode\":\n this.distortion.setLimitingMode(event.data.mode);\n break;\n default: console.warn(\"distortion-processor: Unsupported message\");\n }\n };\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n const clipThreshold = parameters.clippingThreshold[0];\n for (let i = 0; i < output[0].length; ++i) {\n const distortionDrive = parameters.distortionDrive[Math.min(i, parameters.distortionDrive.length - 1)];\n const clippingAmount = parameters.clippingAmount[Math.min(i, parameters.clippingAmount.length - 1)];\n for (let c = 0; c < Math.min(input.length, output.length); c++) {\n let sample = input[c][i];\n sample = this.distortion.applyDrive(sample, distortionDrive);\n sample = this.distortion.applyClipping(sample, clippingAmount, clipThreshold);\n output[c][i] = Math.max(-.999, Math.min(.999, sample));\n }\n }\n return true;\n }\n});\n//#endregion\n//#region src/worklets/processors/follower/envelope-follower-processor.js\nregisterProcessor(\"envelope-follower-processor\", class extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"inputGain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 10,\n automationRate: \"k-rate\"\n },\n {\n name: \"outputGain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 10,\n automationRate: \"k-rate\"\n },\n {\n name: \"attack\",\n defaultValue: .003,\n minValue: .001,\n maxValue: 1,\n automationRate: \"k-rate\"\n },\n {\n name: \"release\",\n defaultValue: .05,\n minValue: .001,\n maxValue: 5,\n automationRate: \"k-rate\"\n }\n ];\n }\n constructor() {\n super();\n this.envelope = 0;\n this.gateThreshold = .005;\n this.debugCounter = 0;\n this.port.postMessage({ type: \"initialized\" });\n }\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n const output = outputs[0];\n const channel = inputs[0][0];\n if (!input || !output || !channel || input.length === 0 || output.length === 0 || channel.length === 0) return true;\n const inChannel = input[0];\n if (!inChannel || inChannel.length === 0) return true;\n const attack = parameters.attack[0];\n const release = parameters.release[0];\n const inputGain = parameters.inputGain[0];\n const outputGain = parameters.outputGain[0];\n const attackCoeff = Math.exp(-1 / (attack * sampleRate));\n const releaseCoeff = Math.exp(-1 / (release * sampleRate));\n for (let sample = 0; sample < output[0].length; sample++) {\n const inputLevel = Math.abs((input[0][sample] || 0) * inputGain);\n if (inputLevel > 1e-6) {\n if (inputLevel > this.envelope) this.envelope = inputLevel + (this.envelope - inputLevel) * attackCoeff;\n else this.envelope = inputLevel + (this.envelope - inputLevel) * releaseCoeff;\n } else this.envelope *= releaseCoeff;\n if (this.envelope < this.gateThreshold) this.envelope = 0;\n const finalOutput = this.envelope * outputGain;\n for (let channel = 0; channel < output.length; channel++) output[channel][sample] = finalOutput;\n }\n return true;\n }\n});\n//#endregion\n", Tt = !1;
|
|
4650
4684
|
async function Et(e) {
|
|
4651
4685
|
if (Tt) return console.info("AudioWorklet processors already initialized, skipping"), {
|
|
4652
4686
|
success: !0,
|
|
@@ -4799,9 +4833,7 @@ var Q = {
|
|
|
4799
4833
|
startThreshold: -30,
|
|
4800
4834
|
autoStop: !1,
|
|
4801
4835
|
stopThreshold: -40,
|
|
4802
|
-
silenceTimeoutMs: 1e3
|
|
4803
|
-
preprocess: !1,
|
|
4804
|
-
preprocessOptions: {}
|
|
4836
|
+
silenceTimeoutMs: 1e3
|
|
4805
4837
|
}, At = class {
|
|
4806
4838
|
nodeId;
|
|
4807
4839
|
nodeType = "recorder";
|
|
@@ -4899,8 +4931,8 @@ var Q = {
|
|
|
4899
4931
|
if (this.#a === Q.ARMED) throw this.cancel(), Error("Recording was armed but never triggered");
|
|
4900
4932
|
if (this.#a !== Q.RECORDING) throw Error("Not recording");
|
|
4901
4933
|
this.#y();
|
|
4902
|
-
let e = await this.#x(), t = await this.#S(e)
|
|
4903
|
-
return this.#
|
|
4934
|
+
let e = await this.#x(), t = await this.#S(e);
|
|
4935
|
+
return this.#i && await this.#i.loadSample(t), this.#a = Q.STOPPED, console.info(`Recorder state: ${this.#a}`), this.sendMessage("record:stop", { duration: t.duration }), this.#b(), t;
|
|
4904
4936
|
}
|
|
4905
4937
|
#x() {
|
|
4906
4938
|
return new Promise((e) => {
|