smplr 0.25.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -67
- package/dist/index.d.mts +79 -50
- package/dist/index.d.ts +79 -50
- package/dist/index.js +72 -70
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +72 -70
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
package/dist/index.mjs
CHANGED
|
@@ -68,11 +68,12 @@ var HttpStorage = {
|
|
|
68
68
|
return fetch(url);
|
|
69
69
|
}
|
|
70
70
|
};
|
|
71
|
-
var _cache, _CacheStorageImpl_instances, tryFromCache_fn, saveResponse_fn;
|
|
71
|
+
var _cache, _warned, _CacheStorageImpl_instances, tryFromCache_fn, saveResponse_fn;
|
|
72
72
|
var CacheStorageImpl = class {
|
|
73
73
|
constructor(name = "smplr") {
|
|
74
74
|
__privateAdd(this, _CacheStorageImpl_instances);
|
|
75
75
|
__privateAdd(this, _cache);
|
|
76
|
+
__privateAdd(this, _warned, false);
|
|
76
77
|
if (typeof window === "undefined" || !("caches" in window)) {
|
|
77
78
|
__privateSet(this, _cache, Promise.reject("CacheStorage not supported"));
|
|
78
79
|
__privateGet(this, _cache).catch(() => {
|
|
@@ -95,6 +96,7 @@ var CacheStorageImpl = class {
|
|
|
95
96
|
}
|
|
96
97
|
};
|
|
97
98
|
_cache = new WeakMap();
|
|
99
|
+
_warned = new WeakMap();
|
|
98
100
|
_CacheStorageImpl_instances = new WeakSet();
|
|
99
101
|
tryFromCache_fn = function(request) {
|
|
100
102
|
return __async(this, null, function* () {
|
|
@@ -110,6 +112,10 @@ saveResponse_fn = function(request, response) {
|
|
|
110
112
|
const cache = yield __privateGet(this, _cache);
|
|
111
113
|
yield cache.put(request, response.clone());
|
|
112
114
|
} catch (err) {
|
|
115
|
+
if (!__privateGet(this, _warned)) {
|
|
116
|
+
__privateSet(this, _warned, true);
|
|
117
|
+
console.warn("smplr: failed to cache response", err);
|
|
118
|
+
}
|
|
113
119
|
}
|
|
114
120
|
});
|
|
115
121
|
};
|
|
@@ -229,6 +235,15 @@ var Channel = class {
|
|
|
229
235
|
__privateGet(this, _config).destination
|
|
230
236
|
]));
|
|
231
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Add a send effect on a parallel bus.
|
|
240
|
+
*
|
|
241
|
+
* The send is **post-fader**: it taps the channel signal after the volume
|
|
242
|
+
* gain (and after any inserts added via {@link addInsert}), before the
|
|
243
|
+
* panner. Lowering `volume` proportionally lowers the send level; `volume = 0`
|
|
244
|
+
* silences the send too. Inserts are upstream of the tap, so they are heard
|
|
245
|
+
* on the send.
|
|
246
|
+
*/
|
|
232
247
|
addEffect(name, effect, mixValue) {
|
|
233
248
|
var _a;
|
|
234
249
|
if (__privateGet(this, _disconnected)) {
|
|
@@ -318,7 +333,7 @@ function pickPlaybackParams(obj) {
|
|
|
318
333
|
return result;
|
|
319
334
|
}
|
|
320
335
|
function resolveParams(defaults, group, region, midi, velocity, overrides) {
|
|
321
|
-
var _a, _b, _c, _d, _e;
|
|
336
|
+
var _a, _b, _c, _d, _e, _f;
|
|
322
337
|
const merged = __spreadValues(__spreadValues(__spreadValues(__spreadValues({}, PARAM_DEFAULTS), defaults), pickPlaybackParams(group)), pickPlaybackParams(region));
|
|
323
338
|
const pitch = (_b = (_a = region.pitch) != null ? _a : region.key) != null ? _b : midi;
|
|
324
339
|
const semitones = midi - pitch;
|
|
@@ -335,9 +350,8 @@ function resolveParams(defaults, group, region, midi, velocity, overrides) {
|
|
|
335
350
|
loop: (_e = overrides == null ? void 0 : overrides.loop) != null ? _e : merged.loop,
|
|
336
351
|
loopStart: merged.loopStart,
|
|
337
352
|
loopEnd: merged.loopEnd,
|
|
338
|
-
ampVelCurve: region.ampVelCurve,
|
|
339
353
|
loopAuto: region.loopAuto,
|
|
340
|
-
reverse: overrides == null ? void 0 : overrides.reverse
|
|
354
|
+
reverse: (_f = overrides == null ? void 0 : overrides.reverse) != null ? _f : merged.reverse
|
|
341
355
|
};
|
|
342
356
|
}
|
|
343
357
|
|
|
@@ -515,14 +529,6 @@ var SampleLoaderImpl = class {
|
|
|
515
529
|
__privateSet(this, _context, context);
|
|
516
530
|
__privateSet(this, _storage, (_a = options == null ? void 0 : options.storage) != null ? _a : HttpStorage);
|
|
517
531
|
}
|
|
518
|
-
/**
|
|
519
|
-
* Load all samples referenced in `json`. Returns a Map of sample name →
|
|
520
|
-
* AudioBuffer. Progress is reported via `onProgress` callback or via
|
|
521
|
-
* options object.
|
|
522
|
-
*
|
|
523
|
-
* - `buffers` in options: pre-loaded buffers — skips fetch for these names.
|
|
524
|
-
* - All samples load in parallel. Failed samples are silently omitted.
|
|
525
|
-
*/
|
|
526
532
|
load(json, onProgressOrOptions) {
|
|
527
533
|
return __async(this, null, function* () {
|
|
528
534
|
var _a, _b;
|
|
@@ -642,14 +648,6 @@ var SchedulerImpl = class {
|
|
|
642
648
|
__privateSet(this, _intervalMs, (_b = options == null ? void 0 : options.intervalMs) != null ? _b : INTERVAL_MS_DEFAULT);
|
|
643
649
|
__privateSet(this, _queue, new SortedQueue((a, b) => a.time - b.time));
|
|
644
650
|
}
|
|
645
|
-
/**
|
|
646
|
-
* Schedule a callback for a NoteEvent.
|
|
647
|
-
*
|
|
648
|
-
* - If the event's time falls within the lookahead window (or has no time), the
|
|
649
|
-
* callback is called synchronously and a no-op StopFn is returned.
|
|
650
|
-
* - Otherwise the event is queued, the interval is started if needed, and a StopFn
|
|
651
|
-
* is returned that removes the event from the queue before it is dispatched.
|
|
652
|
-
*/
|
|
653
651
|
schedule(event, callback) {
|
|
654
652
|
var _a;
|
|
655
653
|
const now = __privateGet(this, _context2).currentTime;
|
|
@@ -665,10 +663,6 @@ var SchedulerImpl = class {
|
|
|
665
663
|
__privateGet(this, _queue).removeAll((q) => q === item);
|
|
666
664
|
};
|
|
667
665
|
}
|
|
668
|
-
/**
|
|
669
|
-
* Clear all queued (not-yet-dispatched) events and stop the interval.
|
|
670
|
-
* Does not affect voices that are already playing.
|
|
671
|
-
*/
|
|
672
666
|
stop() {
|
|
673
667
|
__privateGet(this, _queue).clear();
|
|
674
668
|
if (__privateGet(this, _intervalId) !== void 0) {
|
|
@@ -758,7 +752,7 @@ var Voice = class {
|
|
|
758
752
|
__privateSet(this, _startAt, startAt);
|
|
759
753
|
let offsetSec = 0;
|
|
760
754
|
if (params.offset > 0) {
|
|
761
|
-
offsetSec = params.reverse ?
|
|
755
|
+
offsetSec = params.reverse ? buffer.duration - params.offset : params.offset;
|
|
762
756
|
}
|
|
763
757
|
source.start(startAt, offsetSec);
|
|
764
758
|
__privateSet(this, _source, source);
|
|
@@ -941,10 +935,12 @@ var SmplrImpl = class {
|
|
|
941
935
|
__privateSet(this, _voices2, new VoiceManager());
|
|
942
936
|
this.loader = (_c = options == null ? void 0 : options.loader) != null ? _c : SampleLoader(context, { storage: options == null ? void 0 : options.storage });
|
|
943
937
|
if (json) {
|
|
944
|
-
this.ready = this.loader.load(json,
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
938
|
+
this.ready = this.loader.load(json, {
|
|
939
|
+
onProgress: (loaded, total) => {
|
|
940
|
+
var _a2;
|
|
941
|
+
__privateSet(this, _loadProgress, { loaded, total });
|
|
942
|
+
(_a2 = __privateGet(this, _onLoadProgress)) == null ? void 0 : _a2.call(this, { loaded, total });
|
|
943
|
+
}
|
|
948
944
|
}).then((buffers) => {
|
|
949
945
|
__privateSet(this, _buffers, buffers);
|
|
950
946
|
});
|
|
@@ -1683,6 +1679,9 @@ function fetchJSON(url, storage) {
|
|
|
1683
1679
|
return r.json();
|
|
1684
1680
|
});
|
|
1685
1681
|
jsonCache.set(url, p);
|
|
1682
|
+
p.catch(() => {
|
|
1683
|
+
if (jsonCache.get(url) === p) jsonCache.delete(url);
|
|
1684
|
+
});
|
|
1686
1685
|
}
|
|
1687
1686
|
return p;
|
|
1688
1687
|
}
|
|
@@ -2503,6 +2502,7 @@ var SequencerImpl = class {
|
|
|
2503
2502
|
this._clock.stop();
|
|
2504
2503
|
this._stopLoop();
|
|
2505
2504
|
this._endScheduled = false;
|
|
2505
|
+
for (const stopFn of this._activeVoices.values()) stopFn();
|
|
2506
2506
|
this._activeVoices.clear();
|
|
2507
2507
|
this._emitStateChange("stopped");
|
|
2508
2508
|
return this;
|
|
@@ -3006,8 +3006,6 @@ function buildRegion(props, pathFromSampleName) {
|
|
|
3006
3006
|
if (tune !== void 0) region.tune = tune / 100;
|
|
3007
3007
|
const ampRelease = num(props, "ampeg_release");
|
|
3008
3008
|
if (ampRelease !== void 0) region.ampRelease = ampRelease;
|
|
3009
|
-
const ampVelcurve = numArr(props, "amp_velcurve");
|
|
3010
|
-
if (ampVelcurve) region.ampVelCurve = ampVelcurve;
|
|
3011
3009
|
return region;
|
|
3012
3010
|
}
|
|
3013
3011
|
function resolveDefines(sfz) {
|
|
@@ -3085,16 +3083,18 @@ function str(props, key) {
|
|
|
3085
3083
|
if (typeof v === "string") return v;
|
|
3086
3084
|
return void 0;
|
|
3087
3085
|
}
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3086
|
+
|
|
3087
|
+
// src/fetch-ok.ts
|
|
3088
|
+
function fetchOk(url) {
|
|
3089
|
+
return __async(this, null, function* () {
|
|
3090
|
+
const res = yield fetch(url);
|
|
3091
|
+
if (!res.ok) {
|
|
3092
|
+
throw new Error(
|
|
3093
|
+
`smplr: failed to fetch ${url} (${res.status} ${res.statusText})`
|
|
3094
|
+
);
|
|
3095
3095
|
}
|
|
3096
|
-
|
|
3097
|
-
|
|
3096
|
+
return res;
|
|
3097
|
+
});
|
|
3098
3098
|
}
|
|
3099
3099
|
|
|
3100
3100
|
// src/tremolo.ts
|
|
@@ -3140,8 +3140,10 @@ function createTremolo(context, depth) {
|
|
|
3140
3140
|
splitter.disconnect(ampR, 1);
|
|
3141
3141
|
ampL.disconnect(merger, 0, 0);
|
|
3142
3142
|
ampR.disconnect(merger, 0, 1);
|
|
3143
|
-
lfoL.disconnect(
|
|
3144
|
-
|
|
3143
|
+
lfoL.disconnect(lfoLAmp);
|
|
3144
|
+
lfoLAmp.disconnect(ampL.gain);
|
|
3145
|
+
lfoR.disconnect(lfoRAmp);
|
|
3146
|
+
lfoRAmp.disconnect(ampR.gain);
|
|
3145
3147
|
merger.disconnect(output);
|
|
3146
3148
|
};
|
|
3147
3149
|
return { input, output };
|
|
@@ -3194,7 +3196,7 @@ var ElectricPiano = Instrument(
|
|
|
3194
3196
|
};
|
|
3195
3197
|
const tremoloNode = createTremolo(ctx, depth.subscribe);
|
|
3196
3198
|
smplr.output.addInsert(tremoloNode);
|
|
3197
|
-
const ready =
|
|
3199
|
+
const ready = fetchOk(config.sfzUrl).then((r) => r.text()).then(
|
|
3198
3200
|
(sfzText) => {
|
|
3199
3201
|
var _a;
|
|
3200
3202
|
return smplr.loadInstrument(
|
|
@@ -3212,15 +3214,11 @@ var ElectricPiano = Instrument(
|
|
|
3212
3214
|
|
|
3213
3215
|
// src/versilian.ts
|
|
3214
3216
|
var VCSL_BASE_URL = "https://smpldsnds.github.io/sgossner-vcsl";
|
|
3215
|
-
var
|
|
3217
|
+
var instrumentsPromise;
|
|
3216
3218
|
function getVersilianInstruments() {
|
|
3217
|
-
return
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
(res) => res.json()
|
|
3221
|
-
);
|
|
3222
|
-
return instruments;
|
|
3223
|
-
});
|
|
3219
|
+
return instrumentsPromise != null ? instrumentsPromise : instrumentsPromise = fetchOk(
|
|
3220
|
+
VCSL_BASE_URL + "/sfz_files.json"
|
|
3221
|
+
).then((res) => res.json());
|
|
3224
3222
|
}
|
|
3225
3223
|
var Versilian = Instrument(
|
|
3226
3224
|
(ctx, options = {}, smplr) => loadVersilianInstrument(smplr, options)
|
|
@@ -3231,7 +3229,7 @@ function loadVersilianInstrument(smplr, options) {
|
|
|
3231
3229
|
const sfzUrl = `${VCSL_BASE_URL}/${instrument}.sfz`;
|
|
3232
3230
|
const base = instrument.slice(0, instrument.lastIndexOf("/") + 1);
|
|
3233
3231
|
const sampleBaseUrl2 = `${VCSL_BASE_URL}/${base}`;
|
|
3234
|
-
return
|
|
3232
|
+
return fetchOk(sfzUrl).then((r) => r.text()).then(
|
|
3235
3233
|
(sfzText) => smplr.loadInstrument(
|
|
3236
3234
|
sfzToPreset(sfzText, {
|
|
3237
3235
|
baseUrl: sampleBaseUrl2,
|
|
@@ -3336,7 +3334,7 @@ var Mellotron = Instrument(
|
|
|
3336
3334
|
const variation = INSTRUMENT_VARIATIONS[instrument];
|
|
3337
3335
|
const instrumentName = variation ? variation[0] : instrument;
|
|
3338
3336
|
const baseUrl = `https://smpldsnds.github.io/archiveorg-mellotron/${instrumentName}/`;
|
|
3339
|
-
return
|
|
3337
|
+
return fetchOk(baseUrl + "files.json").then((r) => r.json()).then(
|
|
3340
3338
|
(names) => smplr.loadInstrument(
|
|
3341
3339
|
mellotronToPreset(names, {
|
|
3342
3340
|
instrument: instrumentName,
|
|
@@ -3352,7 +3350,7 @@ function mellotronToPreset(sampleNames, config) {
|
|
|
3352
3350
|
for (const sampleName of sampleNames) {
|
|
3353
3351
|
if (config.variation && !sampleName.includes(config.variation)) continue;
|
|
3354
3352
|
const midi = toMidi((_a = sampleName.split(" ")[0]) != null ? _a : "");
|
|
3355
|
-
if (
|
|
3353
|
+
if (midi === void 0) continue;
|
|
3356
3354
|
entries.push([midi, sampleName]);
|
|
3357
3355
|
}
|
|
3358
3356
|
const spread = spreadKeyRanges(entries);
|
|
@@ -3405,7 +3403,7 @@ function createDattorroReverbEffect(context) {
|
|
|
3405
3403
|
if (!ready) {
|
|
3406
3404
|
const blob = new Blob([PROCESSOR], { type: "application/javascript" });
|
|
3407
3405
|
const url = URL.createObjectURL(blob);
|
|
3408
|
-
ready = context.audioWorklet.addModule(url);
|
|
3406
|
+
ready = context.audioWorklet.addModule(url).finally(() => URL.revokeObjectURL(url));
|
|
3409
3407
|
init.set(context, ready);
|
|
3410
3408
|
}
|
|
3411
3409
|
yield ready;
|
|
@@ -3437,7 +3435,7 @@ var ReverbImpl = class {
|
|
|
3437
3435
|
}
|
|
3438
3436
|
getParam(name) {
|
|
3439
3437
|
var _a;
|
|
3440
|
-
return (_a = __privateGet(this, _effect)) == null ? void 0 : _a.parameters.get(
|
|
3438
|
+
return (_a = __privateGet(this, _effect)) == null ? void 0 : _a.parameters.get(name);
|
|
3441
3439
|
}
|
|
3442
3440
|
get isReady() {
|
|
3443
3441
|
return __privateGet(this, _effect) !== void 0;
|
|
@@ -3446,11 +3444,13 @@ var ReverbImpl = class {
|
|
|
3446
3444
|
return __privateGet(this, _ready);
|
|
3447
3445
|
}
|
|
3448
3446
|
connect(output) {
|
|
3449
|
-
if (__privateGet(this, _effect)) {
|
|
3450
|
-
__privateGet(this, _effect).disconnect(__privateGet(this, _output));
|
|
3451
|
-
__privateGet(this, _effect).connect(output);
|
|
3452
|
-
}
|
|
3453
3447
|
__privateSet(this, _output, output);
|
|
3448
|
+
__privateGet(this, _ready).then(() => {
|
|
3449
|
+
if (__privateGet(this, _effect)) {
|
|
3450
|
+
__privateGet(this, _effect).disconnect();
|
|
3451
|
+
__privateGet(this, _effect).connect(__privateGet(this, _output));
|
|
3452
|
+
}
|
|
3453
|
+
});
|
|
3454
3454
|
}
|
|
3455
3455
|
};
|
|
3456
3456
|
_effect = new WeakMap();
|
|
@@ -3597,7 +3597,7 @@ var Smolken = Instrument(
|
|
|
3597
3597
|
(ctx, options = {}, smplr) => {
|
|
3598
3598
|
var _a;
|
|
3599
3599
|
const sfzUrl = getSmolkenUrl((_a = options.instrument) != null ? _a : "Arco");
|
|
3600
|
-
return
|
|
3600
|
+
return fetchOk(sfzUrl).then((r) => r.text()).then(
|
|
3601
3601
|
(sfzText) => smplr.loadInstrument(
|
|
3602
3602
|
sfzToPreset(sfzText, {
|
|
3603
3603
|
baseUrl: SMOLKEN_BASE_URL,
|
|
@@ -3758,16 +3758,14 @@ function fetchSoundfontLoopData(url, sampleRate = 44100) {
|
|
|
3758
3758
|
return __async(this, null, function* () {
|
|
3759
3759
|
if (!url) return void 0;
|
|
3760
3760
|
try {
|
|
3761
|
-
const req = yield
|
|
3762
|
-
if (req.status !== 200) return;
|
|
3761
|
+
const req = yield fetchOk(url);
|
|
3763
3762
|
const raw = yield req.json();
|
|
3764
3763
|
const loopData = {};
|
|
3765
3764
|
Object.keys(raw).forEach((key) => {
|
|
3766
3765
|
const midi = toMidi(key);
|
|
3767
|
-
if (midi)
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
}
|
|
3766
|
+
if (midi === void 0) return;
|
|
3767
|
+
const offsets = raw[key];
|
|
3768
|
+
loopData[midi] = [offsets[0] / sampleRate, offsets[1] / sampleRate];
|
|
3771
3769
|
});
|
|
3772
3770
|
return loopData;
|
|
3773
3771
|
} catch (err) {
|
|
@@ -3812,7 +3810,7 @@ function decodeSoundfontFile(context, config) {
|
|
|
3812
3810
|
yield Promise.all(
|
|
3813
3811
|
noteNames.map((noteName) => __async(null, null, function* () {
|
|
3814
3812
|
const midi = toMidi(noteName);
|
|
3815
|
-
if (
|
|
3813
|
+
if (midi === void 0) return;
|
|
3816
3814
|
try {
|
|
3817
3815
|
const audioData = base64ToArrayBuffer(
|
|
3818
3816
|
removeBase64Prefix(json[noteName])
|
|
@@ -3913,7 +3911,7 @@ function removeBase64Prefix(audioBase64) {
|
|
|
3913
3911
|
return audioBase64.slice(audioBase64.indexOf(",") + 1);
|
|
3914
3912
|
}
|
|
3915
3913
|
function base64ToArrayBuffer(base64) {
|
|
3916
|
-
const decoded =
|
|
3914
|
+
const decoded = atob(base64);
|
|
3917
3915
|
const len = decoded.length;
|
|
3918
3916
|
const bytes = new Uint8Array(len);
|
|
3919
3917
|
for (let i = 0; i < len; i++) {
|
|
@@ -3970,7 +3968,11 @@ var Soundfont2 = Instrument(
|
|
|
3970
3968
|
const sf2inst = soundfont == null ? void 0 : soundfont.instruments.find(
|
|
3971
3969
|
(inst) => inst.header.name === instrumentName
|
|
3972
3970
|
);
|
|
3973
|
-
if (!sf2inst)
|
|
3971
|
+
if (!sf2inst) {
|
|
3972
|
+
throw new Error(
|
|
3973
|
+
`Soundfont2: instrument "${instrumentName}" not found`
|
|
3974
|
+
);
|
|
3975
|
+
}
|
|
3974
3976
|
const { json, buffers } = sf2InstrumentToPreset(sf2inst, ctx);
|
|
3975
3977
|
return baseLoadInstrument(json, buffers);
|
|
3976
3978
|
}
|