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/README.md +18 -11
- package/dist/index.d.mts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +50 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +50 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -46,7 +46,8 @@ seq.start();
|
|
|
46
46
|
import { SplendidGrandPiano, Reverb, renderOffline } from "smplr";
|
|
47
47
|
|
|
48
48
|
const wav = await renderOffline(async (context) => {
|
|
49
|
-
const piano =
|
|
49
|
+
const piano = SplendidGrandPiano(context);
|
|
50
|
+
await piano.ready;
|
|
50
51
|
piano.output.addEffect("reverb", Reverb(context), 0.3);
|
|
51
52
|
["C4", "E4", "G4", "C5"].forEach((note, i) => {
|
|
52
53
|
piano.start({ note, time: i * 0.4, duration: 0.4 });
|
|
@@ -144,10 +145,10 @@ const marimba = Soundfont(context, { instrument: "marimba" });
|
|
|
144
145
|
You can start playing notes as soon as one sample is loaded. To wait for all of them, await either:
|
|
145
146
|
|
|
146
147
|
- `piano.ready` — resolves to `void` (preferred for new code).
|
|
147
|
-
- `piano.load` — resolves to the instrument itself, so you can create and await in one line:
|
|
148
148
|
|
|
149
149
|
```js
|
|
150
|
-
const piano =
|
|
150
|
+
const piano = SplendidGrandPiano(context);
|
|
151
|
+
await piano.ready;
|
|
151
152
|
```
|
|
152
153
|
|
|
153
154
|
> Upgrading from older versions? See [MIGRATE.md](./MIGRATE.md).
|
|
@@ -235,7 +236,7 @@ const now = context.currentTime;
|
|
|
235
236
|
|
|
236
237
|
#### Looping
|
|
237
238
|
|
|
238
|
-
You can loop a note by using `loop`, `loopStart` and `loopEnd
|
|
239
|
+
You can loop a note by using `loop`, `loopStart` and `loopEnd` (positions in seconds):
|
|
239
240
|
|
|
240
241
|
```js
|
|
241
242
|
const context = new AudioContext();
|
|
@@ -314,6 +315,8 @@ To change the mix level, use `output.setEffectMix(name, mix)`:
|
|
|
314
315
|
piano.output.setEffectMix("reverb", 0.5);
|
|
315
316
|
```
|
|
316
317
|
|
|
318
|
+
Send buses are **post-fader**: they tap the signal after `output.volume` (and after any inserts), so turning `output.volume` down proportionally reduces what reaches the effect. Set `output.volume` to 0 and the send goes silent too.
|
|
319
|
+
|
|
317
320
|
### Events
|
|
318
321
|
|
|
319
322
|
Two events are available: `onStart` and `onEnded`. Both callbacks receive the started note as a parameter, and can be configured globally:
|
|
@@ -721,7 +724,8 @@ Render audio offline (faster than real-time) and export it as a WAV file. Uses `
|
|
|
721
724
|
import { renderOffline } from "smplr";
|
|
722
725
|
|
|
723
726
|
const result = await renderOffline(async (context) => {
|
|
724
|
-
const piano =
|
|
727
|
+
const piano = SplendidGrandPiano(context);
|
|
728
|
+
await piano.ready;
|
|
725
729
|
piano.start({ note: "C4", time: 0, duration: 1 });
|
|
726
730
|
piano.start({ note: "E4", time: 0.5, duration: 1 });
|
|
727
731
|
});
|
|
@@ -764,11 +768,12 @@ import { SplendidGrandPiano, SampleLoader, renderOffline } from "smplr";
|
|
|
764
768
|
|
|
765
769
|
const loader = SampleLoader(audioContext);
|
|
766
770
|
const piano = SplendidGrandPiano(audioContext, { loader });
|
|
767
|
-
await piano.
|
|
771
|
+
await piano.ready;
|
|
768
772
|
|
|
769
773
|
// Offline render reuses cached buffers — no re-fetch
|
|
770
774
|
const result = await renderOffline(async (context) => {
|
|
771
|
-
const offlinePiano =
|
|
775
|
+
const offlinePiano = SplendidGrandPiano(context, { loader });
|
|
776
|
+
await offlinePiano.ready;
|
|
772
777
|
offlinePiano.start({ note: "C4", time: 0, duration: 1 });
|
|
773
778
|
});
|
|
774
779
|
```
|
|
@@ -782,7 +787,8 @@ const { renderOffline, SplendidGrandPiano } =
|
|
|
782
787
|
await import("https://esm.sh/smplr");
|
|
783
788
|
|
|
784
789
|
const result = await renderOffline(async (context) => {
|
|
785
|
-
const piano =
|
|
790
|
+
const piano = SplendidGrandPiano(context);
|
|
791
|
+
await piano.ready;
|
|
786
792
|
piano.start({ note: "C4", time: 0, duration: 2 });
|
|
787
793
|
});
|
|
788
794
|
result.downloadWav16("bug-report.wav");
|
|
@@ -1029,7 +1035,7 @@ const context = new AudioContext();
|
|
|
1029
1035
|
const drums = DrumAbuse(context, {
|
|
1030
1036
|
source: { kind: "machine", machine: "roland-tr-808" },
|
|
1031
1037
|
});
|
|
1032
|
-
await drums.
|
|
1038
|
+
await drums.ready;
|
|
1033
1039
|
|
|
1034
1040
|
drums.start({ note: "kick" });
|
|
1035
1041
|
|
|
@@ -1076,7 +1082,8 @@ const instruments = getSmolkenNames(); // => Arco, Pizzicato & Switched
|
|
|
1076
1082
|
|
|
1077
1083
|
// Create an instrument
|
|
1078
1084
|
const context = new AudioContext();
|
|
1079
|
-
const doubleBass =
|
|
1085
|
+
const doubleBass = Smolken(context, { instrument: "Arco" });
|
|
1086
|
+
await doubleBass.ready;
|
|
1080
1087
|
```
|
|
1081
1088
|
|
|
1082
1089
|
### Versilian
|
|
@@ -1109,7 +1116,7 @@ const sampler = Soundfont2(context, {
|
|
|
1109
1116
|
createSoundfont: (data) => new SoundFont2(data),
|
|
1110
1117
|
});
|
|
1111
1118
|
|
|
1112
|
-
sampler.
|
|
1119
|
+
sampler.ready.then(() => {
|
|
1113
1120
|
// list all available instruments for the soundfont
|
|
1114
1121
|
console.log(sampler.instrumentNames);
|
|
1115
1122
|
|
package/dist/index.d.mts
CHANGED
|
@@ -26,6 +26,15 @@ declare class Channel {
|
|
|
26
26
|
get pan(): number;
|
|
27
27
|
set pan(value: number);
|
|
28
28
|
addInsert(effect: AudioNode | AudioInsert): void;
|
|
29
|
+
/**
|
|
30
|
+
* Add a send effect on a parallel bus.
|
|
31
|
+
*
|
|
32
|
+
* The send is **post-fader**: it taps the channel signal after the volume
|
|
33
|
+
* gain (and after any inserts added via {@link addInsert}), before the
|
|
34
|
+
* panner. Lowering `volume` proportionally lowers the send level; `volume = 0`
|
|
35
|
+
* silences the send too. Inserts are upstream of the tap, so they are heard
|
|
36
|
+
* on the send.
|
|
37
|
+
*/
|
|
29
38
|
addEffect(name: string, effect: AudioNode | {
|
|
30
39
|
input: AudioNode;
|
|
31
40
|
}, mixValue: number): void;
|
|
@@ -101,7 +110,6 @@ type SmplrRegion = PlaybackParams & {
|
|
|
101
110
|
group?: number;
|
|
102
111
|
offBy?: number;
|
|
103
112
|
trigger?: "first" | "legato";
|
|
104
|
-
ampVelCurve?: [number, number];
|
|
105
113
|
/** Auto-compute loop points from buffer duration ratios (0–1). */
|
|
106
114
|
loopAuto?: {
|
|
107
115
|
startRatio: number;
|
|
@@ -186,7 +194,7 @@ type LoadProgress = {
|
|
|
186
194
|
};
|
|
187
195
|
/**
|
|
188
196
|
* Fully resolved playback parameters for a single Voice.
|
|
189
|
-
* Output of resolveParams() — all fields are required, no optionals except
|
|
197
|
+
* Output of resolveParams() — all fields are required, no optionals except loopAuto.
|
|
190
198
|
*/
|
|
191
199
|
type VoiceParams = {
|
|
192
200
|
detune: number;
|
|
@@ -199,7 +207,6 @@ type VoiceParams = {
|
|
|
199
207
|
loop: boolean;
|
|
200
208
|
loopStart: number;
|
|
201
209
|
loopEnd: number;
|
|
202
|
-
ampVelCurve?: [number, number];
|
|
203
210
|
/** If set, loop points are computed from buffer.duration at play time. */
|
|
204
211
|
loopAuto?: {
|
|
205
212
|
startRatio: number;
|
|
@@ -1230,7 +1237,7 @@ declare function sf2InstrumentToPreset(sf2Instrument: Sf2Instrument, context: Ba
|
|
|
1230
1237
|
};
|
|
1231
1238
|
type Soundfont2SamplerExtras = {
|
|
1232
1239
|
readonly instrumentNames: string[];
|
|
1233
|
-
loadInstrument(instrumentName: string): Promise<void
|
|
1240
|
+
loadInstrument(instrumentName: string): Promise<void>;
|
|
1234
1241
|
};
|
|
1235
1242
|
declare const Soundfont2: InstrumentFactory<Soundfont2Options, Soundfont2SamplerExtras>;
|
|
1236
1243
|
/** Instance type returned by the {@link Soundfont2} factory. */
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,15 @@ declare class Channel {
|
|
|
26
26
|
get pan(): number;
|
|
27
27
|
set pan(value: number);
|
|
28
28
|
addInsert(effect: AudioNode | AudioInsert): void;
|
|
29
|
+
/**
|
|
30
|
+
* Add a send effect on a parallel bus.
|
|
31
|
+
*
|
|
32
|
+
* The send is **post-fader**: it taps the channel signal after the volume
|
|
33
|
+
* gain (and after any inserts added via {@link addInsert}), before the
|
|
34
|
+
* panner. Lowering `volume` proportionally lowers the send level; `volume = 0`
|
|
35
|
+
* silences the send too. Inserts are upstream of the tap, so they are heard
|
|
36
|
+
* on the send.
|
|
37
|
+
*/
|
|
29
38
|
addEffect(name: string, effect: AudioNode | {
|
|
30
39
|
input: AudioNode;
|
|
31
40
|
}, mixValue: number): void;
|
|
@@ -101,7 +110,6 @@ type SmplrRegion = PlaybackParams & {
|
|
|
101
110
|
group?: number;
|
|
102
111
|
offBy?: number;
|
|
103
112
|
trigger?: "first" | "legato";
|
|
104
|
-
ampVelCurve?: [number, number];
|
|
105
113
|
/** Auto-compute loop points from buffer duration ratios (0–1). */
|
|
106
114
|
loopAuto?: {
|
|
107
115
|
startRatio: number;
|
|
@@ -186,7 +194,7 @@ type LoadProgress = {
|
|
|
186
194
|
};
|
|
187
195
|
/**
|
|
188
196
|
* Fully resolved playback parameters for a single Voice.
|
|
189
|
-
* Output of resolveParams() — all fields are required, no optionals except
|
|
197
|
+
* Output of resolveParams() — all fields are required, no optionals except loopAuto.
|
|
190
198
|
*/
|
|
191
199
|
type VoiceParams = {
|
|
192
200
|
detune: number;
|
|
@@ -199,7 +207,6 @@ type VoiceParams = {
|
|
|
199
207
|
loop: boolean;
|
|
200
208
|
loopStart: number;
|
|
201
209
|
loopEnd: number;
|
|
202
|
-
ampVelCurve?: [number, number];
|
|
203
210
|
/** If set, loop points are computed from buffer.duration at play time. */
|
|
204
211
|
loopAuto?: {
|
|
205
212
|
startRatio: number;
|
|
@@ -1230,7 +1237,7 @@ declare function sf2InstrumentToPreset(sf2Instrument: Sf2Instrument, context: Ba
|
|
|
1230
1237
|
};
|
|
1231
1238
|
type Soundfont2SamplerExtras = {
|
|
1232
1239
|
readonly instrumentNames: string[];
|
|
1233
|
-
loadInstrument(instrumentName: string): Promise<void
|
|
1240
|
+
loadInstrument(instrumentName: string): Promise<void>;
|
|
1234
1241
|
};
|
|
1235
1242
|
declare const Soundfont2: InstrumentFactory<Soundfont2Options, Soundfont2SamplerExtras>;
|
|
1236
1243
|
/** Instance type returned by the {@link Soundfont2} factory. */
|
package/dist/index.js
CHANGED
|
@@ -136,11 +136,12 @@ var HttpStorage = {
|
|
|
136
136
|
return fetch(url);
|
|
137
137
|
}
|
|
138
138
|
};
|
|
139
|
-
var _cache, _CacheStorageImpl_instances, tryFromCache_fn, saveResponse_fn;
|
|
139
|
+
var _cache, _warned, _CacheStorageImpl_instances, tryFromCache_fn, saveResponse_fn;
|
|
140
140
|
var CacheStorageImpl = class {
|
|
141
141
|
constructor(name = "smplr") {
|
|
142
142
|
__privateAdd(this, _CacheStorageImpl_instances);
|
|
143
143
|
__privateAdd(this, _cache);
|
|
144
|
+
__privateAdd(this, _warned, false);
|
|
144
145
|
if (typeof window === "undefined" || !("caches" in window)) {
|
|
145
146
|
__privateSet(this, _cache, Promise.reject("CacheStorage not supported"));
|
|
146
147
|
__privateGet(this, _cache).catch(() => {
|
|
@@ -163,6 +164,7 @@ var CacheStorageImpl = class {
|
|
|
163
164
|
}
|
|
164
165
|
};
|
|
165
166
|
_cache = new WeakMap();
|
|
167
|
+
_warned = new WeakMap();
|
|
166
168
|
_CacheStorageImpl_instances = new WeakSet();
|
|
167
169
|
tryFromCache_fn = function(request) {
|
|
168
170
|
return __async(this, null, function* () {
|
|
@@ -178,6 +180,10 @@ saveResponse_fn = function(request, response) {
|
|
|
178
180
|
const cache = yield __privateGet(this, _cache);
|
|
179
181
|
yield cache.put(request, response.clone());
|
|
180
182
|
} catch (err) {
|
|
183
|
+
if (!__privateGet(this, _warned)) {
|
|
184
|
+
__privateSet(this, _warned, true);
|
|
185
|
+
console.warn("smplr: failed to cache response", err);
|
|
186
|
+
}
|
|
181
187
|
}
|
|
182
188
|
});
|
|
183
189
|
};
|
|
@@ -297,6 +303,15 @@ var Channel = class {
|
|
|
297
303
|
__privateGet(this, _config).destination
|
|
298
304
|
]));
|
|
299
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Add a send effect on a parallel bus.
|
|
308
|
+
*
|
|
309
|
+
* The send is **post-fader**: it taps the channel signal after the volume
|
|
310
|
+
* gain (and after any inserts added via {@link addInsert}), before the
|
|
311
|
+
* panner. Lowering `volume` proportionally lowers the send level; `volume = 0`
|
|
312
|
+
* silences the send too. Inserts are upstream of the tap, so they are heard
|
|
313
|
+
* on the send.
|
|
314
|
+
*/
|
|
300
315
|
addEffect(name, effect, mixValue) {
|
|
301
316
|
var _a;
|
|
302
317
|
if (__privateGet(this, _disconnected)) {
|
|
@@ -403,7 +418,6 @@ function resolveParams(defaults, group, region, midi, velocity, overrides) {
|
|
|
403
418
|
loop: (_e = overrides == null ? void 0 : overrides.loop) != null ? _e : merged.loop,
|
|
404
419
|
loopStart: merged.loopStart,
|
|
405
420
|
loopEnd: merged.loopEnd,
|
|
406
|
-
ampVelCurve: region.ampVelCurve,
|
|
407
421
|
loopAuto: region.loopAuto,
|
|
408
422
|
reverse: (_f = overrides == null ? void 0 : overrides.reverse) != null ? _f : merged.reverse
|
|
409
423
|
};
|
|
@@ -806,7 +820,7 @@ var Voice = class {
|
|
|
806
820
|
__privateSet(this, _startAt, startAt);
|
|
807
821
|
let offsetSec = 0;
|
|
808
822
|
if (params.offset > 0) {
|
|
809
|
-
offsetSec = params.reverse ?
|
|
823
|
+
offsetSec = params.reverse ? buffer.duration - params.offset : params.offset;
|
|
810
824
|
}
|
|
811
825
|
source.start(startAt, offsetSec);
|
|
812
826
|
__privateSet(this, _source, source);
|
|
@@ -1733,6 +1747,9 @@ function fetchJSON(url, storage) {
|
|
|
1733
1747
|
return r.json();
|
|
1734
1748
|
});
|
|
1735
1749
|
jsonCache.set(url, p);
|
|
1750
|
+
p.catch(() => {
|
|
1751
|
+
if (jsonCache.get(url) === p) jsonCache.delete(url);
|
|
1752
|
+
});
|
|
1736
1753
|
}
|
|
1737
1754
|
return p;
|
|
1738
1755
|
}
|
|
@@ -3057,8 +3074,6 @@ function buildRegion(props, pathFromSampleName) {
|
|
|
3057
3074
|
if (tune !== void 0) region.tune = tune / 100;
|
|
3058
3075
|
const ampRelease = num(props, "ampeg_release");
|
|
3059
3076
|
if (ampRelease !== void 0) region.ampRelease = ampRelease;
|
|
3060
|
-
const ampVelcurve = numArr(props, "amp_velcurve");
|
|
3061
|
-
if (ampVelcurve) region.ampVelCurve = ampVelcurve;
|
|
3062
3077
|
return region;
|
|
3063
3078
|
}
|
|
3064
3079
|
function resolveDefines(sfz) {
|
|
@@ -3136,16 +3151,18 @@ function str(props, key) {
|
|
|
3136
3151
|
if (typeof v === "string") return v;
|
|
3137
3152
|
return void 0;
|
|
3138
3153
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3154
|
+
|
|
3155
|
+
// src/fetch-ok.ts
|
|
3156
|
+
function fetchOk(url) {
|
|
3157
|
+
return __async(this, null, function* () {
|
|
3158
|
+
const res = yield fetch(url);
|
|
3159
|
+
if (!res.ok) {
|
|
3160
|
+
throw new Error(
|
|
3161
|
+
`smplr: failed to fetch ${url} (${res.status} ${res.statusText})`
|
|
3162
|
+
);
|
|
3146
3163
|
}
|
|
3147
|
-
|
|
3148
|
-
|
|
3164
|
+
return res;
|
|
3165
|
+
});
|
|
3149
3166
|
}
|
|
3150
3167
|
|
|
3151
3168
|
// src/tremolo.ts
|
|
@@ -3247,7 +3264,7 @@ var ElectricPiano = Instrument(
|
|
|
3247
3264
|
};
|
|
3248
3265
|
const tremoloNode = createTremolo(ctx, depth.subscribe);
|
|
3249
3266
|
smplr.output.addInsert(tremoloNode);
|
|
3250
|
-
const ready =
|
|
3267
|
+
const ready = fetchOk(config.sfzUrl).then((r) => r.text()).then(
|
|
3251
3268
|
(sfzText) => {
|
|
3252
3269
|
var _a;
|
|
3253
3270
|
return smplr.loadInstrument(
|
|
@@ -3267,7 +3284,7 @@ var ElectricPiano = Instrument(
|
|
|
3267
3284
|
var VCSL_BASE_URL = "https://smpldsnds.github.io/sgossner-vcsl";
|
|
3268
3285
|
var instrumentsPromise;
|
|
3269
3286
|
function getVersilianInstruments() {
|
|
3270
|
-
return instrumentsPromise != null ? instrumentsPromise : instrumentsPromise =
|
|
3287
|
+
return instrumentsPromise != null ? instrumentsPromise : instrumentsPromise = fetchOk(
|
|
3271
3288
|
VCSL_BASE_URL + "/sfz_files.json"
|
|
3272
3289
|
).then((res) => res.json());
|
|
3273
3290
|
}
|
|
@@ -3280,7 +3297,7 @@ function loadVersilianInstrument(smplr, options) {
|
|
|
3280
3297
|
const sfzUrl = `${VCSL_BASE_URL}/${instrument}.sfz`;
|
|
3281
3298
|
const base = instrument.slice(0, instrument.lastIndexOf("/") + 1);
|
|
3282
3299
|
const sampleBaseUrl2 = `${VCSL_BASE_URL}/${base}`;
|
|
3283
|
-
return
|
|
3300
|
+
return fetchOk(sfzUrl).then((r) => r.text()).then(
|
|
3284
3301
|
(sfzText) => smplr.loadInstrument(
|
|
3285
3302
|
sfzToPreset(sfzText, {
|
|
3286
3303
|
baseUrl: sampleBaseUrl2,
|
|
@@ -3385,7 +3402,7 @@ var Mellotron = Instrument(
|
|
|
3385
3402
|
const variation = INSTRUMENT_VARIATIONS[instrument];
|
|
3386
3403
|
const instrumentName = variation ? variation[0] : instrument;
|
|
3387
3404
|
const baseUrl = `https://smpldsnds.github.io/archiveorg-mellotron/${instrumentName}/`;
|
|
3388
|
-
return
|
|
3405
|
+
return fetchOk(baseUrl + "files.json").then((r) => r.json()).then(
|
|
3389
3406
|
(names) => smplr.loadInstrument(
|
|
3390
3407
|
mellotronToPreset(names, {
|
|
3391
3408
|
instrument: instrumentName,
|
|
@@ -3454,7 +3471,7 @@ function createDattorroReverbEffect(context) {
|
|
|
3454
3471
|
if (!ready) {
|
|
3455
3472
|
const blob = new Blob([PROCESSOR], { type: "application/javascript" });
|
|
3456
3473
|
const url = URL.createObjectURL(blob);
|
|
3457
|
-
ready = context.audioWorklet.addModule(url);
|
|
3474
|
+
ready = context.audioWorklet.addModule(url).finally(() => URL.revokeObjectURL(url));
|
|
3458
3475
|
init.set(context, ready);
|
|
3459
3476
|
}
|
|
3460
3477
|
yield ready;
|
|
@@ -3495,11 +3512,13 @@ var ReverbImpl = class {
|
|
|
3495
3512
|
return __privateGet(this, _ready);
|
|
3496
3513
|
}
|
|
3497
3514
|
connect(output) {
|
|
3498
|
-
if (__privateGet(this, _effect)) {
|
|
3499
|
-
__privateGet(this, _effect).disconnect(__privateGet(this, _output));
|
|
3500
|
-
__privateGet(this, _effect).connect(output);
|
|
3501
|
-
}
|
|
3502
3515
|
__privateSet(this, _output, output);
|
|
3516
|
+
__privateGet(this, _ready).then(() => {
|
|
3517
|
+
if (__privateGet(this, _effect)) {
|
|
3518
|
+
__privateGet(this, _effect).disconnect();
|
|
3519
|
+
__privateGet(this, _effect).connect(__privateGet(this, _output));
|
|
3520
|
+
}
|
|
3521
|
+
});
|
|
3503
3522
|
}
|
|
3504
3523
|
};
|
|
3505
3524
|
_effect = new WeakMap();
|
|
@@ -3646,7 +3665,7 @@ var Smolken = Instrument(
|
|
|
3646
3665
|
(ctx, options = {}, smplr) => {
|
|
3647
3666
|
var _a;
|
|
3648
3667
|
const sfzUrl = getSmolkenUrl((_a = options.instrument) != null ? _a : "Arco");
|
|
3649
|
-
return
|
|
3668
|
+
return fetchOk(sfzUrl).then((r) => r.text()).then(
|
|
3650
3669
|
(sfzText) => smplr.loadInstrument(
|
|
3651
3670
|
sfzToPreset(sfzText, {
|
|
3652
3671
|
baseUrl: SMOLKEN_BASE_URL,
|
|
@@ -3807,8 +3826,7 @@ function fetchSoundfontLoopData(url, sampleRate = 44100) {
|
|
|
3807
3826
|
return __async(this, null, function* () {
|
|
3808
3827
|
if (!url) return void 0;
|
|
3809
3828
|
try {
|
|
3810
|
-
const req = yield
|
|
3811
|
-
if (req.status !== 200) return;
|
|
3829
|
+
const req = yield fetchOk(url);
|
|
3812
3830
|
const raw = yield req.json();
|
|
3813
3831
|
const loopData = {};
|
|
3814
3832
|
Object.keys(raw).forEach((key) => {
|
|
@@ -3961,7 +3979,7 @@ function removeBase64Prefix(audioBase64) {
|
|
|
3961
3979
|
return audioBase64.slice(audioBase64.indexOf(",") + 1);
|
|
3962
3980
|
}
|
|
3963
3981
|
function base64ToArrayBuffer(base64) {
|
|
3964
|
-
const decoded =
|
|
3982
|
+
const decoded = atob(base64);
|
|
3965
3983
|
const len = decoded.length;
|
|
3966
3984
|
const bytes = new Uint8Array(len);
|
|
3967
3985
|
for (let i = 0; i < len; i++) {
|
|
@@ -4018,7 +4036,11 @@ var Soundfont2 = Instrument(
|
|
|
4018
4036
|
const sf2inst = soundfont == null ? void 0 : soundfont.instruments.find(
|
|
4019
4037
|
(inst) => inst.header.name === instrumentName
|
|
4020
4038
|
);
|
|
4021
|
-
if (!sf2inst)
|
|
4039
|
+
if (!sf2inst) {
|
|
4040
|
+
throw new Error(
|
|
4041
|
+
`Soundfont2: instrument "${instrumentName}" not found`
|
|
4042
|
+
);
|
|
4043
|
+
}
|
|
4022
4044
|
const { json, buffers } = sf2InstrumentToPreset(sf2inst, ctx);
|
|
4023
4045
|
return baseLoadInstrument(json, buffers);
|
|
4024
4046
|
}
|