smplr 0.26.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/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)) {
@@ -335,7 +350,6 @@ 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
354
  reverse: (_f = overrides == null ? void 0 : overrides.reverse) != null ? _f : merged.reverse
341
355
  };
@@ -738,7 +752,7 @@ var Voice = class {
738
752
  __privateSet(this, _startAt, startAt);
739
753
  let offsetSec = 0;
740
754
  if (params.offset > 0) {
741
- offsetSec = params.reverse ? (buffer.length - params.offset) / buffer.sampleRate : params.offset / buffer.sampleRate;
755
+ offsetSec = params.reverse ? buffer.duration - params.offset : params.offset;
742
756
  }
743
757
  source.start(startAt, offsetSec);
744
758
  __privateSet(this, _source, source);
@@ -1665,6 +1679,9 @@ function fetchJSON(url, storage) {
1665
1679
  return r.json();
1666
1680
  });
1667
1681
  jsonCache.set(url, p);
1682
+ p.catch(() => {
1683
+ if (jsonCache.get(url) === p) jsonCache.delete(url);
1684
+ });
1668
1685
  }
1669
1686
  return p;
1670
1687
  }
@@ -2989,8 +3006,6 @@ function buildRegion(props, pathFromSampleName) {
2989
3006
  if (tune !== void 0) region.tune = tune / 100;
2990
3007
  const ampRelease = num(props, "ampeg_release");
2991
3008
  if (ampRelease !== void 0) region.ampRelease = ampRelease;
2992
- const ampVelcurve = numArr(props, "amp_velcurve");
2993
- if (ampVelcurve) region.ampVelCurve = ampVelcurve;
2994
3009
  return region;
2995
3010
  }
2996
3011
  function resolveDefines(sfz) {
@@ -3068,16 +3083,18 @@ function str(props, key) {
3068
3083
  if (typeof v === "string") return v;
3069
3084
  return void 0;
3070
3085
  }
3071
- function numArr(props, _prefix) {
3072
- for (const [k, v] of Object.entries(props)) {
3073
- if (k.startsWith("amp_velcurve_")) {
3074
- const vel = Number(k.slice("amp_velcurve_".length));
3075
- if (!isNaN(vel) && typeof v === "number") {
3076
- return [vel, v];
3077
- }
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
+ );
3078
3095
  }
3079
- }
3080
- return void 0;
3096
+ return res;
3097
+ });
3081
3098
  }
3082
3099
 
3083
3100
  // src/tremolo.ts
@@ -3179,7 +3196,7 @@ var ElectricPiano = Instrument(
3179
3196
  };
3180
3197
  const tremoloNode = createTremolo(ctx, depth.subscribe);
3181
3198
  smplr.output.addInsert(tremoloNode);
3182
- const ready = fetch(config.sfzUrl).then((r) => r.text()).then(
3199
+ const ready = fetchOk(config.sfzUrl).then((r) => r.text()).then(
3183
3200
  (sfzText) => {
3184
3201
  var _a;
3185
3202
  return smplr.loadInstrument(
@@ -3199,7 +3216,7 @@ var ElectricPiano = Instrument(
3199
3216
  var VCSL_BASE_URL = "https://smpldsnds.github.io/sgossner-vcsl";
3200
3217
  var instrumentsPromise;
3201
3218
  function getVersilianInstruments() {
3202
- return instrumentsPromise != null ? instrumentsPromise : instrumentsPromise = fetch(
3219
+ return instrumentsPromise != null ? instrumentsPromise : instrumentsPromise = fetchOk(
3203
3220
  VCSL_BASE_URL + "/sfz_files.json"
3204
3221
  ).then((res) => res.json());
3205
3222
  }
@@ -3212,7 +3229,7 @@ function loadVersilianInstrument(smplr, options) {
3212
3229
  const sfzUrl = `${VCSL_BASE_URL}/${instrument}.sfz`;
3213
3230
  const base = instrument.slice(0, instrument.lastIndexOf("/") + 1);
3214
3231
  const sampleBaseUrl2 = `${VCSL_BASE_URL}/${base}`;
3215
- return fetch(sfzUrl).then((r) => r.text()).then(
3232
+ return fetchOk(sfzUrl).then((r) => r.text()).then(
3216
3233
  (sfzText) => smplr.loadInstrument(
3217
3234
  sfzToPreset(sfzText, {
3218
3235
  baseUrl: sampleBaseUrl2,
@@ -3317,7 +3334,7 @@ var Mellotron = Instrument(
3317
3334
  const variation = INSTRUMENT_VARIATIONS[instrument];
3318
3335
  const instrumentName = variation ? variation[0] : instrument;
3319
3336
  const baseUrl = `https://smpldsnds.github.io/archiveorg-mellotron/${instrumentName}/`;
3320
- return fetch(baseUrl + "files.json").then((r) => r.json()).then(
3337
+ return fetchOk(baseUrl + "files.json").then((r) => r.json()).then(
3321
3338
  (names) => smplr.loadInstrument(
3322
3339
  mellotronToPreset(names, {
3323
3340
  instrument: instrumentName,
@@ -3386,7 +3403,7 @@ function createDattorroReverbEffect(context) {
3386
3403
  if (!ready) {
3387
3404
  const blob = new Blob([PROCESSOR], { type: "application/javascript" });
3388
3405
  const url = URL.createObjectURL(blob);
3389
- ready = context.audioWorklet.addModule(url);
3406
+ ready = context.audioWorklet.addModule(url).finally(() => URL.revokeObjectURL(url));
3390
3407
  init.set(context, ready);
3391
3408
  }
3392
3409
  yield ready;
@@ -3427,11 +3444,13 @@ var ReverbImpl = class {
3427
3444
  return __privateGet(this, _ready);
3428
3445
  }
3429
3446
  connect(output) {
3430
- if (__privateGet(this, _effect)) {
3431
- __privateGet(this, _effect).disconnect(__privateGet(this, _output));
3432
- __privateGet(this, _effect).connect(output);
3433
- }
3434
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
+ });
3435
3454
  }
3436
3455
  };
3437
3456
  _effect = new WeakMap();
@@ -3578,7 +3597,7 @@ var Smolken = Instrument(
3578
3597
  (ctx, options = {}, smplr) => {
3579
3598
  var _a;
3580
3599
  const sfzUrl = getSmolkenUrl((_a = options.instrument) != null ? _a : "Arco");
3581
- return fetch(sfzUrl).then((r) => r.text()).then(
3600
+ return fetchOk(sfzUrl).then((r) => r.text()).then(
3582
3601
  (sfzText) => smplr.loadInstrument(
3583
3602
  sfzToPreset(sfzText, {
3584
3603
  baseUrl: SMOLKEN_BASE_URL,
@@ -3739,8 +3758,7 @@ function fetchSoundfontLoopData(url, sampleRate = 44100) {
3739
3758
  return __async(this, null, function* () {
3740
3759
  if (!url) return void 0;
3741
3760
  try {
3742
- const req = yield fetch(url);
3743
- if (req.status !== 200) return;
3761
+ const req = yield fetchOk(url);
3744
3762
  const raw = yield req.json();
3745
3763
  const loopData = {};
3746
3764
  Object.keys(raw).forEach((key) => {
@@ -3893,7 +3911,7 @@ function removeBase64Prefix(audioBase64) {
3893
3911
  return audioBase64.slice(audioBase64.indexOf(",") + 1);
3894
3912
  }
3895
3913
  function base64ToArrayBuffer(base64) {
3896
- const decoded = window.atob(base64);
3914
+ const decoded = atob(base64);
3897
3915
  const len = decoded.length;
3898
3916
  const bytes = new Uint8Array(len);
3899
3917
  for (let i = 0; i < len; i++) {
@@ -3950,7 +3968,11 @@ var Soundfont2 = Instrument(
3950
3968
  const sf2inst = soundfont == null ? void 0 : soundfont.instruments.find(
3951
3969
  (inst) => inst.header.name === instrumentName
3952
3970
  );
3953
- if (!sf2inst) return void 0;
3971
+ if (!sf2inst) {
3972
+ throw new Error(
3973
+ `Soundfont2: instrument "${instrumentName}" not found`
3974
+ );
3975
+ }
3954
3976
  const { json, buffers } = sf2InstrumentToPreset(sf2inst, ctx);
3955
3977
  return baseLoadInstrument(json, buffers);
3956
3978
  }