smplr 0.20.0 → 0.22.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
@@ -6,37 +6,60 @@
6
6
 
7
7
  Examples:
8
8
 
9
+ **Play a note from a General MIDI soundfont:**
10
+
9
11
  ```js
10
12
  import { Soundfont } from "smplr";
11
13
 
12
14
  const context = new AudioContext();
13
- const marimba = new Soundfont(context, { instrument: "marimba" });
15
+ const marimba = Soundfont(context, { instrument: "marimba" });
14
16
  marimba.start({ note: 60, velocity: 80 });
15
17
  ```
16
18
 
19
+ **Sequence a beat with a drum machine and a piano on the same clock:**
20
+
17
21
  ```js
18
- import { DrumMachine } from "smplr";
22
+ import { Sequencer, SplendidGrandPiano, DrumMachine } from "smplr";
19
23
 
20
24
  const context = new AudioContext();
21
- const dm = new DrumMachine(context);
22
- dm.start({ note: "kick" });
25
+ const piano = SplendidGrandPiano(context);
26
+ const drums = DrumMachine(context, { instrument: "TR-808" });
27
+
28
+ const seq = new Sequencer(context, { bpm: 110, loop: true });
29
+ seq.addTrack(piano, [
30
+ { note: "C4", at: "1:1", duration: "4n" },
31
+ { note: "E4", at: "1:2", duration: "4n" },
32
+ { note: "G4", at: "1:3", duration: "4n" },
33
+ ]);
34
+ seq.addTrack(drums, [
35
+ { note: "kick", at: "1:1" },
36
+ { note: "snare", at: "1:2" },
37
+ { note: "kick", at: "1:3" },
38
+ { note: "snare", at: "1:4" },
39
+ ]);
40
+ seq.start();
23
41
  ```
24
42
 
25
- ```js
26
- import { SplendidGrandPiano, Reverb } from "smplr";
43
+ **Render an arpeggio with reverb to a WAV file — offline, no speakers needed:**
27
44
 
28
- const context = new AudioContext();
29
- const piano = new SplendidGrandPiano(context);
30
- piano.output.addEffect("reverb", new Reverb(context), 0.2);
45
+ ```js
46
+ import { SplendidGrandPiano, Reverb, renderOffline } from "smplr";
31
47
 
32
- piano.start({ note: "C4" });
48
+ const wav = await renderOffline(async (context) => {
49
+ const piano = await SplendidGrandPiano(context).load;
50
+ piano.output.addEffect("reverb", new Reverb(context), 0.3);
51
+ ["C4", "E4", "G4", "C5"].forEach((note, i) => {
52
+ piano.start({ note, time: i * 0.4, duration: 0.4 });
53
+ });
54
+ });
55
+ wav.downloadWav("arpeggio.wav");
33
56
  ```
34
57
 
35
58
  See demo: https://danigb.github.io/smplr/
36
59
 
37
- `smplr` is still under development and features are considered unstable until v 1.0
60
+ `smplr` is approaching 1.0. The 0.22.0 release lands the final batch of pre-1.0 API work — every documented `new X(ctx, opts)` keeps working, and the documented surface is intended to ship unchanged into 1.0. The formal stability commitment lands once the narrow `loader`/`scheduler` public interfaces sibling ticket is in (see [CHANGELOG](https://github.com/danigb/smplr/blob/main/CHANGELOG.md)).
38
61
 
39
- Read [CHANGELOG](https://github.com/danigb/smplr/blob/main/CHANGELOG.md) for changes.
62
+ > **Upgrading from an earlier 0.x?** No code changes are required — every documented `new X(ctx, opts)` keeps working. New code should drop the `new` (`X(ctx, opts)`) and prefer `await x.ready` over `await x.load`.
40
63
 
41
64
  #### Library goals
42
65
 
@@ -50,7 +73,7 @@ You can install the library with a package manager or use it directly by importi
50
73
 
51
74
  Samples are stored at https://github.com/smpldsnds and there is no need to download them. Kudos to all _samplerist_ 🙌
52
75
 
53
- #### Using a package manger
76
+ #### Using a package manager
54
77
 
55
78
  Use npm or your favourite package manager to install the library to use it in your project:
56
79
 
@@ -70,32 +93,42 @@ You can import directly from the browser. For example:
70
93
  <script type="module">
71
94
  import { SplendidGrandPiano } from "https://unpkg.com/smplr/dist/index.mjs"; // needs to be a url
72
95
  const context = new AudioContext(); // create the audio context
73
- const marimba = new SplendidGrandPiano(context); // create and load the instrument
96
+ const piano = SplendidGrandPiano(context); // create and load the instrument
74
97
 
75
98
  document.getElementById("btn").onclick = () => {
76
99
  context.resume(); // enable audio context after a user interaction
77
- marimba.start({ note: 60, velocity: 80 }); // play the note
100
+ piano.start({ note: 60, velocity: 80 }); // play the note
78
101
  };
79
102
  </script>
80
103
  </html>
81
104
  ```
82
105
 
83
- The package needs to be serve as a url from a service like [unpkg](unpkg.com) or similar.
106
+ The package needs to be served as a URL from a service like [unpkg](https://unpkg.com) or similar.
107
+
108
+ > To author your own instrument or publish a third-party package, see the [Defining an instrument](./AUTHORING.md) guide.
84
109
 
85
110
  ## Documentation
86
111
 
112
+ ### Defining an instrument
113
+
114
+ `smplr` ships ten instruments out of the box — `SplendidGrandPiano`, `Soundfont`, `DrumMachine`, `ElectricPiano`, `Mallet`, `Mellotron`, `Smolken`, `Versilian`, `Sampler`, `Soundfont2Sampler`. If none of them fit your use case, you can author your own with the `Instrument` builder and the `Smplr` interface.
115
+
116
+ See **[Defining an instrument](./AUTHORING.md)** for the full authoring guide — sync and async examples, third-party package layout, and how to use `Smplr` as a TypeScript type for generic helpers.
117
+
87
118
  ### Create and load an instrument
88
119
 
89
- All instruments follows the same pattern: `new Instrument(context, options)`. For example:
120
+ Every smplr instrument is a factory function: call it with an `AudioContext` and an options object to get back an instance.
90
121
 
91
122
  ```js
92
123
  import { SplendidGrandPiano, Soundfont } from "smplr";
93
124
 
94
125
  const context = new AudioContext();
95
- const piano = new SplendidGrandPiano(context, { decayTime: 0.5 });
96
- const marimba = new Soundfont(context, { instrument: "marimba" });
126
+ const piano = SplendidGrandPiano(context, { decayTime: 0.5 });
127
+ const marimba = Soundfont(context, { instrument: "marimba" });
97
128
  ```
98
129
 
130
+ > **Compatibility note:** All factories also support the `new` keyword — `new SplendidGrandPiano(context)` produces the same instance as `SplendidGrandPiano(context)`. Code from earlier `smplr` versions keeps working unchanged. Editors will mark the `new` form as `@deprecated` to nudge new code toward the call form; both remain supported throughout the 1.x line.
131
+
99
132
  #### Wait for audio loading
100
133
 
101
134
  You can start playing notes as soon as one audio is loaded. But if you want to wait for all of them, you can use the `load` property that returns a promise:
@@ -109,9 +142,13 @@ piano.load.then(() => {
109
142
  Since the promise returns the instrument instance, you can create and wait in a single line:
110
143
 
111
144
  ```js
112
- const piano = await new SplendidGrandPiano(context).load;
145
+ const piano = await SplendidGrandPiano(context).load;
113
146
  ```
114
147
 
148
+ The pre-1.0 `new`-prefixed form continues to work — `const piano = await new SplendidGrandPiano(context).load` resolves to the same instrument. This is the documented backward-compat path for code from earlier `smplr` versions.
149
+
150
+ > **New in 1.0:** prefer `await piano.ready` for new code. It resolves to `void` (not the instrument) and won't be removed — `.load` is kept as a deprecated alias for compatibility.
151
+
115
152
  ⚠️ In versions lower than 0.8.0 a `loaded()` function was exposed instead.
116
153
 
117
154
  #### Load progress
@@ -119,7 +156,7 @@ const piano = await new SplendidGrandPiano(context).load;
119
156
  Track how many samples have loaded via the `onLoadProgress` option or the `loadProgress` getter:
120
157
 
121
158
  ```js
122
- const piano = new SplendidGrandPiano(context, {
159
+ const piano = SplendidGrandPiano(context, {
123
160
  onLoadProgress: ({ loaded, total }) => {
124
161
  console.log(`${loaded} / ${total} samples loaded`);
125
162
  },
@@ -133,17 +170,19 @@ console.log(piano.loadProgress); // { loaded: 12, total: 48 }
133
170
 
134
171
  #### Shared configuration options
135
172
 
136
- All instruments share some configuration options that are passed as second argument of the constructor. As it name implies, all fields are optional:
173
+ All instruments share some configuration options, passed as the second argument to the factory. Every field is optional:
137
174
 
138
- - `volume`: A number from 0 to 127 representing the instrument global volume. 100 by default
139
- - `destination`: An `AudioNode` that is the output of the instrument. `AudioContext.destination` is used by default
140
- - `volumeToGain`: a function to convert the volume to gain. It uses MIDI standard as default.
141
- - `disableScheduler`: disable internal scheduler. `false` by default.
142
- - `scheduleLookaheadMs`: the lookahead of the scheduler. If the start time of the note is less than current time plus this lookahead time, the note will be started. 200ms by default.
143
- - `scheduleIntervalMs`: the interval of the scheduler. 50ms by default.
175
+ - `volume`: a number from 0 to 127 representing the instrument's global volume. 100 by default.
176
+ - `velocity`: default note velocity (0–127) when not specified per note. 100 by default.
177
+ - `pan`: stereo pan, -1 (full left) to +1 (full right). 0 by default.
178
+ - `destination`: the `AudioNode` the instrument writes to. `AudioContext.destination` by default.
179
+ - `volumeToGain`: a function to map MIDI volume to a linear gain. Uses the MIDI standard curve by default.
180
+ - `storage`: a [storage backend](#cache-requests) used to fetch sample buffers. `HttpStorage` by default.
181
+ - `loader`: a shared `SampleLoader` instance. Pass the same loader to multiple instruments to cache buffers across them (see [Buffer reuse](#buffer-reuse)).
182
+ - `scheduler`: a shared `Scheduler` instance. Construct your own to tune scheduling — for example, `new Scheduler(context, { lookaheadMs: 100, intervalMs: 25 })` — or omit to get a per-instrument default.
144
183
  - `onLoadProgress`: a function called after each sample buffer is decoded. Receives `{ loaded, total }` where `total` is the full count known before loading starts.
145
- - `onStart`: a function that is called when starting a note. It receives the note started as parameter. Bear in mind that the time this function is called is not precise, and it's determined by lookahead.
146
- - `onEnded`: a function that is called when the note ends. It receives the started note as parameter.
184
+ - `onStart`: called when a note is dispatched to the audio engine. Receives the started note. See ⚠️ note under [Events](#events) on timing precision.
185
+ - `onEnded`: called when each voice's audio node ends. Receives the started note.
147
186
 
148
187
  #### Usage with standardized-audio-context
149
188
 
@@ -153,7 +192,7 @@ This package should be compatible with [standardized-audio-context](https://gith
153
192
  import { AudioContext } from "standardized-audio-context";
154
193
 
155
194
  const context = new AudioContext();
156
- const piano = new SplendidGrandPiano(context);
195
+ const piano = SplendidGrandPiano(context);
157
196
  ```
158
197
 
159
198
  However, if you are using Typescript, you might need to "force cast" the types:
@@ -163,7 +202,7 @@ import { Soundfont } from "smplr";
163
202
  import { AudioContext as StandardizedAudioContext } from "standardized-audio-context";
164
203
 
165
204
  const context = new StandardizedAudioContext() as unknown as AudioContext;
166
- const marimba = new Soundfont(context, { instrument: "marimba" });
205
+ const marimba = Soundfont(context, { instrument: "marimba" });
167
206
  ```
168
207
 
169
208
  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:
@@ -176,12 +215,12 @@ import {
176
215
  } from "standardized-audio-context";
177
216
 
178
217
  window.AudioWorkletNode = AudioWorkletNode as any;
179
- const context = new StandardizedAudioContext() as unknown AudioContext;
218
+ const context = new StandardizedAudioContext() as unknown as AudioContext;
180
219
 
181
220
  // ... rest of the code
182
221
  ```
183
222
 
184
- You can read more about this issue [here](https://github.com/chrisguttandin/standardized-audio-context/issues/897)
223
+ See [standardized-audio-context issue #897](https://github.com/chrisguttandin/standardized-audio-context/issues/897) for background on why the cast is required.
185
224
 
186
225
  ### Play
187
226
 
@@ -211,11 +250,11 @@ Instruments have a global `stop` function that can be used to stop all notes:
211
250
  piano.stop();
212
251
  ```
213
252
 
214
- Or stop the specified one:
253
+ Or stop the specified one. The argument is a `stopId` — by default the same value you passed as `note`, but you can override it via `start({ note, stopId })`:
215
254
 
216
255
  ```js
217
- // This will stop C4 note
218
- piano.stop(60);
256
+ piano.stop("C4"); // stop the note(s) started with `note: "C4"`
257
+ piano.stop(60); // stop the note(s) started with `note: 60`
219
258
  ```
220
259
 
221
260
  #### Schedule notes
@@ -236,10 +275,13 @@ const now = context.currentTime;
236
275
  You can loop a note by using `loop`, `loopStart` and `loopEnd`:
237
276
 
238
277
  ```js
239
- const sampler = new Sampler(audioContext, { duh: "duh-duh-ah.mp3" });
278
+ const context = new AudioContext();
279
+ const sampler = Sampler(context, {
280
+ buffers: { duh: "https://example.com/duh-duh-ah.mp3" },
281
+ });
240
282
  sampler.start({
241
- note: "duh"
242
- loop: true
283
+ note: "duh",
284
+ loop: true,
243
285
  loopStart: 1.0,
244
286
  loopEnd: 9.0,
245
287
  });
@@ -249,14 +291,42 @@ If `loop` is true but `loopStart` or `loopEnd` are not specified, 0 and total du
249
291
 
250
292
  #### Change volume
251
293
 
252
- Instrument `output` attribute represents the main output of the instrument. `output.setVolume` method accepts a number where 0 means no volume, and 127 is max volume without amplification:
294
+ Instrument `output` attribute represents the main output of the instrument. The `output.volume` getter/setter accepts a number where 0 means no volume, and 127 is max volume without amplification:
253
295
 
254
296
  ```js
255
- piano.output.setVolume(80);
297
+ piano.output.volume = 80;
298
+ piano.output.volume; // => 80
256
299
  ```
257
300
 
301
+ `output.setVolume(n)` is kept as a deprecated alias and continues to work.
302
+
258
303
  ⚠️ `volume` is global to the instrument, but `velocity` is specific for each note.
259
304
 
305
+ #### MIDI CC
306
+
307
+ Set and read MIDI Control Change values on the instrument:
308
+
309
+ ```js
310
+ piano.setCC(64, 127); // sustain pedal on
311
+ piano.getCC(64); // => 127
312
+ piano.setCC(64, 0); // sustain pedal off
313
+ ```
314
+
315
+ Unset CCs default to `0` (matches MIDI's "undefined controller defaults to 0" convention).
316
+
317
+ #### Disposing
318
+
319
+ When you're done with an instrument, call `dispose()` to stop all voices, tear down the audio graph, and stop the scheduler. The instance must not be used after this call.
320
+
321
+ ```js
322
+ useEffect(() => {
323
+ const piano = SplendidGrandPiano(context);
324
+ return () => piano.dispose();
325
+ }, []);
326
+ ```
327
+
328
+ `disconnect()` is kept as a deprecated alias and continues to work.
329
+
260
330
  #### Events
261
331
 
262
332
  Two events are supported `onStart` and `onEnded`. Both callbacks will receive as parameter started note.
@@ -265,7 +335,7 @@ Events can be configured globally:
265
335
 
266
336
  ```js
267
337
  const context = new AudioContext();
268
- const sampler = new Sample(context, {
338
+ const piano = SplendidGrandPiano(context, {
269
339
  onStart: (note) => {
270
340
  console.log(note.time, context.currentTime);
271
341
  },
@@ -286,36 +356,36 @@ piano.start({
286
356
 
287
357
  Global callbacks will be invoked regardless of whether local events are defined.
288
358
 
289
- ⚠️ The invocation time of `onStart` is not exact. It triggers slightly before the actual start time and is influenced by the `scheduleLookaheadMs` parameter.
359
+ ⚠️ The invocation time of `onStart` is not exact: it fires slightly before the audio actually starts, by up to the scheduler's lookahead window (200ms by default; configurable via the `scheduler` option — see [Shared configuration options](#shared-configuration-options)).
290
360
 
291
361
  ### Effects
292
362
 
293
363
  #### Reverb
294
364
 
295
- An packed version of [DattorroReverbNode](https://github.com/khoin/DattorroReverbNode) algorithmic reverb is included.
365
+ A packaged version of the [DattorroReverbNode](https://github.com/khoin/DattorroReverbNode) algorithmic reverb is included.
296
366
 
297
367
  Use `output.addEffect(name, effect, mix)` to connect an effect using a send bus:
298
368
 
299
369
  ```js
300
370
  import { Reverb, SplendidGrandPiano } from "smplr";
301
371
  const reverb = new Reverb(context);
302
- const piano = new SplendidGrandPiano(context, { volume });
372
+ const piano = SplendidGrandPiano(context, { volume });
303
373
  piano.output.addEffect("reverb", reverb, 0.2);
304
374
  ```
305
375
 
306
- To change the mix level, use `output.sendEffect(name, mix)`:
376
+ To change the mix level, use `output.setEffectMix(name, mix)`:
307
377
 
308
378
  ```js
309
- piano.output.sendEffect("reverb", 0.5);
379
+ piano.output.setEffectMix("reverb", 0.5);
310
380
  ```
311
381
 
312
- ### Experimental features
382
+ `output.sendEffect(name, mix)` is kept as a deprecated alias and continues to work.
313
383
 
314
- #### Cache requests
384
+ ### Cache requests
315
385
 
316
- If you use default samples, they are stored at github pages. Github rate limits the number of requests per second. That could be a problem, specially if you're using a development environment with hot reload (like most React frameworks).
386
+ The default sample sets are hosted on GitHub Pages, which rate-limits requests per second. That can be a problem, especially in a development environment with hot reload (most React frameworks).
317
387
 
318
- If you want to cache samples on the browser you can use a `CacheStorage` object:
388
+ To cache samples in the browser, use a `CacheStorage` object:
319
389
 
320
390
  ```ts
321
391
  import { SplendidGrandPiano, CacheStorage } from "smplr";
@@ -323,21 +393,21 @@ import { SplendidGrandPiano, CacheStorage } from "smplr";
323
393
  const context = new AudioContext();
324
394
  const storage = new CacheStorage();
325
395
  // First time the instrument loads, will fetch the samples from http. Subsequent times from cache.
326
- const piano = new SplendidGrandPiano(context, { storage });
396
+ const piano = SplendidGrandPiano(context, { storage });
327
397
  ```
328
398
 
329
- ⚠️ `CacheStorage` is based on [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) and only works in secure environments that runs with `https`. Read your framework documentation for setup instructions. For example, in nextjs you can use https://www.npmjs.com/package/next-dev-https. For vite there's https://github.com/liuweiGL/vite-plugin-mkcert. Find the appropriate solution for your environment.
399
+ ⚠️ `CacheStorage` is based on the [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) and only works in secure environments that run over `https`. Check your framework's documentation for local-HTTPS setup for example [next-dev-https](https://www.npmjs.com/package/next-dev-https) for Next.js or [vite-plugin-mkcert](https://github.com/liuweiGL/vite-plugin-mkcert) for Vite.
330
400
 
331
401
  ## Sequencer
332
402
 
333
- `Sequencer` schedules notes from one or more tracks against any smplr instrument with sample-accurate timing.
403
+ `Sequencer` schedules notes from one or more tracks against any smplr instrument with sample-accurate timing. Unlike instruments, it's a regular class — always constructed with `new Sequencer(context, opts)`.
334
404
 
335
405
  ```js
336
406
  import { Sequencer, SplendidGrandPiano, DrumMachine } from "smplr";
337
407
 
338
408
  const context = new AudioContext();
339
- const piano = new SplendidGrandPiano(context);
340
- const drums = new DrumMachine(context, { instrument: "TR-808" });
409
+ const piano = SplendidGrandPiano(context);
410
+ const drums = DrumMachine(context, { instrument: "TR-808" });
341
411
 
342
412
  const seq = new Sequencer(context, { bpm: 120, loop: true });
343
413
 
@@ -530,7 +600,7 @@ Render audio offline (faster than real-time) and export it as a WAV file. Uses `
530
600
  import { renderOffline } from "smplr";
531
601
 
532
602
  const result = await renderOffline(async (context) => {
533
- const piano = await new SplendidGrandPiano(context).load;
603
+ const piano = await SplendidGrandPiano(context).load;
534
604
  piano.start({ note: "C4", time: 0, duration: 1 });
535
605
  piano.start({ note: "E4", time: 0.5, duration: 1 });
536
606
  });
@@ -572,12 +642,12 @@ If you already have an instrument loaded, pass the same `SampleLoader` to avoid
572
642
  import { SplendidGrandPiano, SampleLoader, renderOffline } from "smplr";
573
643
 
574
644
  const loader = new SampleLoader(audioContext);
575
- const piano = new SplendidGrandPiano(audioContext, { loader });
645
+ const piano = SplendidGrandPiano(audioContext, { loader });
576
646
  await piano.load;
577
647
 
578
648
  // Offline render reuses cached buffers — no re-fetch
579
649
  const result = await renderOffline(async (context) => {
580
- const offlinePiano = await new SplendidGrandPiano(context, { loader }).load;
650
+ const offlinePiano = await SplendidGrandPiano(context, { loader }).load;
581
651
  offlinePiano.start({ note: "C4", time: 0, duration: 1 });
582
652
  });
583
653
  ```
@@ -587,10 +657,11 @@ const result = await renderOffline(async (context) => {
587
657
  Use offline rendering to generate reproducible audio files for issue reports. No install needed — just open your browser's DevTools console on any page and paste:
588
658
 
589
659
  ```js
590
- const { renderOffline, SplendidGrandPiano } = await import("https://esm.sh/smplr");
660
+ const { renderOffline, SplendidGrandPiano } =
661
+ await import("https://esm.sh/smplr");
591
662
 
592
663
  const result = await renderOffline(async (context) => {
593
- const piano = await new SplendidGrandPiano(context).load;
664
+ const piano = await SplendidGrandPiano(context).load;
594
665
  piano.start({ note: "C4", time: 0, duration: 2 });
595
666
  });
596
667
  result.downloadWav16("bug-report.wav");
@@ -602,6 +673,22 @@ This will download a WAV file you can attach to your issue or pull request.
602
673
 
603
674
  ## Instruments
604
675
 
676
+ ### Available instruments
677
+
678
+ Each instrument family exposes a synchronous helper that returns the names you can pass to its factory:
679
+
680
+ | Factory | Names helper |
681
+ |---|---|
682
+ | `Soundfont` | `getSoundfontNames(): string[]` |
683
+ | `ElectricPiano` | `getElectricPianoNames(): string[]` |
684
+ | `Mallet` | `getMalletNames(): string[]` |
685
+ | `Mellotron` | `getMellotronNames(): string[]` |
686
+ | `DrumMachine` | `getDrumMachineNames(): string[]` |
687
+ | `Smolken` | `getSmolkenNames(): string[]` |
688
+ | `Versilian` | `getVersilianInstruments(): Promise<string[]>` |
689
+
690
+ `getVersilianInstruments` is async because the catalog is fetched from the network on first call (cached thereafter).
691
+
605
692
  ### Sampler
606
693
 
607
694
  An audio buffer sampler. Pass a `buffers` object with the files to be load:
@@ -613,7 +700,7 @@ const buffers = {
613
700
  kick: "https://smpldsnds.github.io/drum-machines/808-mini/kick.m4a",
614
701
  snare: "https://smpldsnds.github.io/drum-machines/808-mini/snare-1.m4a",
615
702
  };
616
- const sampler = new Sampler(new AudioContext(), { buffers });
703
+ const sampler = Sampler(new AudioContext(), { buffers });
617
704
  ```
618
705
 
619
706
  And then use the name of the buffer as note name:
@@ -630,7 +717,7 @@ A Soundfont player. By default it loads audio from Benjamin Gleitzman's package
630
717
  ```js
631
718
  import { Soundfont, getSoundfontNames, getSoundfontKits } from "smplr";
632
719
 
633
- const marimba = new Soundfont(new AudioContext(), { instrument: "marimba" });
720
+ const marimba = Soundfont(new AudioContext(), { instrument: "marimba" });
634
721
  marimba.start({ note: "C4" });
635
722
  ```
636
723
 
@@ -640,10 +727,10 @@ It's intended to be a modern replacement of [soundfont-player](https://github.co
640
727
 
641
728
  Use `getSoundfontNames` to get all available instrument names and `getSoundfontKits` to get kit names.
642
729
 
643
- There are two kits available: `MusyngKite` or `FluidR3_GM`. The first one is used by default: it sounds better but samples weights more.
730
+ There are two kits available: `MusyngKite` or `FluidR3_GM`. The first one is used by default: it sounds better but the samples are heavier.
644
731
 
645
732
  ```js
646
- const marimba = new Soundfont(context, {
733
+ const marimba = Soundfont(context, {
647
734
  instrument: "clavinet",
648
735
  kit: "FluidR3_GM", // "MusyngKite" is used by default if not specified
649
736
  });
@@ -652,7 +739,7 @@ const marimba = new Soundfont(context, {
652
739
  Alternatively, you can pass your custom url as the instrument. In that case, the `kit` is ignored:
653
740
 
654
741
  ```js
655
- const marimba = new Soundfont(context, {
742
+ const marimba = Soundfont(context, {
656
743
  instrumentUrl:
657
744
  "https://gleitz.github.io/midi-js-soundfonts/MusyngKite/marimba-mp3.js",
658
745
  });
@@ -663,7 +750,7 @@ const marimba = new Soundfont(context, {
663
750
  You can enable note looping to make note names indefinitely long by loading loop data:
664
751
 
665
752
  ```js
666
- const marimba = new Soundfont(context, {
753
+ const marimba = Soundfont(context, {
667
754
  instrument: "cello",
668
755
  loadLoopData: true,
669
756
  });
@@ -679,7 +766,7 @@ A sampled acoustic piano. It uses Steinway samples with 4 velocity groups from
679
766
  ```js
680
767
  import { SplendidGrandPiano } from "smplr";
681
768
 
682
- const piano = new SplendidGrandPiano(new AudioContext());
769
+ const piano = SplendidGrandPiano(new AudioContext());
683
770
 
684
771
  piano.start({ note: "C4" });
685
772
  ```
@@ -688,7 +775,7 @@ piano.start({ note: "C4" });
688
775
 
689
776
  The second argument of the constructor accepts the following options:
690
777
 
691
- - `baseUrl`:
778
+ - `baseUrl`: where the piano samples are fetched from. Defaults to the public hosted set on `smpldsnds.github.io`; override only if you mirror the samples yourself.
692
779
  - `detune`: global detune in cents (0 if not specified)
693
780
  - `velocity`: default velocity (100 if not specified)
694
781
  - `volume`: default volume (100 if not specified)
@@ -698,7 +785,7 @@ The second argument of the constructor accepts the following options:
698
785
  Example:
699
786
 
700
787
  ```ts
701
- const piano = new SplendidGrandPiano(context, {
788
+ const piano = SplendidGrandPiano(context, {
702
789
  detune: -20,
703
790
  volume: 80,
704
791
  notesToLoad: {
@@ -715,9 +802,9 @@ A sampled electric pianos. Samples from https://github.com/sfzinstruments/GregSu
715
802
  ```js
716
803
  import { ElectricPiano, getElectricPianoNames } from "smplr";
717
804
 
718
- const instruments = getElectricPianoNames(); // => ["CP80", "PianetT", "WurlitzerEP200"]
805
+ const instruments = getElectricPianoNames(); // => ["CP80", "PianetT", "WurlitzerEP200", "TX81Z"]
719
806
 
720
- const epiano = new ElectricPiano(new AudioContext(), {
807
+ const epiano = ElectricPiano(new AudioContext(), {
721
808
  instrument: "PianetT",
722
809
  });
723
810
 
@@ -732,6 +819,7 @@ Available instruments:
732
819
  - `CP80`: Yamaha CP80 Electric Grand Piano v1.3 (29-Sep-2004)
733
820
  - `PianetT`: Hohner Pianet T (type 2) v1.3 (24-Sep-2004)
734
821
  - `WurlitzerEP200`: Wurlitzer EP200 Electric Piano v1.1 (16-May-1999)
822
+ - `TX81Z`: Yamaha TX81Z "FM Piano" patch (from the VCSL Electrophones set)
735
823
 
736
824
  ### Mallets
737
825
 
@@ -742,7 +830,7 @@ import { Mallet, getMalletNames } from "smplr";
742
830
 
743
831
  const instruments = getMalletNames();
744
832
 
745
- const mallet = new Mallet(new AudioContext(), {
833
+ const mallet = Mallet(new AudioContext(), {
746
834
  instrument: instruments[0],
747
835
  });
748
836
  ```
@@ -756,7 +844,7 @@ import { Mellotron, getMellotronNames } from "smplr";
756
844
 
757
845
  const instruments = getMellotronNames();
758
846
 
759
- const mallet = new Mellotron(new AudioContext(), {
847
+ const mellotron = Mellotron(new AudioContext(), {
760
848
  instrument: instruments[0],
761
849
  });
762
850
  ```
@@ -771,13 +859,13 @@ import { DrumMachine, getDrumMachineNames } from "smplr";
771
859
  const instruments = getDrumMachineNames();
772
860
 
773
861
  const context = new AudioContext();
774
- const drums = new DrumMachine(context, { instrument: "TR-808" });
862
+ const drums = DrumMachine(context, { instrument: "TR-808" });
775
863
  drums.start({ note: "kick" });
776
864
 
777
865
  // Drum samples are grouped and can have sample variations:
778
866
  drums.getSampleNames(); // => ['kick-1', 'kick-2', 'snare-1', 'snare-2', ...]
779
867
  drums.getGroupNames(); // => ['kick', 'snare']
780
- drums.getSampleNamesForGroup("kick") => // => ['kick-1', 'kick-2']
868
+ drums.getSampleNamesForGroup("kick"); // => ['kick-1', 'kick-2']
781
869
 
782
870
  // You can trigger samples by group name or specific sample
783
871
  drums.start("kick"); // Play the first sample of the group
@@ -793,7 +881,7 @@ const instruments = getSmolkenNames(); // => Arco, Pizzicato & Switched
793
881
 
794
882
  // Create an instrument
795
883
  const context = new AudioContext();
796
- const doubleBass = await new Smolken(context, { instrument: "Arco" }).load;
884
+ const doubleBass = await Smolken(context, { instrument: "Arco" }).load;
797
885
  ```
798
886
 
799
887
  ### Versilian
@@ -809,7 +897,7 @@ import { Versilian, getVersilianInstruments } from "smplr";
809
897
  const instrumentNames = await getVersilianInstruments();
810
898
 
811
899
  const context = new AudioContext();
812
- const sampler = new Versilian(context, { instrument: instrumentNames[0] });
900
+ const versilian = Versilian(context, { instrument: instrumentNames[0] });
813
901
  ```
814
902
 
815
903
  ### Soundfont2Sampler
@@ -821,7 +909,7 @@ import { Soundfont2Sampler } from "smplr";
821
909
  import { SoundFont2 } from "soundfont2";
822
910
 
823
911
  const context = new AudioContext();
824
- const sampler = new Soundfont2Sampler(context, {
912
+ const sampler = Soundfont2Sampler(context, {
825
913
  url: "https://smpldsnds.github.io/soundfonts/soundfonts/galaxy-electric-pianos.sf2",
826
914
  createSoundfont: (data) => new SoundFont2(data),
827
915
  });