smplr 0.8.1 → 0.10.1

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
@@ -44,21 +44,49 @@ Read [CHANGELOG](https://github.com/danigb/smplr/blob/main/CHANGELOG.md) for cha
44
44
  - Easy to use: everything should be intuitive for non-experienced developers
45
45
  - Decent sounding: uses high quality open source samples. For better or worse, it is sample based 🤷
46
46
 
47
- ## Install
47
+ ## Setup
48
48
 
49
- Install with npm or your favourite package manager:
49
+ You can install the library with a package manager or use it directly by importing from the browser.
50
+
51
+ Samples are stored at https://github.com/smpldsnds and there is no need to download them. Kudos to all _samplerist_ 🙌
52
+
53
+ #### Using a package manger
54
+
55
+ Use npm or your favourite package manager to install the library to use it in your project:
50
56
 
51
57
  ```
52
58
  npm i smplr
53
59
  ```
54
60
 
55
- Samples are stored at https://github.com/danigb/samples and there is no need to install them. Kudos to all samplers 🙌
61
+ #### Usage from the browser
62
+
63
+ You can import directly from the browser. For example:
64
+
65
+ ```html
66
+ <html>
67
+ <body>
68
+ <button id="btn">play</button>
69
+ </body>
70
+ <script type="module">
71
+ import { SplendidGrandPiano } from "https://unpkg.com/smplr@0.10.0/dist/index.mjs"; // needs to be a url
72
+ const context = new AudioContext(); // create the audio context
73
+ const marimba = new SplendidGrandPiano(context); // create and load the instrument
74
+
75
+ document.getElementById("btn").onclick = () => {
76
+ context.resume(); // enable audio context after a user interaction
77
+ marimba.start({ note: 60, velocity: 80 }); // play the note
78
+ };
79
+ </script>
80
+ </html>
81
+ ```
82
+
83
+ The package needs to be serve as a url from a service like [unpkg](unpkg.com) or similar.
56
84
 
57
85
  ## Documentation
58
86
 
59
87
  ### Create and load an instrument
60
88
 
61
- All instruments follows the same pattern: `new Instrument(context, options?)`. For example:
89
+ All instruments follows the same pattern: `new Instrument(context, options)`. For example:
62
90
 
63
91
  ```js
64
92
  import { SplendidGrandPiano, Soundfont } from "smplr";
@@ -84,6 +112,8 @@ Since the promise returns the instrument instance, you can create and wait in a
84
112
  const piano = await new SplendidGrandPiano(context).load;
85
113
  ```
86
114
 
115
+ ⚠️ In versions lower than 0.8.0 a `loaded()` function was exposed instead.
116
+
87
117
  ### Play
88
118
 
89
119
  #### Start and stop notes
@@ -121,7 +151,7 @@ piano.stop(60);
121
151
 
122
152
  #### Schedule notes
123
153
 
124
- You can schedule notes using `time` and `duration` properties. Both are measured in seconds, and time is the number of seconds since the AudioContext was created.
154
+ 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`
125
155
 
126
156
  For example, next example plays a C major arpeggio, one note per second:
127
157
 
@@ -137,16 +167,16 @@ const now = context.currentTime;
137
167
  You can loop a note by using `loop`, `loopStart` and `loopEnd`:
138
168
 
139
169
  ```js
140
- const sampler = new Sampler(audioContext, { string: "mi-long-sample.mp3" });
170
+ const sampler = new Sampler(audioContext, { duh: "duh-duh-ah.mp3" });
141
171
  sampler.start({
142
- note: "string",
143
- loop: "true",
172
+ note: "duh"
173
+ loop: true
144
174
  loopStart: 1.0,
145
175
  loopEnd: 9.0,
146
176
  });
147
177
  ```
148
178
 
149
- If `loopStart` or `loopEnd` is not specified it will be use by default 0 and total duration respectively.
179
+ If `loop` is true but `loopStart` or `loopEnd` are not specified, 0 and total duration will be used by default, respectively.
150
180
 
151
181
  #### Change volume
152
182
 
@@ -156,10 +186,28 @@ Instrument `output` attribute represents the main output of the instrument. `out
156
186
  piano.output.setVolume(80);
157
187
  ```
158
188
 
159
- Bear in mind that `volume` is global to the instrument, but `velocity` is specific for each note.
189
+ ⚠️ `volume` is global to the instrument, but `velocity` is specific for each note.
190
+
191
+ #### Events
192
+
193
+ You can add a `onEnded` callback that will be invoked when the note ends:
194
+
195
+ ```js
196
+ piano.start({
197
+ note: "C4",
198
+ duration: 1,
199
+ onEnded: () => {
200
+ // will be called after 1 second
201
+ },
202
+ });
203
+ ```
204
+
205
+ The callback will receive as parameter the same object you pass to the `start` function;
160
206
 
161
207
  ### Effects
162
208
 
209
+ #### Reverb
210
+
163
211
  An packed version of [DattorroReverbNode](https://github.com/khoin/DattorroReverbNode) algorithmic reverb is included.
164
212
 
165
213
  Use `output.addEffect(name, effect, mix)` to connect an effect using a send bus:
@@ -177,22 +225,6 @@ To change the mix level, use `output.sendEffect(name, mix)`:
177
225
  piano.output.sendEffect("reverb", 0.5);
178
226
  ```
179
227
 
180
- #### Events
181
-
182
- You can add a `onEnded` callback that will be invoked when the note ends:
183
-
184
- ```js
185
- piano.start({
186
- note: "C4",
187
- duration: 1,
188
- onEnded: () => {
189
- // will be called after 1 second
190
- },
191
- });
192
- ```
193
-
194
- The callback will receive as parameter the same object you pass to the `start` function;
195
-
196
228
  ### Experimental features
197
229
 
198
230
  #### Cache requests
@@ -265,7 +297,7 @@ const marimba = new Soundfont(context, {
265
297
  });
266
298
  ```
267
299
 
268
- #### Looping (experimental)
300
+ #### Soundfont sustained notes
269
301
 
270
302
  You can enable note looping to make note names indefinitely long by loading loop data:
271
303
 
@@ -276,11 +308,11 @@ const marimba = new Soundfont(context, {
276
308
  });
277
309
  ```
278
310
 
279
- Bear in mind that currently that feature produces click on lot of instruments.
311
+ ⚠️ This feature is still experimental and can produces clicks on lot of instruments.
280
312
 
281
- ### Piano
313
+ ### SplendidGrandPiano
282
314
 
283
- A sampled acoustic piano. It uses Steinway samples with 4 velocity layers from
315
+ A sampled acoustic piano. It uses Steinway samples with 4 velocity groups from
284
316
  [SplendidGrandPiano](https://github.com/sfzinstruments/SplendidGrandPiano)
285
317
 
286
318
  ```js
@@ -364,6 +396,34 @@ drums.getVariations("kick").forEach((variation, index) => {
364
396
  });
365
397
  ```
366
398
 
399
+ ### Smolken double bass
400
+
401
+ ```js
402
+ import { Smolken, getSmolkenNames } from "smplr";
403
+
404
+ const instruments = getSmolkenNames(); // => Arco, Pizzicato & Switched
405
+
406
+ // Create an instrument
407
+ const context = new AudioContext();
408
+ const doubleBass = await new Smolken(context, { instrument: "Arco" }).load;
409
+ ```
410
+
411
+ ### Versilian
412
+
413
+ Versilian is a sample capable of using the [Versilian Community Sample Library](https://github.com/sgossner/VCSL).
414
+
415
+ ⚠️ Not all features are implemented. Some instruments may sound incorrect ⚠️
416
+
417
+ ```js
418
+ import { Versilian, getVersilianInstruments } from "smplr";
419
+
420
+ // getVersilianInstruments returns a Promise
421
+ const instrumentNames = await getVersilianInstruments();
422
+
423
+ const context = new AudioContext();
424
+ const sampler = new Versilian(context, { instrument: instrumentNAmes[0] });
425
+ ```
426
+
367
427
  ## License
368
428
 
369
429
  MIT License
@@ -0,0 +1,492 @@
1
+ type AudioInsert = {
2
+ input: AudioNode;
3
+ output: AudioNode;
4
+ };
5
+
6
+ type ChannelOptions = {
7
+ destination: AudioNode;
8
+ volume: number;
9
+ volumeToGain: (volume: number) => number;
10
+ };
11
+ type OutputChannel = Omit<Channel, "input">;
12
+ /**
13
+ * An output channel with audio effects
14
+ * @private
15
+ */
16
+ declare class Channel {
17
+ #private;
18
+ readonly context: BaseAudioContext;
19
+ readonly setVolume: (vol: number) => void;
20
+ readonly input: AudioNode;
21
+ constructor(context: BaseAudioContext, options: Partial<ChannelOptions>);
22
+ addInsert(effect: AudioNode | AudioInsert): void;
23
+ addEffect(name: string, effect: AudioNode | {
24
+ input: AudioNode;
25
+ }, mixValue: number): void;
26
+ sendEffect(name: string, mix: number): void;
27
+ disconnect(): void;
28
+ }
29
+
30
+ type StorageResponse = {
31
+ readonly status: number;
32
+ arrayBuffer(): Promise<ArrayBuffer>;
33
+ json(): Promise<any>;
34
+ text(): Promise<string>;
35
+ };
36
+ type Storage = {
37
+ fetch: (url: string) => Promise<StorageResponse>;
38
+ };
39
+ declare const HttpStorage: Storage;
40
+ declare class CacheStorage implements Storage {
41
+ #private;
42
+ constructor(name?: string);
43
+ fetch(url: string): Promise<StorageResponse>;
44
+ }
45
+
46
+ type AudioBuffers = Record<string | number, AudioBuffer | undefined>;
47
+ /**
48
+ * A function that downloads audio into a AudioBuffers
49
+ */
50
+ type AudioBuffersLoader = (context: BaseAudioContext, buffers: AudioBuffers) => Promise<void>;
51
+
52
+ /**
53
+ * A function to unsubscribe from an event or control
54
+ */
55
+ type Unsubscribe = () => void;
56
+ /**
57
+ * A function that listener to event or control changes
58
+ */
59
+ type Listener<T> = (value: T) => void;
60
+ /**
61
+ * A function to subscribe an trigger or control events
62
+ */
63
+ type Subscribe<T> = (listener: Listener<T>) => Unsubscribe;
64
+
65
+ /**
66
+ * @private
67
+ */
68
+ type InternalPlayer = {
69
+ readonly buffers: AudioBuffers;
70
+ readonly context: BaseAudioContext;
71
+ start(sample: SampleStart): (time?: number) => void;
72
+ stop(sample?: SampleStop): void;
73
+ disconnect(): void;
74
+ };
75
+ type SampleStop = {
76
+ stopId?: string | number;
77
+ time?: number;
78
+ };
79
+ type SampleOptions = {
80
+ decayTime?: number;
81
+ detune?: number;
82
+ duration?: number | null;
83
+ velocity?: number;
84
+ lpfCutoffHz?: number;
85
+ loop?: boolean;
86
+ loopStart?: number;
87
+ loopEnd?: number;
88
+ gainOffset?: number;
89
+ };
90
+ type SampleStart = {
91
+ name?: string;
92
+ note: string | number;
93
+ onEnded?: (sample: SampleStart) => void;
94
+ stop?: Subscribe<number>;
95
+ stopId?: string | number;
96
+ time?: number;
97
+ } & SampleOptions;
98
+ /**
99
+ * Heavily inspired by SFZ format
100
+ */
101
+ type SampleRegion = {
102
+ sampleName: string;
103
+ /**
104
+ * This specifies the MIDI key number that corresponds to the sample's original pitch
105
+ */
106
+ midiPitch: number;
107
+ /**
108
+ * This defines the lowest MIDI key number that will trigger this sample.
109
+ */
110
+ midiLow?: number;
111
+ /**
112
+ * This specifies the highest MIDI key number that will trigger this sample.
113
+ */
114
+ midiHigh?: number;
115
+ velLow?: number;
116
+ /**
117
+ * This determines the highest MIDI velocity at which this sample will be triggered.
118
+ */
119
+ velHigh?: number;
120
+ /**
121
+ * These define the pitch bend range for the samples in this group. The values are given in cents
122
+ */
123
+ bendUp?: number;
124
+ bendDown?: number;
125
+ /**
126
+ * Velocity-based amplitude scaling. [Vel, Gain] tells the sampler to play the sample
127
+ * at volume Gain when the note's velocity is Vel
128
+ */
129
+ ampVelCurve?: [number, number];
130
+ /**
131
+ * Amplitude envelope release time in seconds.
132
+ * Stored here for convenience (flatness) but needs to be
133
+ * copied inside sample options before playback
134
+ */
135
+ ampRelease?: number;
136
+ /**
137
+ * Attack time in seconds. Currently not implemented
138
+ *
139
+ * @see http://sfzformat.com/opcodes/amp_attack.html
140
+ */
141
+ ampAttack?: number;
142
+ /**
143
+ * seqLength defines how many samples are in the sequence.
144
+ * When using the seqPosition and seqLength , you can set up round-robin
145
+ * or sequential sample playback.
146
+ */
147
+ seqLength?: number;
148
+ /**
149
+ * Determine the position of this particular sample within the sequence
150
+ * 1 means first element of the sequence (not zero based!)
151
+ */
152
+ seqPosition?: number;
153
+ /**
154
+ * This assigns the group to a specific number.
155
+ * Group numbers can be used in combination with groupOffBy to implement
156
+ * exclusive groups, where playing one sample can stop another sample from playing.
157
+ */
158
+ group?: number;
159
+ /**
160
+ * Triggering a sample in this region will stop (or "turn off") any samples
161
+ * currently playing in the specified group number
162
+ */
163
+ groupOffBy?: number;
164
+ /**
165
+ * Start offset (in samples). Not implemented (yet)
166
+ */
167
+ offset?: number;
168
+ /**
169
+ * Adjust the playback pitch of a sample (in semitones)
170
+ */
171
+ tune?: number;
172
+ /**
173
+ * The volume opcode in SFZ defines the default playback volume for a given region.
174
+ * It specifies an adjustment to the sample's original amplitude.
175
+ * The unit for the volume opcode is decibels (dB).
176
+ */
177
+ volume?: number;
178
+ /**
179
+ * sample options for this particular region
180
+ */
181
+ sample?: Partial<SampleOptions>;
182
+ };
183
+ type RegionGroup = {
184
+ regions: SampleRegion[];
185
+ sample: Partial<SampleOptions>;
186
+ };
187
+
188
+ declare function getDrumMachineNames(): string[];
189
+ type DrumMachineConfig = ChannelOptions & SampleOptions & {
190
+ instrument: string;
191
+ storage?: Storage;
192
+ };
193
+ declare class DrumMachine {
194
+ #private;
195
+ private readonly player;
196
+ readonly load: Promise<this>;
197
+ readonly output: OutputChannel;
198
+ constructor(context: AudioContext, options?: Partial<DrumMachineConfig>);
199
+ loaded(): Promise<this>;
200
+ get sampleNames(): string[];
201
+ getVariations(name: string): string[];
202
+ start(sample: SampleStart): (time?: number | undefined) => void;
203
+ stop(sample: SampleStop): void;
204
+ }
205
+
206
+ type SfzInstrument = {
207
+ name: string;
208
+ formats?: string[];
209
+ baseUrl?: string;
210
+ websfzUrl: string;
211
+ tags?: string[];
212
+ };
213
+
214
+ type Websfz = {
215
+ global: Record<string, string | number>;
216
+ groups: WebsfzGroup[];
217
+ meta: {
218
+ name?: string;
219
+ description?: string;
220
+ license?: string;
221
+ source?: string;
222
+ baseUrl?: string;
223
+ websfzUrl?: string;
224
+ formats?: string[];
225
+ tags?: string[];
226
+ };
227
+ };
228
+ type WebsfzGroup = {
229
+ group_label?: string;
230
+ group?: number;
231
+ hikey?: number;
232
+ hivel?: number;
233
+ lokey?: number;
234
+ lovel?: number;
235
+ off_by?: number;
236
+ off_mode?: "normal";
237
+ pitch_keycenter?: number;
238
+ regions: WebsfzRegion[];
239
+ seq_length?: number;
240
+ trigger?: "first" | "legato";
241
+ volume?: number;
242
+ amp_velcurve_83?: number;
243
+ locc64?: number;
244
+ hicc64?: number;
245
+ hicc107?: number;
246
+ locc107?: number;
247
+ pan_oncc122?: number;
248
+ tune_oncc123?: number;
249
+ eg06_time1_oncc109?: number;
250
+ ampeg_attack_oncc100?: number;
251
+ };
252
+ type WebsfzRegion = {
253
+ end?: number;
254
+ group?: number;
255
+ hivel?: number;
256
+ lovel?: number;
257
+ hikey?: number;
258
+ key?: number;
259
+ lokey?: number;
260
+ off_by?: number;
261
+ pitch_keycenter?: number;
262
+ region_label?: number;
263
+ sample: string;
264
+ seq_position?: number;
265
+ trigger?: "first" | "legato";
266
+ volume?: number;
267
+ locc64?: number;
268
+ hicc64?: number;
269
+ ampeg_attack_oncc100?: number;
270
+ eg06_time1_oncc109?: number;
271
+ pan_oncc122?: number;
272
+ tune_oncc123?: number;
273
+ };
274
+
275
+ /**
276
+ * Splendid Grand Piano options
277
+ */
278
+ type SfzSamplerConfig = {
279
+ instrument: SfzInstrument | Websfz | string;
280
+ storage?: Storage;
281
+ destination: AudioNode;
282
+ volume: number;
283
+ velocity: number;
284
+ detune: number;
285
+ decayTime: number;
286
+ lpfCutoffHz?: number;
287
+ };
288
+ declare class SfzSampler {
289
+ #private;
290
+ readonly context: AudioContext;
291
+ readonly options: Readonly<Partial<SfzSamplerConfig>>;
292
+ private readonly player;
293
+ readonly load: Promise<this>;
294
+ constructor(context: AudioContext, options: Partial<SfzSamplerConfig> & Pick<SfzSamplerConfig, "instrument">);
295
+ get output(): OutputChannel;
296
+ loaded(): Promise<this>;
297
+ start(sample: SampleStart | string | number): void;
298
+ stop(sample?: SampleStop | string | number): void;
299
+ disconnect(): void;
300
+ }
301
+
302
+ declare function getElectricPianoNames(): string[];
303
+ declare class ElectricPiano extends SfzSampler {
304
+ readonly tremolo: Readonly<{
305
+ level: (value: number) => void;
306
+ }>;
307
+ constructor(context: AudioContext, options: Partial<SfzSamplerConfig> & {
308
+ instrument: string;
309
+ });
310
+ }
311
+
312
+ declare function getVersilianInstruments(): Promise<string[]>;
313
+ declare function VcslInstrumentLoader(instrument: string, buffers: AudioBuffers, group: RegionGroup): (context: BaseAudioContext, storage: Storage) => Promise<void[]>;
314
+ type VersilianConfig = {
315
+ instrument: string;
316
+ storage: Storage;
317
+ };
318
+ type VersilianOptions = Partial<VersilianConfig & SampleOptions & ChannelOptions>;
319
+ /**
320
+ * Versilian
321
+ *
322
+ * The Versilian Community Sample Library is an open CC0 general-purpose sample library created by Versilian Studios LLC
323
+ * for the purpose of introducing a set of quality, publicly available samples suitable for use in software and media of all kinds.
324
+ */
325
+ declare class Versilian implements InternalPlayer {
326
+ private readonly player;
327
+ readonly load: Promise<this>;
328
+ private config;
329
+ constructor(context: BaseAudioContext, options?: VersilianOptions);
330
+ get output(): OutputChannel;
331
+ get buffers(): AudioBuffers;
332
+ get context(): BaseAudioContext;
333
+ start(sample: SampleStart | string | number): (time?: number | undefined) => void;
334
+ stop(sample?: SampleStop | string | number): void;
335
+ disconnect(): void;
336
+ }
337
+
338
+ declare function getMalletNames(): string[];
339
+ declare class Mallet extends Versilian {
340
+ constructor(context: AudioContext, options: VersilianOptions);
341
+ }
342
+ declare const NAME_TO_PATH: Record<string, string | undefined>;
343
+
344
+ declare function getMellotronNames(): string[];
345
+ type MellotronConfig = {
346
+ instrument: string;
347
+ storage: Storage;
348
+ };
349
+ type MellotronOptions = Partial<SampleOptions & ChannelOptions & MellotronConfig>;
350
+ declare class Mellotron implements InternalPlayer {
351
+ readonly context: BaseAudioContext;
352
+ private readonly config;
353
+ private readonly player;
354
+ private readonly group;
355
+ readonly load: Promise<this>;
356
+ constructor(context: BaseAudioContext, options: MellotronOptions);
357
+ get buffers(): AudioBuffers;
358
+ get output(): OutputChannel;
359
+ start(sample: SampleStart | string | number): (time?: number | undefined) => void;
360
+ stop(sample?: SampleStop | string | number): void;
361
+ disconnect(): void;
362
+ }
363
+
364
+ declare const PARAMS: readonly ["preDelay", "bandwidth", "inputDiffusion1", "inputDiffusion2", "decay", "decayDiffusion1", "decayDiffusion2", "damping", "excursionRate", "excursionDepth", "wet", "dry"];
365
+ declare class Reverb {
366
+ #private;
367
+ readonly input: AudioNode;
368
+ constructor(context: AudioContext);
369
+ get paramNames(): readonly ["preDelay", "bandwidth", "inputDiffusion1", "inputDiffusion2", "decay", "decayDiffusion1", "decayDiffusion2", "damping", "excursionRate", "excursionDepth", "wet", "dry"];
370
+ getParam(name: (typeof PARAMS)[number]): AudioParam | undefined;
371
+ get isReady(): boolean;
372
+ ready(): Promise<this>;
373
+ connect(output: AudioNode): void;
374
+ }
375
+
376
+ type SamplerConfig = {
377
+ storage?: Storage;
378
+ detune: number;
379
+ volume: number;
380
+ velocity: number;
381
+ decayTime?: number;
382
+ lpfCutoffHz?: number;
383
+ destination: AudioNode;
384
+ buffers: Record<string | number, string | AudioBuffers> | AudioBuffersLoader;
385
+ volumeToGain: (volume: number) => number;
386
+ };
387
+ /**
388
+ * A Sampler instrument
389
+ *
390
+ * @private
391
+ */
392
+ declare class Sampler {
393
+ #private;
394
+ readonly context: AudioContext;
395
+ private readonly player;
396
+ readonly load: Promise<this>;
397
+ constructor(context: AudioContext, options?: Partial<SamplerConfig>);
398
+ loaded(): Promise<this>;
399
+ get output(): OutputChannel;
400
+ start(sample: SampleStart | string | number): (time?: number | undefined) => void;
401
+ stop(sample?: SampleStop | string | number): void;
402
+ disconnect(): void;
403
+ }
404
+
405
+ declare function getSmolkenNames(): string[];
406
+ type SmolkenConfig = {
407
+ instrument: string;
408
+ storage: Storage;
409
+ };
410
+ type SmolkenOptions = Partial<SmolkenConfig & SampleOptions & ChannelOptions>;
411
+ declare class Smolken implements InternalPlayer {
412
+ private readonly player;
413
+ private readonly group;
414
+ readonly load: Promise<this>;
415
+ private config;
416
+ private seqNum;
417
+ constructor(context: BaseAudioContext, options?: SmolkenOptions);
418
+ get output(): OutputChannel;
419
+ get buffers(): AudioBuffers;
420
+ get context(): BaseAudioContext;
421
+ start(sample: SampleStart | string | number): (time?: number) => void;
422
+ stop(sample?: SampleStop | string | number): void;
423
+ disconnect(): void;
424
+ }
425
+
426
+ declare function getSoundfontKits(): string[];
427
+ declare function getSoundfontNames(): string[];
428
+ type SoundfontConfig = {
429
+ kit: "FluidR3_GM" | "MusyngKite" | string;
430
+ instrument?: string;
431
+ instrumentUrl: string;
432
+ storage: Storage;
433
+ extraGain: number;
434
+ loadLoopData: boolean;
435
+ loopDataUrl?: string;
436
+ };
437
+ type SoundfontOptions = Partial<SoundfontConfig & SampleOptions & ChannelOptions>;
438
+ declare class Soundfont {
439
+ #private;
440
+ readonly context: AudioContext;
441
+ readonly config: Readonly<SoundfontConfig>;
442
+ private readonly player;
443
+ readonly load: Promise<this>;
444
+ readonly group: RegionGroup;
445
+ constructor(context: AudioContext, options: SoundfontOptions);
446
+ get output(): OutputChannel;
447
+ get hasLoops(): boolean;
448
+ loaded(): Promise<this>;
449
+ disconnect(): void;
450
+ start(sample: SampleStart | string | number): (time?: number | undefined) => void;
451
+ stop(sample?: SampleStop | string | number): void;
452
+ }
453
+
454
+ /**
455
+ * Splendid Grand Piano options
456
+ */
457
+ type SplendidGrandPianoConfig = {
458
+ baseUrl: string;
459
+ destination: AudioNode;
460
+ storage: Storage;
461
+ detune: number;
462
+ volume: number;
463
+ velocity: number;
464
+ decayTime?: number;
465
+ lpfCutoffHz?: number;
466
+ };
467
+ declare class SplendidGrandPiano {
468
+ #private;
469
+ readonly context: AudioContext;
470
+ options: Readonly<SplendidGrandPianoConfig>;
471
+ private readonly player;
472
+ readonly load: Promise<this>;
473
+ constructor(context: AudioContext, options?: Partial<SplendidGrandPianoConfig>);
474
+ get output(): OutputChannel;
475
+ get buffers(): AudioBuffers;
476
+ loaded(): Promise<this>;
477
+ start(sampleOrNote: SampleStart | number | string): (time?: number | undefined) => void;
478
+ stop(sample?: SampleStop | number | string): void;
479
+ }
480
+ declare const LAYERS: ({
481
+ name: string;
482
+ vel_range: number[];
483
+ cutoff: number;
484
+ samples: (string | number)[][];
485
+ } | {
486
+ name: string;
487
+ vel_range: number[];
488
+ samples: (string | number)[][];
489
+ cutoff?: undefined;
490
+ })[];
491
+
492
+ export { CacheStorage, DrumMachine, DrumMachineConfig, ElectricPiano, HttpStorage, LAYERS, Mallet, Mellotron, MellotronConfig, MellotronOptions, NAME_TO_PATH, Reverb, Sampler, SamplerConfig, Smolken, SmolkenConfig, SmolkenOptions, Soundfont, SoundfontOptions, SplendidGrandPiano, SplendidGrandPianoConfig, Storage, StorageResponse, VcslInstrumentLoader, Versilian, VersilianConfig, VersilianOptions, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments };