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 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 = await SplendidGrandPiano(context).load;
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 });
@@ -71,7 +72,7 @@ Samples are stored at https://github.com/smpldsnds and there is no need to downl
71
72
 
72
73
  #### Using a package manager
73
74
 
74
- Use npm or your favourite package manager to install the library to use it in your project:
75
+ Install with npm or your favourite package manager:
75
76
 
76
77
  ```
77
78
  npm i smplr
@@ -105,29 +106,27 @@ The package needs to be served as a URL from a service like [unpkg](https://unpk
105
106
 
106
107
  `smplr` ships eleven instruments out of the box. Pick one and jump to its section in the [Instrument reference](#instrument-reference) for setup details.
107
108
 
108
- | Instrument | Description | Names helper |
109
- | ------------------------------------------- | ----------------------------------------- | ----------------------------- |
110
- | [`Sampler`](#sampler) | Your own buffers or SFZ-style preset | — |
111
- | [`Soundfont`](#soundfont) | General MIDI soundfonts | `getSoundfontNames()` |
112
- | [`SplendidGrandPiano`](#splendidgrandpiano) | Sampled Steinway grand, 4 velocity layers | — |
113
- | [`ElectricPiano`](#electric-piano) | CP80, PianetT, Wurlitzer, TX81Z | `getElectricPianoNames()` |
114
- | [`DrumMachine`](#drum-machines) | Classic drum machines (TR-808, …) | `getDrumMachineNames()` |
115
- | [`DrumAbuse`](#drumabuse) | ~210 machines (Synthabuse collection) | `getDrumAbuseMachineNames()` |
116
- | [`Mallet`](#mallets) | VCSL mallets | `getMalletNames()` |
117
- | [`Mellotron`](#mellotron) | Mellotron archive samples | `getMellotronNames()` |
118
- | [`Smolken`](#smolken-double-bass) | Smolken double bass (Arco/Pizz/Switched) | `getSmolkenNames()` |
119
- | [`Versilian`](#versilian) | VCSL multi-instrument (partial support) | `getVersilianInstruments()` * |
120
- | [`Soundfont2`](#soundfont2) | Reads .sf2 files directly | — |
121
-
122
- `*` `getVersilianInstruments` is async because the catalog is fetched from the network on first call (cached thereafter).
109
+ | Instrument | Description | Names helper |
110
+ | ------------------------------------------- | ----------------------------------------- | ---------------------------- |
111
+ | [`Sampler`](#sampler) | Your own buffers or SFZ-style preset | — |
112
+ | [`Soundfont`](#soundfont) | General MIDI soundfonts | `getSoundfontNames()` |
113
+ | [`SplendidGrandPiano`](#splendidgrandpiano) | Sampled Steinway grand, 4 velocity layers | — |
114
+ | [`ElectricPiano`](#electric-piano) | CP80, PianetT, Wurlitzer, TX81Z | `getElectricPianoNames()` |
115
+ | [`DrumMachine`](#drum-machines) | Classic drum machines (TR-808, …) | `getDrumMachineNames()` |
116
+ | [`DrumAbuse`](#drumabuse) | ~210 machines (Synthabuse collection) | `getDrumAbuseMachineNames()` |
117
+ | [`Mallet`](#mallets) | VCSL mallets | `getMalletNames()` |
118
+ | [`Mellotron`](#mellotron) | Mellotron archive samples | `getMellotronNames()` |
119
+ | [`Smolken`](#smolken-double-bass) | Smolken double bass (Arco/Pizz/Switched) | `getSmolkenNames()` |
120
+ | [`Versilian`](#versilian) | VCSL multi-instrument (partial support) | `getVersilianInstruments()` |
121
+ | [`Soundfont2`](#soundfont2) | Reads .sf2 files directly | — |
123
122
 
124
- Each names helper returns the strings you can pass to the corresponding factory's `instrument` option.
123
+ Each names helper returns strings to pass as the factory's `instrument` option. `getVersilianInstruments` is async (the catalog is fetched once and cached).
125
124
 
126
- If none of the bundled instruments fits your use case, you can author your own — see [Defining your own instrument](#defining-your-own-instrument).
125
+ To build your own instrument, see [Defining your own instrument](#defining-your-own-instrument).
127
126
 
128
127
  ## Using an instrument
129
128
 
130
- Every smplr instrument follows the same usage pattern. This section covers what's shared across all of them; for instrument-specific options, see the [Instrument reference](#instrument-reference).
129
+ The shared API below applies to every instrument. Instrument-specific options live in the [Instrument reference](#instrument-reference).
131
130
 
132
131
  ### Create and load
133
132
 
@@ -146,10 +145,10 @@ const marimba = Soundfont(context, { instrument: "marimba" });
146
145
  You can start playing notes as soon as one sample is loaded. To wait for all of them, await either:
147
146
 
148
147
  - `piano.ready` — resolves to `void` (preferred for new code).
149
- - `piano.load` — resolves to the instrument itself, so you can create and await in one line:
150
148
 
151
149
  ```js
152
- const piano = await SplendidGrandPiano(context).load;
150
+ const piano = SplendidGrandPiano(context);
151
+ await piano.ready;
153
152
  ```
154
153
 
155
154
  > Upgrading from older versions? See [MIGRATE.md](./MIGRATE.md).
@@ -191,13 +190,13 @@ All instruments share some configuration options, passed as the second argument
191
190
 
192
191
  #### Start and stop notes
193
192
 
194
- The `start` function accepts a bunch of options:
193
+ The `start` function accepts these options:
195
194
 
196
195
  ```js
197
196
  piano.start({ note: "C4", velocity: 80, time: 5, duration: 1 });
198
197
  ```
199
198
 
200
- The `velocity` is a number between 0 and 127 the represents at which velocity the key is pressed. The bigger the number, louder the sound. But `velocity` not only controls the loudness. In some instruments, it also affects the timbre.
199
+ `velocity` (0127) represents how hard the key is pressed: louder at higher values, and on some instruments it also changes timbre.
201
200
 
202
201
  The `start` function returns a `stop` function for the given note:
203
202
 
@@ -224,9 +223,9 @@ piano.stop(60); // stop the note(s) started with `note: 60`
224
223
 
225
224
  #### Schedule notes
226
225
 
227
- You can schedule notes using `time` and `duration` properties. Both are measured in seconds. Time is the number of seconds since the AudioContext was created, like in `audioContext.currentTime`
226
+ Schedule notes via the `time` and `duration` properties (both in seconds). `time` is measured against `audioContext.currentTime`.
228
227
 
229
- For example, next example plays a C major arpeggio, one note per second:
228
+ This plays a C major arpeggio, one note per second:
230
229
 
231
230
  ```js
232
231
  const now = context.currentTime;
@@ -237,7 +236,7 @@ const now = context.currentTime;
237
236
 
238
237
  #### Looping
239
238
 
240
- 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):
241
240
 
242
241
  ```js
243
242
  const context = new AudioContext();
@@ -316,11 +315,11 @@ To change the mix level, use `output.setEffectMix(name, mix)`:
316
315
  piano.output.setEffectMix("reverb", 0.5);
317
316
  ```
318
317
 
319
- ### Events
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.
320
319
 
321
- Two events are supported `onStart` and `onEnded`. Both callbacks will receive as parameter started note.
320
+ ### Events
322
321
 
323
- Events can be configured globally:
322
+ Two events are available: `onStart` and `onEnded`. Both callbacks receive the started note as a parameter, and can be configured globally:
324
323
 
325
324
  ```js
326
325
  const context = new AudioContext();
@@ -396,7 +395,7 @@ const context = new StandardizedAudioContext() as unknown as AudioContext;
396
395
  const marimba = Soundfont(context, { instrument: "marimba" });
397
396
  ```
398
397
 
399
- In case you need to use the `Reverb` module (or any other module that needs `AudioWorkletNode`) you need to enforce to use the one from `standardized-audio-context` package. Here is how:
398
+ If you use `Reverb` (or anything else that needs `AudioWorkletNode`), force the `standardized-audio-context` version:
400
399
 
401
400
  ```ts
402
401
  import {
@@ -488,13 +487,13 @@ seq.clearTracks(); // remove every track
488
487
 
489
488
  `addTrack`'s third argument accepts:
490
489
 
491
- | Field | Type | Description |
492
- | ---------- | ------------------------------------------- | --------------------------------------------------------------------- |
493
- | `id` | `string` | Stable id for `setTrackVolume` / `muteTrack` / `soloTrack`. |
494
- | `humanize` | `{ timingMs?: number; velocity?: number }` | Per-track humanize. Overrides the sequencer-level setting when set. |
495
- | `volume` | `number` | Multiplicative velocity scalar (default 1). `0.5` halves velocities. |
496
- | `muted` | `boolean` | When true, this track does not dispatch notes. |
497
- | `solo` | `boolean` | When true, only soloed tracks play. |
490
+ | Field | Type | Description |
491
+ | ---------- | ------------------------------------------ | -------------------------------------------------------------------- |
492
+ | `id` | `string` | Stable id for `setTrackVolume` / `muteTrack` / `soloTrack`. |
493
+ | `humanize` | `{ timingMs?: number; velocity?: number }` | Per-track humanize. Overrides the sequencer-level setting when set. |
494
+ | `volume` | `number` | Multiplicative velocity scalar (default 1). `0.5` halves velocities. |
495
+ | `muted` | `boolean` | When true, this track does not dispatch notes. |
496
+ | `solo` | `boolean` | When true, only soloed tracks play. |
498
497
 
499
498
  After `setPatterns` is called (see [Pattern chain](#pattern-chain-song-mode)), `addTrack` / `removeTrack` / `clearTracks` throw — the chain is owned by the patterns array.
500
499
 
@@ -663,22 +662,28 @@ seq.addTrack(piano, notes, { humanize: { timingMs: 0, velocity: 0 } });
663
662
 
664
663
  #### SequencerNote fields
665
664
 
666
- | Field | Type | Description |
667
- | ---------------------- | ------------------- | ---------------------------------------------------------------------------- |
668
- | `note` | `string \| number` | Note name or MIDI number. |
669
- | `at` | `string \| number` | Musical position (ticks or `"bar:beat[.frac][:ticks]"` / `"4n"` / `"1m"`). |
670
- | `duration` | `string \| number?` | Duration; omit for a one-shot trigger. |
671
- | `velocity` | `number?` | Velocity 0–127. Default 100. |
672
- | `id` | `string \| number?` | Used as `noteId` in `noteOn` / `noteOff` events. Default: array index. |
665
+ | Field | Type | Description |
666
+ | ---------------------- | ------------------- | ----------------------------------------------------------------------------- |
667
+ | `note` | `string \| number` | Note name or MIDI number. |
668
+ | `at` | `string \| number` | Musical position (ticks or `"bar:beat[.frac][:ticks]"` / `"4n"` / `"1m"`). |
669
+ | `duration` | `string \| number?` | Duration; omit for a one-shot trigger. |
670
+ | `velocity` | `number?` | Velocity 0–127. Default 100. |
671
+ | `id` | `string \| number?` | Used as `noteId` in `noteOn` / `noteOff` events. Default: array index. |
673
672
  | `chance` | `number?` | Probability 0–100 that this note fires on each pass. Re-rolled on every loop. |
674
- | `ratchet` | `number?` | Expand into N sub-notes over `duration` (requires `duration`). |
675
- | `ratchetVelocityDecay` | `number?` | Per-step velocity decay; each sub-note scaled by `(1 - decay)^i`. |
673
+ | `ratchet` | `number?` | Expand into N sub-notes over `duration` (requires `duration`). |
674
+ | `ratchetVelocityDecay` | `number?` | Per-step velocity decay; each sub-note scaled by `(1 - decay)^i`. |
676
675
 
677
676
  Example:
678
677
 
679
678
  ```js
680
679
  seq.addTrack(drums, [
681
- { note: "hat", at: "1:4", duration: "8n", ratchet: 4, ratchetVelocityDecay: 0.2 },
680
+ {
681
+ note: "hat",
682
+ at: "1:4",
683
+ duration: "8n",
684
+ ratchet: 4,
685
+ ratchetVelocityDecay: 0.2,
686
+ },
682
687
  { note: "snare", at: "1:2", chance: 50 }, // fires 50% of the time
683
688
  ]);
684
689
  ```
@@ -697,7 +702,7 @@ seq.setPatterns([
697
702
  ]);
698
703
 
699
704
  seq.chainOrder = [0, 1, 2, 1, 2]; // intro, verse, chorus, verse, chorus
700
- seq.loop = true; // loop the whole chain
705
+ seq.loop = true; // loop the whole chain
701
706
  seq.start();
702
707
 
703
708
  seq.on("patternChange", (idx) => ui.highlightPattern(idx));
@@ -719,7 +724,8 @@ Render audio offline (faster than real-time) and export it as a WAV file. Uses `
719
724
  import { renderOffline } from "smplr";
720
725
 
721
726
  const result = await renderOffline(async (context) => {
722
- const piano = await SplendidGrandPiano(context).load;
727
+ const piano = SplendidGrandPiano(context);
728
+ await piano.ready;
723
729
  piano.start({ note: "C4", time: 0, duration: 1 });
724
730
  piano.start({ note: "E4", time: 0.5, duration: 1 });
725
731
  });
@@ -762,11 +768,12 @@ import { SplendidGrandPiano, SampleLoader, renderOffline } from "smplr";
762
768
 
763
769
  const loader = SampleLoader(audioContext);
764
770
  const piano = SplendidGrandPiano(audioContext, { loader });
765
- await piano.load;
771
+ await piano.ready;
766
772
 
767
773
  // Offline render reuses cached buffers — no re-fetch
768
774
  const result = await renderOffline(async (context) => {
769
- const offlinePiano = await SplendidGrandPiano(context, { loader }).load;
775
+ const offlinePiano = SplendidGrandPiano(context, { loader });
776
+ await offlinePiano.ready;
770
777
  offlinePiano.start({ note: "C4", time: 0, duration: 1 });
771
778
  });
772
779
  ```
@@ -780,7 +787,8 @@ const { renderOffline, SplendidGrandPiano } =
780
787
  await import("https://esm.sh/smplr");
781
788
 
782
789
  const result = await renderOffline(async (context) => {
783
- const piano = await SplendidGrandPiano(context).load;
790
+ const piano = SplendidGrandPiano(context);
791
+ await piano.ready;
784
792
  piano.start({ note: "C4", time: 0, duration: 2 });
785
793
  });
786
794
  result.downloadWav16("bug-report.wav");
@@ -796,7 +804,7 @@ Detailed configuration for each bundled instrument. For the shared API (load, pl
796
804
 
797
805
  ### Sampler
798
806
 
799
- An audio buffer sampler. Pass a `buffers` object with the files to be load:
807
+ An audio buffer sampler. Pass a `buffers` map of name URL:
800
808
 
801
809
  #### Buffers mode
802
810
 
@@ -816,7 +824,7 @@ And then use the name of the buffer as note name:
816
824
  sampler.start({ note: "kick" });
817
825
  ```
818
826
 
819
- #### Advanced mode
827
+ #### Preset mode
820
828
 
821
829
  For advanced use cases (per-region pitch/velocity/round-robin, SFZ-like multi-sample instruments, runtime swaps), pass a `SmplrPreset` directly:
822
830
 
@@ -843,7 +851,7 @@ sampler.start({ note: 60 });
843
851
  await sampler.reload(kitB);
844
852
  ```
845
853
 
846
- The full `SmplrPreset` schema is documented in [SMPLR_PRESET.md](./SMPLR_PRESET.md). Note: `buffers` and `preset` are mutually exclusive on construction — pass exactly one.
854
+ The full `SmplrPreset` schema is documented in [PRESET_SCHEMA.md](./PRESET_SCHEMA.md). Note: `buffers` and `preset` are mutually exclusive on construction — pass exactly one.
847
855
 
848
856
  `sampler.reload(input)` accepts either shape (flat buffers record or full `SmplrPreset`), regardless of which mode was used at construction.
849
857
 
@@ -1027,15 +1035,15 @@ const context = new AudioContext();
1027
1035
  const drums = DrumAbuse(context, {
1028
1036
  source: { kind: "machine", machine: "roland-tr-808" },
1029
1037
  });
1030
- await drums.load;
1038
+ await drums.ready;
1031
1039
 
1032
1040
  drums.start({ note: "kick" });
1033
1041
 
1034
1042
  // Samples are grouped by instrument name, like DrumMachine:
1035
- drums.getGroupNames(); // => ["kick", "snare", "hi-hat", ...]
1043
+ drums.getGroupNames(); // => ["kick", "snare", "hi-hat", ...]
1036
1044
  drums.getSampleNamesForGroup("kick"); // => ["kick/1", "kick/2", ...]
1037
- drums.start({ note: "kick" }); // first sample in the group
1038
- drums.start({ note: "kick/1" }); // a specific sample
1045
+ drums.start({ note: "kick" }); // first sample in the group
1046
+ drums.start({ note: "kick/1" }); // a specific sample
1039
1047
  ```
1040
1048
 
1041
1049
  If a machine has more than one sample set, pass `set` to pick a specific one. Omit to load the first set.
@@ -1057,8 +1065,8 @@ import {
1057
1065
  getDrumAbuseMachinesForPack,
1058
1066
  } from "smplr";
1059
1067
 
1060
- getDrumAbusePackNames(); // => ["vol1", "vol2", "vol3", "vol4", "vol5"]
1061
- getDrumAbuseMachinesForPack("vol1"); // => machine ids in vol1
1068
+ getDrumAbusePackNames(); // => ["vol1", "vol2", "vol3", "vol4", "vol5"]
1069
+ getDrumAbuseMachinesForPack("vol1"); // => machine ids in vol1
1062
1070
 
1063
1071
  const drums = DrumAbuse(new AudioContext(), {
1064
1072
  source: { kind: "pack", pack: "vol1", instrument: "bass-drum" },
@@ -1074,12 +1082,13 @@ const instruments = getSmolkenNames(); // => Arco, Pizzicato & Switched
1074
1082
 
1075
1083
  // Create an instrument
1076
1084
  const context = new AudioContext();
1077
- const doubleBass = await Smolken(context, { instrument: "Arco" }).load;
1085
+ const doubleBass = Smolken(context, { instrument: "Arco" });
1086
+ await doubleBass.ready;
1078
1087
  ```
1079
1088
 
1080
1089
  ### Versilian
1081
1090
 
1082
- Versilian is a sample capable of using the [Versilian Community Sample Library](https://github.com/sgossner/VCSL).
1091
+ Plays instruments from the [Versilian Community Sample Library](https://github.com/sgossner/VCSL).
1083
1092
 
1084
1093
  ⚠️ Not all features are implemented. Some instruments may sound incorrect ⚠️
1085
1094
 
@@ -1107,7 +1116,7 @@ const sampler = Soundfont2(context, {
1107
1116
  createSoundfont: (data) => new SoundFont2(data),
1108
1117
  });
1109
1118
 
1110
- sampler.load.then(() => {
1119
+ sampler.ready.then(() => {
1111
1120
  // list all available instruments for the soundfont
1112
1121
  console.log(sampler.instrumentNames);
1113
1122
 
@@ -1116,8 +1125,6 @@ sampler.load.then(() => {
1116
1125
  });
1117
1126
  ```
1118
1127
 
1119
- Still limited support. API may vary.
1120
-
1121
1128
  ## Defining your own instrument
1122
1129
 
1123
1130
  If none of the bundled instruments fits your use case, you can author your own with the `Instrument` builder and the `Smplr` interface.
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 ampVelCurve/loopAuto.
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;
@@ -208,68 +215,90 @@ type VoiceParams = {
208
215
  reverse?: boolean;
209
216
  };
210
217
 
218
+ /** Options accepted by `SampleLoader(context, options)`. */
219
+ type SampleLoaderOptions = {
220
+ /** Custom storage backend (e.g. `CacheStorage` for offline). Defaults to `HttpStorage`. */
221
+ storage?: Storage;
222
+ };
223
+ /** Options accepted by `loader.load(json, options)`. */
224
+ type SampleLoaderLoadOptions = {
225
+ /** Pre-decoded buffers keyed by sample name — skip fetch for these. */
226
+ buffers?: Map<string, AudioBuffer>;
227
+ /** Called once per sample (including cache hits) with cumulative progress. */
228
+ onProgress?: (loaded: number, total: number) => void;
229
+ };
230
+ type SampleLoaderFactory = {
231
+ (context: BaseAudioContext, options?: SampleLoaderOptions): SampleLoader;
232
+ /** @deprecated Call as a function: `SampleLoader(...)` instead of `new SampleLoader(...)`. */
233
+ new (context: BaseAudioContext, options?: SampleLoaderOptions): SampleLoader;
234
+ };
211
235
  /**
212
- * Loads and caches AudioBuffers for all samples referenced in a SmplrPreset.
213
- *
214
- * The cache is keyed by resolved URL, so the same audio file is never fetched
215
- * or decoded twice. Multiple Smplr instances can share one SampleLoader by
216
- * passing it via SmplrOptions.loader.
236
+ * Loads and decodes AudioBuffers for the samples referenced by a {@link SmplrPreset}.
237
+ * Used internally by every smplr instrument; pass an instance via
238
+ * {@link SmplrOptions.loader} to share buffer caching across multiple instruments.
217
239
  */
218
- declare class SampleLoaderImpl {
219
- #private;
220
- constructor(context: BaseAudioContext, options?: {
221
- storage?: Storage;
222
- });
240
+ interface SampleLoader {
223
241
  /**
224
- * Load all samples referenced in `json`. Returns a Map of sample name →
225
- * AudioBuffer. Progress is reported via `onProgress` callback or via
226
- * options object.
242
+ * Load all samples referenced by `json`. Returns a Map keyed by sample
243
+ * name (`region.sample`), values are decoded `AudioBuffer`s. Failed
244
+ * samples are silently omitted (callers handle absence at lookup time).
245
+ *
246
+ * Internally cached by resolved URL, so repeated calls with the same
247
+ * baseUrl/format/path do not re-fetch.
227
248
  *
228
- * - `buffers` in options: pre-loaded buffers skips fetch for these names.
229
- * - All samples load in parallel. Failed samples are silently omitted.
249
+ * @param json The preset describing samples to load.
250
+ * @param options
251
+ * - `buffers`: pre-decoded buffers keyed by sample name — skip fetch for these.
252
+ * - `onProgress`: called with `(loaded, total)` per sample (including cache hits).
230
253
  */
231
- load(json: SmplrPreset, onProgressOrOptions?: ((loaded: number, total: number) => void) | {
232
- buffers?: Map<string, AudioBuffer>;
233
- onProgress?: (loaded: number, total: number) => void;
234
- }): Promise<Map<string, AudioBuffer>>;
254
+ load(json: SmplrPreset, options?: SampleLoaderLoadOptions): Promise<Map<string, AudioBuffer>>;
255
+ /**
256
+ * @deprecated Pass `{ onProgress }` instead. The bare-callback form is kept
257
+ * for compatibility; the options form is the canonical 1.x signature.
258
+ */
259
+ load(json: SmplrPreset, onProgress: (loaded: number, total: number) => void): Promise<Map<string, AudioBuffer>>;
235
260
  }
236
- declare const SampleLoader: Constructable<[context: BaseAudioContext, options?: {
237
- storage?: Storage;
238
- } | undefined], SampleLoaderImpl>;
239
- type SampleLoader = ReturnType<typeof SampleLoader>;
261
+ declare const SampleLoader: SampleLoaderFactory;
240
262
 
263
+ /** Options accepted by `Scheduler(context, options)`. */
264
+ type SchedulerOptions = {
265
+ /**
266
+ * How far ahead of `currentTime` events are dispatched synchronously.
267
+ * Defaults to 200ms.
268
+ */
269
+ lookaheadMs?: number;
270
+ /**
271
+ * How often the queue is polled for events ready to dispatch.
272
+ * Defaults to 50ms.
273
+ */
274
+ intervalMs?: number;
275
+ };
276
+ type SchedulerFactory = {
277
+ (context: BaseAudioContext, options?: SchedulerOptions): Scheduler;
278
+ /** @deprecated Call as a function: `Scheduler(...)` instead of `new Scheduler(...)`. */
279
+ new (context: BaseAudioContext, options?: SchedulerOptions): Scheduler;
280
+ };
241
281
  /**
242
- * Standalone scheduler. Dispatches NoteEvents immediately when they fall within the
243
- * lookahead window, or queues them for future dispatch via a self-managing interval.
244
- *
245
- * Multiple Smplr instances can share a single Scheduler for coordinated timing.
282
+ * Schedules note events for future dispatch. Used internally by every smplr
283
+ * instrument; pass an instance via {@link SmplrOptions.scheduler} to share one
284
+ * scheduler across multiple instruments.
246
285
  */
247
- declare class SchedulerImpl {
248
- #private;
249
- constructor(context: BaseAudioContext, options?: {
250
- lookaheadMs?: number;
251
- intervalMs?: number;
252
- });
286
+ interface Scheduler {
253
287
  /**
254
- * Schedule a callback for a NoteEvent.
288
+ * Dispatch `callback` at `event.time`. If `event.time` is within the
289
+ * scheduler's lookahead window (or omitted), the callback fires synchronously
290
+ * and the returned {@link StopFn} is a no-op. Otherwise the event is queued.
255
291
  *
256
- * - If the event's time falls within the lookahead window (or has no time), the
257
- * callback is called synchronously and a no-op StopFn is returned.
258
- * - Otherwise the event is queued, the interval is started if needed, and a StopFn
259
- * is returned that removes the event from the queue before it is dispatched.
292
+ * The returned function removes the event from the queue before dispatch.
260
293
  */
261
294
  schedule(event: NoteEvent, callback: (event: NoteEvent) => void): StopFn;
262
295
  /**
263
- * Clear all queued (not-yet-dispatched) events and stop the interval.
264
- * Does not affect voices that are already playing.
296
+ * Clear all queued (not-yet-dispatched) events and stop the polling
297
+ * interval. Does not affect voices already playing.
265
298
  */
266
299
  stop(): void;
267
300
  }
268
- declare const Scheduler: Constructable<[context: BaseAudioContext, options?: {
269
- lookaheadMs?: number;
270
- intervalMs?: number;
271
- } | undefined], SchedulerImpl>;
272
- type Scheduler = ReturnType<typeof Scheduler>;
301
+ declare const Scheduler: SchedulerFactory;
273
302
 
274
303
  type SmplrOptions = {
275
304
  /** Custom storage backend for sample fetching (e.g. CacheStorage). */
@@ -1208,7 +1237,7 @@ declare function sf2InstrumentToPreset(sf2Instrument: Sf2Instrument, context: Ba
1208
1237
  };
1209
1238
  type Soundfont2SamplerExtras = {
1210
1239
  readonly instrumentNames: string[];
1211
- loadInstrument(instrumentName: string): Promise<void> | undefined;
1240
+ loadInstrument(instrumentName: string): Promise<void>;
1212
1241
  };
1213
1242
  declare const Soundfont2: InstrumentFactory<Soundfont2Options, Soundfont2SamplerExtras>;
1214
1243
  /** Instance type returned by the {@link Soundfont2} factory. */
@@ -1273,4 +1302,4 @@ declare const LAYERS: ({
1273
1302
  cutoff?: undefined;
1274
1303
  })[];
1275
1304
 
1276
- export { type AddTrackOptions, CacheStorage, DRUM_ABUSE_PACKS, DrumAbuse, type DrumAbuseConfig, type DrumAbuseExtras, type DrumAbuseOptions, type DrumAbusePackId, type DrumAbuseSource, DrumMachine, type DrumMachineOptions, ElectricPiano, type ElectricPianoOptions, HttpStorage, Instrument, LAYERS, type LoadProgress, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, type NoteEvent, type PatternInput, type PlaybackParams, type RenderOfflineOptions, RenderResult, Reverb, SampleLoader, Sampler, type SamplerConfig, type SamplerReloadInput, Scheduler, Sequencer, type SequencerInstrument, type SequencerNote, type SequencerNoteEvent, type SequencerOptions, Smolken, type SmolkenConfig, type SmolkenOptions, type Smplr, type SmplrGroup, type SmplrOptions, type SmplrPlugin, type SmplrPreset, type SmplrRegion, type SmplrSamples, Soundfont, Soundfont2, type Soundfont2Options, Soundfont2Sampler, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type StopFn, type StopTarget, type Storage, type StorageResponse, type TimeSignature, Versilian, type VersilianConfig, type VersilianOptions, type VoiceParams, audioBufferToWav, audioBufferToWav16, drumAbuseSampleUrl, drumMachineToPreset, getDrumAbuseMachineNames, getDrumAbuseMachinePack, getDrumAbuseMachinesForPack, getDrumAbusePackNames, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments, loadVersilianInstrument, mellotronToPreset, pianoToPreset, renderOffline, samplerToPreset, sf2InstrumentToPreset, soundfontToPreset, trimSilence };
1305
+ export { type AddTrackOptions, CacheStorage, DRUM_ABUSE_PACKS, DrumAbuse, type DrumAbuseConfig, type DrumAbuseExtras, type DrumAbuseOptions, type DrumAbusePackId, type DrumAbuseSource, DrumMachine, type DrumMachineOptions, ElectricPiano, type ElectricPianoOptions, HttpStorage, Instrument, LAYERS, type LoadProgress, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, type NoteEvent, type PatternInput, type PlaybackParams, type RenderOfflineOptions, RenderResult, Reverb, SampleLoader, type SampleLoaderLoadOptions, type SampleLoaderOptions, Sampler, type SamplerConfig, type SamplerReloadInput, Scheduler, type SchedulerOptions, Sequencer, type SequencerInstrument, type SequencerNote, type SequencerNoteEvent, type SequencerOptions, Smolken, type SmolkenConfig, type SmolkenOptions, type Smplr, type SmplrGroup, type SmplrOptions, type SmplrPlugin, type SmplrPreset, type SmplrRegion, type SmplrSamples, Soundfont, Soundfont2, type Soundfont2Options, Soundfont2Sampler, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type StopFn, type StopTarget, type Storage, type StorageResponse, type TimeSignature, Versilian, type VersilianConfig, type VersilianOptions, type VoiceParams, audioBufferToWav, audioBufferToWav16, drumAbuseSampleUrl, drumMachineToPreset, getDrumAbuseMachineNames, getDrumAbuseMachinePack, getDrumAbuseMachinesForPack, getDrumAbusePackNames, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments, loadVersilianInstrument, mellotronToPreset, pianoToPreset, renderOffline, samplerToPreset, sf2InstrumentToPreset, soundfontToPreset, trimSilence };