smplr 0.13.4 → 0.15.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
@@ -121,6 +121,7 @@ All instruments share some configuration options that are passed as second argum
121
121
  - `volume`: A number from 0 to 127 representing the instrument global volume. 100 by default
122
122
  - `destination`: An `AudioNode` that is the output of the instrument. `AudioContext.destination` is used by default
123
123
  - `volumeToGain`: a function to convert the volume to gain. It uses MIDI standard as default.
124
+ - `disableScheduler`: disable internal scheduler. `false` by default.
124
125
  - `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.
125
126
  - `scheduleIntervalMs`: the interval of the scheduler. 50ms by default.
126
127
  - `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.
@@ -498,9 +499,34 @@ import { Versilian, getVersilianInstruments } from "smplr";
498
499
  const instrumentNames = await getVersilianInstruments();
499
500
 
500
501
  const context = new AudioContext();
501
- const sampler = new Versilian(context, { instrument: instrumentNAmes[0] });
502
+ const sampler = new Versilian(context, { instrument: instrumentNames[0] });
502
503
  ```
503
504
 
505
+ ### Soundfont2Sampler
506
+
507
+ Sampler capable of reading .sf2 files directly:
508
+
509
+ ```ts
510
+ import { Soundfont2Sampler } from "smplr";
511
+ import { SoundFont2 } from "soundfont2";
512
+
513
+ const context = new AudioContext();
514
+ const sampler = new Soundfont2Sampler(context, {
515
+ url: "https://smpldsnds.github.io/soundfonts/soundfonts/galaxy-electric-pianos.sf2",
516
+ createSoundfont: (data) => new SoundFont2(data),
517
+ });
518
+
519
+ sampler.load.then(() => {
520
+ // list all available instruments for the soundfont
521
+ console.log(sampler.instrumentNames);
522
+
523
+ // load the first available instrument
524
+ sampler.loadInstrument(sampler.instrumentNames[0]);
525
+ });
526
+ ```
527
+
528
+ Still limited support. API may vary.
529
+
504
530
  ## License
505
531
 
506
532
  MIT License
package/dist/index.d.mts CHANGED
@@ -1,32 +1,3 @@
1
- type AudioInsert = {
2
- input: AudioNode;
3
- output: AudioNode;
4
- };
5
-
6
- type ChannelConfig = {
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<ChannelConfig>);
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
1
  type StorageResponse = {
31
2
  readonly status: number;
32
3
  arrayBuffer(): Promise<ArrayBuffer>;
@@ -62,13 +33,14 @@ type Listener<T> = (value: T) => void;
62
33
  */
63
34
  type Subscribe<T> = (listener: Listener<T>) => Unsubscribe;
64
35
 
36
+ type StopFn = (time?: number) => void;
65
37
  /**
66
38
  * @private
67
39
  */
68
40
  type InternalPlayer = {
69
41
  readonly buffers: AudioBuffers;
70
42
  readonly context: BaseAudioContext;
71
- start(sample: SampleStart): (time?: number) => void;
43
+ start(sample: SampleStart): StopFn;
72
44
  stop(sample?: SampleStop): void;
73
45
  disconnect(): void;
74
46
  };
@@ -195,8 +167,40 @@ type SampleRegion = {
195
167
  };
196
168
  type RegionGroup = {
197
169
  regions: SampleRegion[];
198
- sample: Partial<SampleOptions>;
199
170
  };
171
+ type SamplerInstrument = {
172
+ groups: RegionGroup[];
173
+ options: Partial<SampleOptions>;
174
+ };
175
+
176
+ type AudioInsert = {
177
+ input: AudioNode;
178
+ output: AudioNode;
179
+ };
180
+
181
+ type ChannelConfig = {
182
+ destination: AudioNode;
183
+ volume: number;
184
+ volumeToGain: (volume: number) => number;
185
+ };
186
+ type OutputChannel = Omit<Channel, "input">;
187
+ /**
188
+ * An output channel with audio effects
189
+ * @private
190
+ */
191
+ declare class Channel {
192
+ #private;
193
+ readonly context: BaseAudioContext;
194
+ readonly setVolume: (vol: number) => void;
195
+ readonly input: AudioNode;
196
+ constructor(context: BaseAudioContext, options?: Partial<ChannelConfig>);
197
+ addInsert(effect: AudioNode | AudioInsert): void;
198
+ addEffect(name: string, effect: AudioNode | {
199
+ input: AudioNode;
200
+ }, mixValue: number): void;
201
+ sendEffect(name: string, mix: number): void;
202
+ disconnect(): void;
203
+ }
200
204
 
201
205
  type SamplerConfig = {
202
206
  storage?: Storage;
@@ -222,7 +226,7 @@ declare class Sampler {
222
226
  constructor(context: AudioContext, options?: Partial<SamplerConfig>);
223
227
  loaded(): Promise<this>;
224
228
  get output(): OutputChannel;
225
- start(sample: SampleStart | string | number): (time?: number | undefined) => void;
229
+ start(sample: SampleStart | string | number): StopFn;
226
230
  stop(sample?: SampleStop | string | number): void;
227
231
  disconnect(): void;
228
232
  }
@@ -261,7 +265,7 @@ declare class DrumMachine {
261
265
  loaded(): Promise<this>;
262
266
  get sampleNames(): string[];
263
267
  getVariations(name: string): string[];
264
- start(sample: SampleStart): (time?: number | undefined) => void;
268
+ start(sample: SampleStart): StopFn;
265
269
  stop(sample: SampleStop): void;
266
270
  }
267
271
 
@@ -369,7 +373,7 @@ declare class ElectricPiano extends SfzSampler {
369
373
  }
370
374
 
371
375
  declare function getVersilianInstruments(): Promise<string[]>;
372
- declare function VcslInstrumentLoader(instrument: string, buffers: AudioBuffers, group: RegionGroup): (context: BaseAudioContext, storage: Storage) => Promise<void[]>;
376
+ declare function VcslInstrumentLoader(instrument: string, buffers: AudioBuffers): (context: BaseAudioContext, storage: Storage) => Promise<RegionGroup>;
373
377
  type VersilianConfig = {
374
378
  instrument: string;
375
379
  storage: Storage;
@@ -408,6 +412,7 @@ type MellotronConfig = {
408
412
  type MellotronOptions = Partial<MellotronConfig & DefaultPlayerConfig>;
409
413
  declare class Mellotron implements InternalPlayer {
410
414
  readonly context: BaseAudioContext;
415
+ private readonly options;
411
416
  private readonly config;
412
417
  private readonly player;
413
418
  private readonly group;
@@ -415,7 +420,7 @@ declare class Mellotron implements InternalPlayer {
415
420
  constructor(context: BaseAudioContext, options: MellotronOptions);
416
421
  get buffers(): AudioBuffers;
417
422
  get output(): OutputChannel;
418
- start(sample: SampleStart | string | number): (time?: number | undefined) => void;
423
+ start(sample: SampleStart | string | number): StopFn;
419
424
  stop(sample?: SampleStop | string | number): void;
420
425
  disconnect(): void;
421
426
  }
@@ -477,8 +482,75 @@ declare class Soundfont {
477
482
  get hasLoops(): boolean;
478
483
  loaded(): Promise<this>;
479
484
  disconnect(): void;
485
+ start(sample: SampleStart | string | number): StopFn;
486
+ stop(sample?: SampleStop | string | number): void;
487
+ }
488
+
489
+ type RegionPlayerOptions = ChannelConfig & SampleOptions & QueuedPlayerConfig;
490
+ /**
491
+ * A player with an channel output and a region group to read samples info from
492
+ * @private
493
+ */
494
+ declare class RegionPlayer implements InternalPlayer {
495
+ readonly context: BaseAudioContext;
496
+ readonly output: OutputChannel;
497
+ instrument: SamplerInstrument;
498
+ private readonly player;
499
+ private seqNum;
500
+ constructor(context: BaseAudioContext, options: Partial<RegionPlayerOptions>);
501
+ get buffers(): AudioBuffers;
502
+ start(sample: SampleStart | string | number): (time?: number) => void;
503
+ stop(sample?: SampleStop | string | number): void;
504
+ disconnect(): void;
505
+ }
506
+
507
+ type Sf2 = {
508
+ instruments: Sf2Instrument[];
509
+ };
510
+ type Sf2Instrument = {
511
+ header: {
512
+ name: string;
513
+ };
514
+ zones: Sf2Zone[];
515
+ };
516
+ type Sf2Zone = {
517
+ sample: Sf2Sample;
518
+ keyRange?: {
519
+ lo: number;
520
+ hi: number;
521
+ };
522
+ };
523
+ type Sf2Sample = {
524
+ data: Int16Array;
525
+ header: {
526
+ name: string;
527
+ sampleRate: number;
528
+ originalPitch: number;
529
+ pitchCorrection: number;
530
+ start: number;
531
+ end: number;
532
+ startLoop: number;
533
+ endLoop: number;
534
+ };
535
+ };
536
+ type Soundfont2Options = Partial<RegionPlayerOptions> & {
537
+ url: string;
538
+ createSoundfont: (data: Uint8Array) => Sf2;
539
+ };
540
+ declare class Soundfont2Sampler {
541
+ #private;
542
+ readonly context: AudioContext;
543
+ readonly options: Soundfont2Options;
544
+ player: RegionPlayer;
545
+ soundfont: Sf2 | undefined;
546
+ load: Promise<this>;
547
+ constructor(context: AudioContext, options: Soundfont2Options);
548
+ get instrumentNames(): string[];
549
+ loadInstrument(instrumentName: string): (AudioBuffers | SamplerInstrument)[] | undefined;
550
+ get output(): OutputChannel;
480
551
  start(sample: SampleStart | string | number): (time?: number | undefined) => void;
481
552
  stop(sample?: SampleStop | string | number): void;
553
+ disconnect(): void;
482
554
  }
483
555
 
484
556
  /**
@@ -505,7 +577,7 @@ declare class SplendidGrandPiano {
505
577
  get output(): OutputChannel;
506
578
  get buffers(): AudioBuffers;
507
579
  loaded(): Promise<this>;
508
- start(sampleOrNote: SampleStart | number | string): (time?: number | undefined) => void;
580
+ start(sampleOrNote: SampleStart | number | string): StopFn;
509
581
  stop(sample?: SampleStop | number | string): void;
510
582
  }
511
583
  declare const LAYERS: ({
@@ -520,4 +592,4 @@ declare const LAYERS: ({
520
592
  cutoff?: undefined;
521
593
  })[];
522
594
 
523
- export { CacheStorage, DrumMachine, type DrumMachineOptions, ElectricPiano, HttpStorage, LAYERS, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, Reverb, Sampler, type SamplerConfig, Smolken, type SmolkenConfig, type SmolkenOptions, Soundfont, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type Storage, type StorageResponse, VcslInstrumentLoader, Versilian, type VersilianConfig, type VersilianOptions, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments };
595
+ export { CacheStorage, DrumMachine, type DrumMachineOptions, ElectricPiano, HttpStorage, LAYERS, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, Reverb, Sampler, type SamplerConfig, Smolken, type SmolkenConfig, type SmolkenOptions, Soundfont, type Soundfont2Options, Soundfont2Sampler, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type Storage, type StorageResponse, VcslInstrumentLoader, Versilian, type VersilianConfig, type VersilianOptions, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments };
package/dist/index.d.ts CHANGED
@@ -1,32 +1,3 @@
1
- type AudioInsert = {
2
- input: AudioNode;
3
- output: AudioNode;
4
- };
5
-
6
- type ChannelConfig = {
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<ChannelConfig>);
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
1
  type StorageResponse = {
31
2
  readonly status: number;
32
3
  arrayBuffer(): Promise<ArrayBuffer>;
@@ -62,13 +33,14 @@ type Listener<T> = (value: T) => void;
62
33
  */
63
34
  type Subscribe<T> = (listener: Listener<T>) => Unsubscribe;
64
35
 
36
+ type StopFn = (time?: number) => void;
65
37
  /**
66
38
  * @private
67
39
  */
68
40
  type InternalPlayer = {
69
41
  readonly buffers: AudioBuffers;
70
42
  readonly context: BaseAudioContext;
71
- start(sample: SampleStart): (time?: number) => void;
43
+ start(sample: SampleStart): StopFn;
72
44
  stop(sample?: SampleStop): void;
73
45
  disconnect(): void;
74
46
  };
@@ -195,8 +167,40 @@ type SampleRegion = {
195
167
  };
196
168
  type RegionGroup = {
197
169
  regions: SampleRegion[];
198
- sample: Partial<SampleOptions>;
199
170
  };
171
+ type SamplerInstrument = {
172
+ groups: RegionGroup[];
173
+ options: Partial<SampleOptions>;
174
+ };
175
+
176
+ type AudioInsert = {
177
+ input: AudioNode;
178
+ output: AudioNode;
179
+ };
180
+
181
+ type ChannelConfig = {
182
+ destination: AudioNode;
183
+ volume: number;
184
+ volumeToGain: (volume: number) => number;
185
+ };
186
+ type OutputChannel = Omit<Channel, "input">;
187
+ /**
188
+ * An output channel with audio effects
189
+ * @private
190
+ */
191
+ declare class Channel {
192
+ #private;
193
+ readonly context: BaseAudioContext;
194
+ readonly setVolume: (vol: number) => void;
195
+ readonly input: AudioNode;
196
+ constructor(context: BaseAudioContext, options?: Partial<ChannelConfig>);
197
+ addInsert(effect: AudioNode | AudioInsert): void;
198
+ addEffect(name: string, effect: AudioNode | {
199
+ input: AudioNode;
200
+ }, mixValue: number): void;
201
+ sendEffect(name: string, mix: number): void;
202
+ disconnect(): void;
203
+ }
200
204
 
201
205
  type SamplerConfig = {
202
206
  storage?: Storage;
@@ -222,7 +226,7 @@ declare class Sampler {
222
226
  constructor(context: AudioContext, options?: Partial<SamplerConfig>);
223
227
  loaded(): Promise<this>;
224
228
  get output(): OutputChannel;
225
- start(sample: SampleStart | string | number): (time?: number | undefined) => void;
229
+ start(sample: SampleStart | string | number): StopFn;
226
230
  stop(sample?: SampleStop | string | number): void;
227
231
  disconnect(): void;
228
232
  }
@@ -261,7 +265,7 @@ declare class DrumMachine {
261
265
  loaded(): Promise<this>;
262
266
  get sampleNames(): string[];
263
267
  getVariations(name: string): string[];
264
- start(sample: SampleStart): (time?: number | undefined) => void;
268
+ start(sample: SampleStart): StopFn;
265
269
  stop(sample: SampleStop): void;
266
270
  }
267
271
 
@@ -369,7 +373,7 @@ declare class ElectricPiano extends SfzSampler {
369
373
  }
370
374
 
371
375
  declare function getVersilianInstruments(): Promise<string[]>;
372
- declare function VcslInstrumentLoader(instrument: string, buffers: AudioBuffers, group: RegionGroup): (context: BaseAudioContext, storage: Storage) => Promise<void[]>;
376
+ declare function VcslInstrumentLoader(instrument: string, buffers: AudioBuffers): (context: BaseAudioContext, storage: Storage) => Promise<RegionGroup>;
373
377
  type VersilianConfig = {
374
378
  instrument: string;
375
379
  storage: Storage;
@@ -408,6 +412,7 @@ type MellotronConfig = {
408
412
  type MellotronOptions = Partial<MellotronConfig & DefaultPlayerConfig>;
409
413
  declare class Mellotron implements InternalPlayer {
410
414
  readonly context: BaseAudioContext;
415
+ private readonly options;
411
416
  private readonly config;
412
417
  private readonly player;
413
418
  private readonly group;
@@ -415,7 +420,7 @@ declare class Mellotron implements InternalPlayer {
415
420
  constructor(context: BaseAudioContext, options: MellotronOptions);
416
421
  get buffers(): AudioBuffers;
417
422
  get output(): OutputChannel;
418
- start(sample: SampleStart | string | number): (time?: number | undefined) => void;
423
+ start(sample: SampleStart | string | number): StopFn;
419
424
  stop(sample?: SampleStop | string | number): void;
420
425
  disconnect(): void;
421
426
  }
@@ -477,8 +482,75 @@ declare class Soundfont {
477
482
  get hasLoops(): boolean;
478
483
  loaded(): Promise<this>;
479
484
  disconnect(): void;
485
+ start(sample: SampleStart | string | number): StopFn;
486
+ stop(sample?: SampleStop | string | number): void;
487
+ }
488
+
489
+ type RegionPlayerOptions = ChannelConfig & SampleOptions & QueuedPlayerConfig;
490
+ /**
491
+ * A player with an channel output and a region group to read samples info from
492
+ * @private
493
+ */
494
+ declare class RegionPlayer implements InternalPlayer {
495
+ readonly context: BaseAudioContext;
496
+ readonly output: OutputChannel;
497
+ instrument: SamplerInstrument;
498
+ private readonly player;
499
+ private seqNum;
500
+ constructor(context: BaseAudioContext, options: Partial<RegionPlayerOptions>);
501
+ get buffers(): AudioBuffers;
502
+ start(sample: SampleStart | string | number): (time?: number) => void;
503
+ stop(sample?: SampleStop | string | number): void;
504
+ disconnect(): void;
505
+ }
506
+
507
+ type Sf2 = {
508
+ instruments: Sf2Instrument[];
509
+ };
510
+ type Sf2Instrument = {
511
+ header: {
512
+ name: string;
513
+ };
514
+ zones: Sf2Zone[];
515
+ };
516
+ type Sf2Zone = {
517
+ sample: Sf2Sample;
518
+ keyRange?: {
519
+ lo: number;
520
+ hi: number;
521
+ };
522
+ };
523
+ type Sf2Sample = {
524
+ data: Int16Array;
525
+ header: {
526
+ name: string;
527
+ sampleRate: number;
528
+ originalPitch: number;
529
+ pitchCorrection: number;
530
+ start: number;
531
+ end: number;
532
+ startLoop: number;
533
+ endLoop: number;
534
+ };
535
+ };
536
+ type Soundfont2Options = Partial<RegionPlayerOptions> & {
537
+ url: string;
538
+ createSoundfont: (data: Uint8Array) => Sf2;
539
+ };
540
+ declare class Soundfont2Sampler {
541
+ #private;
542
+ readonly context: AudioContext;
543
+ readonly options: Soundfont2Options;
544
+ player: RegionPlayer;
545
+ soundfont: Sf2 | undefined;
546
+ load: Promise<this>;
547
+ constructor(context: AudioContext, options: Soundfont2Options);
548
+ get instrumentNames(): string[];
549
+ loadInstrument(instrumentName: string): (AudioBuffers | SamplerInstrument)[] | undefined;
550
+ get output(): OutputChannel;
480
551
  start(sample: SampleStart | string | number): (time?: number | undefined) => void;
481
552
  stop(sample?: SampleStop | string | number): void;
553
+ disconnect(): void;
482
554
  }
483
555
 
484
556
  /**
@@ -505,7 +577,7 @@ declare class SplendidGrandPiano {
505
577
  get output(): OutputChannel;
506
578
  get buffers(): AudioBuffers;
507
579
  loaded(): Promise<this>;
508
- start(sampleOrNote: SampleStart | number | string): (time?: number | undefined) => void;
580
+ start(sampleOrNote: SampleStart | number | string): StopFn;
509
581
  stop(sample?: SampleStop | number | string): void;
510
582
  }
511
583
  declare const LAYERS: ({
@@ -520,4 +592,4 @@ declare const LAYERS: ({
520
592
  cutoff?: undefined;
521
593
  })[];
522
594
 
523
- export { CacheStorage, DrumMachine, type DrumMachineOptions, ElectricPiano, HttpStorage, LAYERS, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, Reverb, Sampler, type SamplerConfig, Smolken, type SmolkenConfig, type SmolkenOptions, Soundfont, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type Storage, type StorageResponse, VcslInstrumentLoader, Versilian, type VersilianConfig, type VersilianOptions, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments };
595
+ export { CacheStorage, DrumMachine, type DrumMachineOptions, ElectricPiano, HttpStorage, LAYERS, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, Reverb, Sampler, type SamplerConfig, Smolken, type SmolkenConfig, type SmolkenOptions, Soundfont, type Soundfont2Options, Soundfont2Sampler, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type Storage, type StorageResponse, VcslInstrumentLoader, Versilian, type VersilianConfig, type VersilianOptions, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments };