smplr 0.11.2 → 0.12.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
@@ -126,6 +126,27 @@ All instruments share some configuration options that are passed as second argum
126
126
  - `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.
127
127
  - `onEnded`: a function that is called when the note ends. It receives the started note as parameter.
128
128
 
129
+ #### Usage with standardized-audio-context
130
+
131
+ This package should be compatible with [standardized-audio-context](https://github.com/chrisguttandin/standardized-audio-context):
132
+
133
+ ```js
134
+ import { AudioContext } from "standardized-audio-context";
135
+
136
+ const context = new AudioContext();
137
+ const piano = new SplendidGrandPiano(context);
138
+ ```
139
+
140
+ However, if you are using Typescript, you might need to "force cast" the types:
141
+
142
+ ```ts
143
+ import { Soundfont } from "smplr";
144
+ import { AudioContext as StandardizedAudioContext } from "standardized-audio-context";
145
+
146
+ const context = new StandardizedAudioContext() as unknown as AudioContext;
147
+ const marimba = new Soundfont(context, { instrument: "marimba" });
148
+ ```
149
+
129
150
  ### Play
130
151
 
131
152
  #### Start and stop notes
@@ -350,6 +371,30 @@ const piano = new SplendidGrandPiano(new AudioContext());
350
371
  piano.start({ note: "C4" });
351
372
  ```
352
373
 
374
+ #### SplendidGrandPiano constructor
375
+
376
+ The second argument of the constructor accepts the following options:
377
+
378
+ - `baseUrl`:
379
+ - `detune`: global detune in cents (0 if not specified)
380
+ - `velocity`: default velocity (100 if not specified)
381
+ - `volume`: default volume (100 if not specified)
382
+ - `decayTime`: default decay time (0.5 seconds)
383
+ - `notesToLoad`: an object with the following shape: `{ notes: number[], velocityRange: [number, number]}` to specify a subset of notes to load
384
+
385
+ Example:
386
+
387
+ ```ts
388
+ const piano = new SplendidGrandPiano(context, {
389
+ detune: -20,
390
+ volume: 80,
391
+ notesToLoad: {
392
+ notes: [60],
393
+ velocityRange: [1, 127],
394
+ },
395
+ });
396
+ ```
397
+
353
398
  ### Electric Piano
354
399
 
355
400
  A sampled electric pianos. Samples from https://github.com/sfzinstruments/GregSullivan.E-Pianos
package/dist/index.d.mts CHANGED
@@ -49,34 +49,6 @@ type AudioBuffers = Record<string | number, AudioBuffer | undefined>;
49
49
  */
50
50
  type AudioBuffersLoader = (context: BaseAudioContext, buffers: AudioBuffers) => Promise<void>;
51
51
 
52
- type StorageResponse$1 = {
53
- readonly status: number;
54
- arrayBuffer(): Promise<ArrayBuffer>;
55
- json(): Promise<any>;
56
- text(): Promise<string>;
57
- };
58
- type Storage$1 = {
59
- fetch: (url: string) => Promise<StorageResponse$1>;
60
- };
61
-
62
- type AudioBuffers$1 = Record<string | number, AudioBuffer | undefined>;
63
- /**
64
- * A function that downloads audio into a AudioBuffers
65
- */
66
- type AudioBuffersLoader$1 = (context: BaseAudioContext, buffers: AudioBuffers$1) => Promise<void>;
67
-
68
- type SamplerConfig$1 = {
69
- storage?: Storage$1;
70
- detune: number;
71
- volume: number;
72
- velocity: number;
73
- decayTime?: number;
74
- lpfCutoffHz?: number;
75
- destination: AudioNode;
76
- buffers: Record<string | number, string | AudioBuffers$1> | AudioBuffersLoader$1;
77
- volumeToGain: (volume: number) => number;
78
- };
79
-
80
52
  /**
81
53
  * A function to unsubscribe from an event or control
82
54
  */
@@ -214,6 +186,35 @@ type RegionGroup = {
214
186
  sample: Partial<SampleOptions>;
215
187
  };
216
188
 
189
+ type SamplerConfig = {
190
+ storage?: Storage;
191
+ detune: number;
192
+ volume: number;
193
+ velocity: number;
194
+ decayTime?: number;
195
+ lpfCutoffHz?: number;
196
+ destination: AudioNode;
197
+ buffers: Record<string | number, string | AudioBuffers> | AudioBuffersLoader;
198
+ volumeToGain: (volume: number) => number;
199
+ };
200
+ /**
201
+ * A Sampler instrument
202
+ *
203
+ * @private
204
+ */
205
+ declare class Sampler {
206
+ #private;
207
+ readonly context: AudioContext;
208
+ private readonly player;
209
+ readonly load: Promise<this>;
210
+ constructor(context: AudioContext, options?: Partial<SamplerConfig>);
211
+ loaded(): Promise<this>;
212
+ get output(): OutputChannel;
213
+ start(sample: SampleStart | string | number): (time?: number | undefined) => void;
214
+ stop(sample?: SampleStop | string | number): void;
215
+ disconnect(): void;
216
+ }
217
+
217
218
  type QueuedPlayerConfig = {
218
219
  scheduleLookaheadMs: number;
219
220
  scheduleIntervalMs: number;
@@ -221,7 +222,7 @@ type QueuedPlayerConfig = {
221
222
  onEnded?: (sample: SampleStart) => void;
222
223
  };
223
224
 
224
- type DefaultPlayerConfig = ChannelConfig & SamplerConfig$1 & QueuedPlayerConfig;
225
+ type DefaultPlayerConfig = ChannelConfig & SamplerConfig & QueuedPlayerConfig;
225
226
 
226
227
  declare function getDrumMachineNames(): string[];
227
228
  type DrumMachineConfig = {
@@ -410,35 +411,6 @@ declare class Reverb {
410
411
  connect(output: AudioNode): void;
411
412
  }
412
413
 
413
- type SamplerConfig = {
414
- storage?: Storage;
415
- detune: number;
416
- volume: number;
417
- velocity: number;
418
- decayTime?: number;
419
- lpfCutoffHz?: number;
420
- destination: AudioNode;
421
- buffers: Record<string | number, string | AudioBuffers> | AudioBuffersLoader;
422
- volumeToGain: (volume: number) => number;
423
- };
424
- /**
425
- * A Sampler instrument
426
- *
427
- * @private
428
- */
429
- declare class Sampler {
430
- #private;
431
- readonly context: AudioContext;
432
- private readonly player;
433
- readonly load: Promise<this>;
434
- constructor(context: AudioContext, options?: Partial<SamplerConfig>);
435
- loaded(): Promise<this>;
436
- get output(): OutputChannel;
437
- start(sample: SampleStart | string | number): (time?: number | undefined) => void;
438
- stop(sample?: SampleStop | string | number): void;
439
- disconnect(): void;
440
- }
441
-
442
414
  declare function getSmolkenNames(): string[];
443
415
  type SmolkenConfig = {
444
416
  instrument: string;
@@ -523,4 +495,4 @@ declare const LAYERS: ({
523
495
  cutoff?: undefined;
524
496
  })[];
525
497
 
526
- export { CacheStorage, DrumMachine, DrumMachineOptions, 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 };
498
+ 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 };
package/dist/index.d.ts CHANGED
@@ -49,34 +49,6 @@ type AudioBuffers = Record<string | number, AudioBuffer | undefined>;
49
49
  */
50
50
  type AudioBuffersLoader = (context: BaseAudioContext, buffers: AudioBuffers) => Promise<void>;
51
51
 
52
- type StorageResponse$1 = {
53
- readonly status: number;
54
- arrayBuffer(): Promise<ArrayBuffer>;
55
- json(): Promise<any>;
56
- text(): Promise<string>;
57
- };
58
- type Storage$1 = {
59
- fetch: (url: string) => Promise<StorageResponse$1>;
60
- };
61
-
62
- type AudioBuffers$1 = Record<string | number, AudioBuffer | undefined>;
63
- /**
64
- * A function that downloads audio into a AudioBuffers
65
- */
66
- type AudioBuffersLoader$1 = (context: BaseAudioContext, buffers: AudioBuffers$1) => Promise<void>;
67
-
68
- type SamplerConfig$1 = {
69
- storage?: Storage$1;
70
- detune: number;
71
- volume: number;
72
- velocity: number;
73
- decayTime?: number;
74
- lpfCutoffHz?: number;
75
- destination: AudioNode;
76
- buffers: Record<string | number, string | AudioBuffers$1> | AudioBuffersLoader$1;
77
- volumeToGain: (volume: number) => number;
78
- };
79
-
80
52
  /**
81
53
  * A function to unsubscribe from an event or control
82
54
  */
@@ -214,6 +186,35 @@ type RegionGroup = {
214
186
  sample: Partial<SampleOptions>;
215
187
  };
216
188
 
189
+ type SamplerConfig = {
190
+ storage?: Storage;
191
+ detune: number;
192
+ volume: number;
193
+ velocity: number;
194
+ decayTime?: number;
195
+ lpfCutoffHz?: number;
196
+ destination: AudioNode;
197
+ buffers: Record<string | number, string | AudioBuffers> | AudioBuffersLoader;
198
+ volumeToGain: (volume: number) => number;
199
+ };
200
+ /**
201
+ * A Sampler instrument
202
+ *
203
+ * @private
204
+ */
205
+ declare class Sampler {
206
+ #private;
207
+ readonly context: AudioContext;
208
+ private readonly player;
209
+ readonly load: Promise<this>;
210
+ constructor(context: AudioContext, options?: Partial<SamplerConfig>);
211
+ loaded(): Promise<this>;
212
+ get output(): OutputChannel;
213
+ start(sample: SampleStart | string | number): (time?: number | undefined) => void;
214
+ stop(sample?: SampleStop | string | number): void;
215
+ disconnect(): void;
216
+ }
217
+
217
218
  type QueuedPlayerConfig = {
218
219
  scheduleLookaheadMs: number;
219
220
  scheduleIntervalMs: number;
@@ -221,7 +222,7 @@ type QueuedPlayerConfig = {
221
222
  onEnded?: (sample: SampleStart) => void;
222
223
  };
223
224
 
224
- type DefaultPlayerConfig = ChannelConfig & SamplerConfig$1 & QueuedPlayerConfig;
225
+ type DefaultPlayerConfig = ChannelConfig & SamplerConfig & QueuedPlayerConfig;
225
226
 
226
227
  declare function getDrumMachineNames(): string[];
227
228
  type DrumMachineConfig = {
@@ -410,35 +411,6 @@ declare class Reverb {
410
411
  connect(output: AudioNode): void;
411
412
  }
412
413
 
413
- type SamplerConfig = {
414
- storage?: Storage;
415
- detune: number;
416
- volume: number;
417
- velocity: number;
418
- decayTime?: number;
419
- lpfCutoffHz?: number;
420
- destination: AudioNode;
421
- buffers: Record<string | number, string | AudioBuffers> | AudioBuffersLoader;
422
- volumeToGain: (volume: number) => number;
423
- };
424
- /**
425
- * A Sampler instrument
426
- *
427
- * @private
428
- */
429
- declare class Sampler {
430
- #private;
431
- readonly context: AudioContext;
432
- private readonly player;
433
- readonly load: Promise<this>;
434
- constructor(context: AudioContext, options?: Partial<SamplerConfig>);
435
- loaded(): Promise<this>;
436
- get output(): OutputChannel;
437
- start(sample: SampleStart | string | number): (time?: number | undefined) => void;
438
- stop(sample?: SampleStop | string | number): void;
439
- disconnect(): void;
440
- }
441
-
442
414
  declare function getSmolkenNames(): string[];
443
415
  type SmolkenConfig = {
444
416
  instrument: string;
@@ -523,4 +495,4 @@ declare const LAYERS: ({
523
495
  cutoff?: undefined;
524
496
  })[];
525
497
 
526
- export { CacheStorage, DrumMachine, DrumMachineOptions, 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 };
498
+ 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 };
package/dist/index.js CHANGED
@@ -226,7 +226,7 @@ var Channel = class {
226
226
  if (__privateGet(this, _disconnected)) {
227
227
  throw Error("Can't add effect to disconnected channel");
228
228
  }
229
- const mix = new GainNode(this.context);
229
+ const mix = this.context.createGain();
230
230
  mix.gain.value = mixValue;
231
231
  const input = "input" in effect ? effect.input : effect;
232
232
  const disconnect = connectSerial([__privateGet(this, _volume), mix, input]);
@@ -446,12 +446,18 @@ var SamplePlayer = class {
446
446
  }
447
447
  const source = context.createBufferSource();
448
448
  source.buffer = buffer;
449
- source.detune.value = (_b = (_a = sample.detune) != null ? _a : this.options.detune) != null ? _b : 0;
449
+ const cents = (_b = (_a = sample.detune) != null ? _a : this.options.detune) != null ? _b : 0;
450
+ if (source.detune) {
451
+ source.detune.value = cents;
452
+ } else if (source.playbackRate) {
453
+ source.playbackRate.value = Math.pow(2, cents / 1200);
454
+ }
450
455
  const lpfCutoffHz = (_c = sample.lpfCutoffHz) != null ? _c : this.options.lpfCutoffHz;
451
- const lpf = lpfCutoffHz ? new BiquadFilterNode(context, {
452
- type: "lowpass",
453
- frequency: sample.lpfCutoffHz
454
- }) : void 0;
456
+ const lpf = lpfCutoffHz ? context.createBiquadFilter() : void 0;
457
+ if (lpfCutoffHz && lpf) {
458
+ lpf.type = "lowpass";
459
+ lpf.frequency.value = lpfCutoffHz;
460
+ }
455
461
  const volume = context.createGain();
456
462
  const velocity = (_e = (_d = sample.velocity) != null ? _d : this.options.velocity) != null ? _e : 100;
457
463
  volume.gain.value = __privateGet(this, _config3).velocityToGain(velocity);
@@ -472,7 +478,10 @@ var SamplePlayer = class {
472
478
  source.stop(stopAt);
473
479
  }
474
480
  }
475
- const gainCompensation = sample.gainOffset ? new GainNode(context, { gain: sample.gainOffset }) : void 0;
481
+ const gainCompensation = sample.gainOffset ? context.createGain() : void 0;
482
+ if (gainCompensation && sample.gainOffset) {
483
+ gainCompensation.gain.value = sample.gainOffset;
484
+ }
476
485
  const stopId = (_j = sample.stopId) != null ? _j : sample.note;
477
486
  const cleanup = unsubscribeAll([
478
487
  connectSerial([
@@ -525,7 +534,8 @@ _disconnected2 = new WeakMap();
525
534
  _stop = new WeakMap();
526
535
  function createDecayEnvelope(context, envelopeTime = 0.2) {
527
536
  let stopAt = 0;
528
- const envelope = new GainNode(context, { gain: 1 });
537
+ const envelope = context.createGain();
538
+ envelope.gain.value = 1;
529
539
  function start(time) {
530
540
  if (stopAt)
531
541
  return stopAt;
@@ -992,26 +1002,26 @@ startLayers_fn = function(sample) {
992
1002
 
993
1003
  // src/tremolo.ts
994
1004
  function createTremolo(context, depth) {
995
- const input = new GainNode(context);
996
- const output = new GainNode(context);
1005
+ const input = context.createGain();
1006
+ const output = context.createGain();
997
1007
  input.channelCount = 2;
998
1008
  input.channelCountMode = "explicit";
999
1009
  const splitter = new ChannelSplitterNode(context, { numberOfOutputs: 2 });
1000
- const ampL = new GainNode(context);
1001
- const ampR = new GainNode(context);
1010
+ const ampL = context.createGain();
1011
+ const ampR = context.createGain();
1002
1012
  const merger = new ChannelMergerNode(context, { numberOfInputs: 2 });
1003
1013
  const lfoL = new OscillatorNode(context, {
1004
1014
  type: "sine",
1005
1015
  frequency: 1
1006
1016
  });
1007
1017
  lfoL.start();
1008
- const lfoLAmp = new GainNode(context);
1018
+ const lfoLAmp = context.createGain();
1009
1019
  const lfoR = new OscillatorNode(context, {
1010
1020
  type: "sine",
1011
1021
  frequency: 1.1
1012
1022
  });
1013
1023
  lfoR.start();
1014
- const lfoRAmp = new GainNode(context);
1024
+ const lfoRAmp = context.createGain();
1015
1025
  input.connect(splitter);
1016
1026
  splitter.connect(ampL, 0);
1017
1027
  splitter.connect(ampR, 1);
@@ -2041,7 +2051,8 @@ var Soundfont = class {
2041
2051
  __privateSet(this, _hasLoops, hasLoops);
2042
2052
  return this;
2043
2053
  });
2044
- const gain = new GainNode(context, { gain: this.config.extraGain });
2054
+ const gain = context.createGain();
2055
+ gain.gain.value = this.config.extraGain;
2045
2056
  this.player.output.addInsert(gain);
2046
2057
  }
2047
2058
  get output() {