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/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 });
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
199
|
+
`velocity` (0–127) 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
|
-
|
|
226
|
+
Schedule notes via the `time` and `duration` properties (both in seconds). `time` is measured against `audioContext.currentTime`.
|
|
228
227
|
|
|
229
|
-
|
|
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
|
-
|
|
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
|
-
|
|
320
|
+
### Events
|
|
322
321
|
|
|
323
|
-
|
|
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
|
-
|
|
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
|
|
492
|
-
| ---------- |
|
|
493
|
-
| `id` | `string`
|
|
494
|
-
| `humanize` | `{ timingMs?: number; velocity?: number }`
|
|
495
|
-
| `volume` | `number`
|
|
496
|
-
| `muted` | `boolean`
|
|
497
|
-
| `solo` | `boolean`
|
|
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
|
-
{
|
|
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;
|
|
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 =
|
|
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.
|
|
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 =
|
|
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 =
|
|
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`
|
|
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
|
-
####
|
|
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 [
|
|
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.
|
|
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();
|
|
1043
|
+
drums.getGroupNames(); // => ["kick", "snare", "hi-hat", ...]
|
|
1036
1044
|
drums.getSampleNamesForGroup("kick"); // => ["kick/1", "kick/2", ...]
|
|
1037
|
-
drums.start({ note: "kick" });
|
|
1038
|
-
drums.start({ note: "kick/1" });
|
|
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();
|
|
1061
|
-
getDrumAbuseMachinesForPack("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 =
|
|
1085
|
+
const doubleBass = Smolken(context, { instrument: "Arco" });
|
|
1086
|
+
await doubleBass.ready;
|
|
1078
1087
|
```
|
|
1079
1088
|
|
|
1080
1089
|
### Versilian
|
|
1081
1090
|
|
|
1082
|
-
|
|
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.
|
|
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
|
|
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
|
|
213
|
-
*
|
|
214
|
-
*
|
|
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
|
-
|
|
219
|
-
#private;
|
|
220
|
-
constructor(context: BaseAudioContext, options?: {
|
|
221
|
-
storage?: Storage;
|
|
222
|
-
});
|
|
240
|
+
interface SampleLoader {
|
|
223
241
|
/**
|
|
224
|
-
* Load all samples referenced
|
|
225
|
-
*
|
|
226
|
-
*
|
|
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
|
-
*
|
|
229
|
-
*
|
|
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,
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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:
|
|
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
|
-
*
|
|
243
|
-
*
|
|
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
|
-
|
|
248
|
-
#private;
|
|
249
|
-
constructor(context: BaseAudioContext, options?: {
|
|
250
|
-
lookaheadMs?: number;
|
|
251
|
-
intervalMs?: number;
|
|
252
|
-
});
|
|
286
|
+
interface Scheduler {
|
|
253
287
|
/**
|
|
254
|
-
*
|
|
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
|
-
*
|
|
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
|
|
264
|
-
* Does not affect voices
|
|
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:
|
|
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
|
|
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 };
|