smplr 0.18.1 → 0.19.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/dist/index.d.mts CHANGED
@@ -7,6 +7,7 @@ type ChannelConfig = {
7
7
  destination: AudioNode;
8
8
  volume: number;
9
9
  volumeToGain: (volume: number) => number;
10
+ pan?: number;
10
11
  };
11
12
  type OutputChannel = Omit<Channel, "input">;
12
13
  /**
@@ -19,6 +20,8 @@ declare class Channel {
19
20
  readonly setVolume: (vol: number) => void;
20
21
  readonly input: AudioNode;
21
22
  constructor(context: BaseAudioContext, options?: Partial<ChannelConfig>);
23
+ get pan(): number;
24
+ set pan(value: number);
22
25
  addInsert(effect: AudioNode | AudioInsert): void;
23
26
  addEffect(name: string, effect: AudioNode | {
24
27
  input: AudioNode;
@@ -58,6 +61,7 @@ type PlaybackParams = {
58
61
  loop?: boolean;
59
62
  loopStart?: number;
60
63
  loopEnd?: number;
64
+ reverse?: boolean;
61
65
  };
62
66
  /**
63
67
  * An individual sample region. Maps a sample to a range of notes and velocities.
@@ -122,7 +126,7 @@ type SmplrJson = {
122
126
  /**
123
127
  * A note event passed to Smplr.start(). Can be a full object, a note name, or a MIDI number.
124
128
  */
125
- type NoteEvent$1 = {
129
+ type NoteEvent = {
126
130
  note: string | number;
127
131
  velocity?: number;
128
132
  time?: number;
@@ -132,8 +136,9 @@ type NoteEvent$1 = {
132
136
  loop?: boolean;
133
137
  ampRelease?: number;
134
138
  stopId?: string | number;
135
- onStart?: (event: NoteEvent$1) => void;
136
- onEnded?: (event: NoteEvent$1) => void;
139
+ onStart?: (event: NoteEvent) => void;
140
+ onEnded?: (event: NoteEvent) => void;
141
+ reverse?: boolean;
137
142
  } | string | number;
138
143
  /**
139
144
  * Target for Smplr.stop(). Can be a full object, a stopId, or a MIDI number.
@@ -153,6 +158,166 @@ type LoadProgress = {
153
158
  loaded: number;
154
159
  total: number;
155
160
  };
161
+ /**
162
+ * Fully resolved playback parameters for a single Voice.
163
+ * Output of resolveParams() — all fields are required, no optionals except ampVelCurve/loopAuto.
164
+ */
165
+ type VoiceParams = {
166
+ detune: number;
167
+ velocity: number;
168
+ volume: number;
169
+ ampRelease: number;
170
+ ampAttack: number;
171
+ lpfCutoffHz: number;
172
+ offset: number;
173
+ loop: boolean;
174
+ loopStart: number;
175
+ loopEnd: number;
176
+ ampVelCurve?: [number, number];
177
+ /** If set, loop points are computed from buffer.duration at play time. */
178
+ loopAuto?: {
179
+ startRatio: number;
180
+ endRatio: number;
181
+ };
182
+ reverse?: boolean;
183
+ };
184
+
185
+ /**
186
+ * Loads and caches AudioBuffers for all samples referenced in a SmplrJson.
187
+ *
188
+ * The cache is keyed by resolved URL, so the same audio file is never fetched
189
+ * or decoded twice. Multiple Smplr instances can share one SampleLoader by
190
+ * passing it via SmplrOptions.loader.
191
+ */
192
+ declare class SampleLoader {
193
+ #private;
194
+ constructor(context: BaseAudioContext, options?: {
195
+ storage?: Storage;
196
+ });
197
+ /**
198
+ * Load all samples referenced in `json`. Returns a Map of sample name →
199
+ * AudioBuffer. Progress is reported via `onProgress` callback or via
200
+ * options object.
201
+ *
202
+ * - `buffers` in options: pre-loaded buffers — skips fetch for these names.
203
+ * - All samples load in parallel. Failed samples are silently omitted.
204
+ */
205
+ load(json: SmplrJson, onProgressOrOptions?: ((loaded: number, total: number) => void) | {
206
+ buffers?: Map<string, AudioBuffer>;
207
+ onProgress?: (loaded: number, total: number) => void;
208
+ }): Promise<Map<string, AudioBuffer>>;
209
+ }
210
+
211
+ /**
212
+ * Standalone scheduler. Dispatches NoteEvents immediately when they fall within the
213
+ * lookahead window, or queues them for future dispatch via a self-managing interval.
214
+ *
215
+ * Multiple Smplr instances can share a single Scheduler for coordinated timing.
216
+ */
217
+ declare class Scheduler {
218
+ #private;
219
+ constructor(context: BaseAudioContext, options?: {
220
+ lookaheadMs?: number;
221
+ intervalMs?: number;
222
+ });
223
+ /**
224
+ * Schedule a callback for a NoteEvent.
225
+ *
226
+ * - If the event's time falls within the lookahead window (or has no time), the
227
+ * callback is called synchronously and a no-op StopFn is returned.
228
+ * - Otherwise the event is queued, the interval is started if needed, and a StopFn
229
+ * is returned that removes the event from the queue before it is dispatched.
230
+ */
231
+ schedule(event: NoteEvent, callback: (event: NoteEvent) => void): StopFn;
232
+ /**
233
+ * Clear all queued (not-yet-dispatched) events and stop the interval.
234
+ * Does not affect voices that are already playing.
235
+ */
236
+ stop(): void;
237
+ }
238
+
239
+ type SmplrOptions = {
240
+ /** Custom storage backend for sample fetching (e.g. CacheStorage). */
241
+ storage?: Storage;
242
+ /** Destination audio node. Defaults to context.destination. */
243
+ destination?: AudioNode;
244
+ /** Master volume (0–127 MIDI scale). Defaults to 100. */
245
+ volume?: number;
246
+ /** Custom volume-to-gain mapping function. Defaults to midiVelToGain. */
247
+ volumeToGain?: (volume: number) => number;
248
+ /** Stereo pan position (-1 = full left, 0 = centre, +1 = full right). Defaults to 0. */
249
+ pan?: number;
250
+ /** Default note velocity when not specified in NoteEvent (0–127). Defaults to 100. */
251
+ velocity?: number;
252
+ /** Shared SampleLoader instance. If omitted, a private one is created. */
253
+ loader?: SampleLoader;
254
+ /** Shared Scheduler instance. If omitted, a private one is created. */
255
+ scheduler?: Scheduler;
256
+ /** Called after each buffer is loaded (or served from cache). */
257
+ onLoadProgress?: (progress: LoadProgress) => void;
258
+ /** Called when a note is dispatched to the audio engine (slightly before playback). */
259
+ onStart?: (event: NoteEvent) => void;
260
+ /** Called when each voice's audio node ends. */
261
+ onEnded?: (event: NoteEvent) => void;
262
+ };
263
+ /**
264
+ * The main sampler class. Loads samples described by a SmplrJson descriptor,
265
+ * matches notes to regions, and plays them through a Channel.
266
+ *
267
+ * Multiple Smplr instances can share a SampleLoader (shared cache) and/or a
268
+ * Scheduler (coordinated timing) by passing them via SmplrOptions.
269
+ *
270
+ * Pattern A — json provided at construction:
271
+ * `new Smplr(context, json, options?)`
272
+ *
273
+ * Pattern B — json loaded later via loadInstrument():
274
+ * `new Smplr(context, options?)` then `smplr.loadInstrument(json)`
275
+ */
276
+ declare class Smplr {
277
+ #private;
278
+ /** Resolves with `this` once all sample buffers are loaded. */
279
+ readonly load: Promise<Smplr>;
280
+ /** The AudioContext passed to the constructor. */
281
+ readonly context: AudioContext;
282
+ constructor(context: AudioContext, json: SmplrJson, options?: SmplrOptions);
283
+ constructor(context: AudioContext, options?: SmplrOptions);
284
+ /**
285
+ * Load (or replace) the instrument descriptor. Creates a new RegionMatcher
286
+ * and fetches all sample buffers. Pre-loaded buffers (e.g. base64-decoded)
287
+ * can be passed via the `buffers` parameter — those skip the fetch step.
288
+ *
289
+ * Returns a Promise that resolves when all samples are ready.
290
+ */
291
+ loadInstrument(json: SmplrJson, buffers?: Map<string, AudioBuffer>): Promise<void>;
292
+ /** Current loading progress snapshot. `total` is known before loading starts. */
293
+ get loadProgress(): LoadProgress;
294
+ /** The output channel — use to add effects, adjust volume, or route audio. */
295
+ get output(): OutputChannel;
296
+ /**
297
+ * Set a MIDI CC value. Affects region matching for groups/regions that have
298
+ * ccRange constraints (e.g. CC64 sustain pedal).
299
+ */
300
+ setCC(cc: number, value: number): void;
301
+ /**
302
+ * Start playing a note. Returns a StopFn that cancels the note if it hasn't
303
+ * played yet, or stops the resulting voices if it has.
304
+ */
305
+ start(event: NoteEvent): StopFn;
306
+ /**
307
+ * Stop voices.
308
+ *
309
+ * - No argument → stop all active voices
310
+ * - String or number → stop all voices with that stopId
311
+ * - `{ stopId }` → stop voices with that stopId, optionally at a future time
312
+ * - `{ time }` (no stopId) → stop all voices at a future time
313
+ */
314
+ stop(target?: StopTarget): void;
315
+ /**
316
+ * Stop all voices, disconnect the output channel, and stop the scheduler.
317
+ * The instance should not be used after this call.
318
+ */
319
+ disconnect(): void;
320
+ }
156
321
 
157
322
  /**
158
323
  * Given a list of [midi, sampleName] pairs, return one entry per sample with
@@ -188,6 +353,7 @@ type DrumMachineConfig = {
188
353
  type DrumMachineOptions = Partial<DrumMachineConfig & {
189
354
  destination?: AudioNode;
190
355
  volume?: number;
356
+ pan?: number;
191
357
  velocity?: number;
192
358
  onLoadProgress?: (progress: LoadProgress) => void;
193
359
  }>;
@@ -199,7 +365,7 @@ declare class DrumMachine {
199
365
  getSampleNames(): string[];
200
366
  getGroupNames(): string[];
201
367
  getSampleNamesForGroup(groupName: string): string[];
202
- start(sample: NoteEvent$1): StopFn;
368
+ start(sample: NoteEvent): StopFn;
203
369
  stop(sample?: StopTarget): void;
204
370
  disconnect(): void;
205
371
  /** @deprecated */
@@ -246,6 +412,8 @@ type SequencerNote = {
246
412
  /** Note duration: ticks, "4n", "8n", etc. Omit for a one-shot trigger. */
247
413
  duration?: string | number;
248
414
  velocity?: number;
415
+ /** Probability (0–100) that this note fires on each pass. Default 100 (always). */
416
+ chance?: number;
249
417
  };
250
418
  /**
251
419
  * Any instrument the Sequencer can drive.
@@ -264,7 +432,7 @@ type SequencerInstrument = {
264
432
  }): unknown;
265
433
  };
266
434
  /** Emitted with "noteOn" and "noteOff" events. */
267
- type NoteEvent = {
435
+ type SequencerNoteEvent = {
268
436
  noteId: string | number;
269
437
  trackIndex: number;
270
438
  noteIndex: number;
@@ -286,12 +454,15 @@ type SequencerOptions = {
286
454
  timingMs?: number;
287
455
  velocity?: number;
288
456
  };
457
+ /** Emit a "step" event at this interval. Accepts musical notation or ticks: "16n", "8n", ticks, etc. */
458
+ stepSize?: string | number;
289
459
  };
290
460
  declare class Sequencer {
291
461
  private readonly _context;
292
462
  private readonly _clock;
293
463
  private readonly _ppq;
294
464
  private _timeSignature;
465
+ private _stepTicks;
295
466
  private _tracks;
296
467
  private _repeatEvents;
297
468
  private _listeners;
@@ -377,8 +548,9 @@ declare class Sequencer {
377
548
  * | "loop" | |
378
549
  * | "beat" | (beat: number, time: number) |
379
550
  * | "bar" | (bar: number, time: number) |
380
- * | "noteOn" | (event: NoteEvent) |
381
- * | "noteOff" | (event: NoteEvent) |
551
+ * | "step" | (stepIndex: number, time: number) |
552
+ * | "noteOn" | (event: SequencerNoteEvent) |
553
+ * | "noteOff" | (event: SequencerNoteEvent) |
382
554
  */
383
555
  on(event: string, callback: (...args: any[]) => void): this;
384
556
  off(event: string, callback: (...args: any[]) => void): this;
@@ -386,6 +558,7 @@ declare class Sequencer {
386
558
  private _stopLoop;
387
559
  private _flush;
388
560
  private _scheduleWindow;
561
+ private _emitStepsInWindow;
389
562
  private _emitBeatsInWindow;
390
563
  private _emit;
391
564
  /** Emit both the specific state event ("start"/"pause"/"stop") and the unified "statechange" event. */
@@ -424,7 +597,7 @@ declare class ElectricPiano {
424
597
  });
425
598
  get output(): OutputChannel;
426
599
  get loadProgress(): LoadProgress;
427
- start(sample: NoteEvent$1 | string | number): StopFn;
600
+ start(sample: NoteEvent | string | number): StopFn;
428
601
  stop(target?: StopTarget): void;
429
602
  disconnect(): void;
430
603
  }
@@ -453,7 +626,7 @@ declare class Versilian {
453
626
  constructor(context: BaseAudioContext, options?: VersilianOptions);
454
627
  get output(): OutputChannel;
455
628
  get loadProgress(): LoadProgress;
456
- start(sample: NoteEvent$1 | string | number): StopFn;
629
+ start(sample: NoteEvent | string | number): StopFn;
457
630
  stop(target?: StopTarget): void;
458
631
  disconnect(): void;
459
632
  }
@@ -484,7 +657,7 @@ declare class Mellotron {
484
657
  constructor(context: BaseAudioContext, options?: MellotronOptions);
485
658
  get output(): OutputChannel;
486
659
  get loadProgress(): LoadProgress;
487
- start(sample: NoteEvent$1 | string | number): StopFn;
660
+ start(sample: NoteEvent | string | number): StopFn;
488
661
  stop(target?: StopTarget): void;
489
662
  disconnect(): void;
490
663
  }
@@ -542,7 +715,7 @@ declare class Sampler {
542
715
  constructor(context: AudioContext, options?: Partial<SamplerConfig>);
543
716
  loaded(): Promise<this>;
544
717
  get output(): OutputChannel;
545
- start(sample: NoteEvent$1 | string | number): StopFn;
718
+ start(sample: NoteEvent | string | number): StopFn;
546
719
  stop(sample?: StopTarget | string | number): void;
547
720
  disconnect(): void;
548
721
  }
@@ -582,7 +755,7 @@ declare class Smolken {
582
755
  constructor(context: BaseAudioContext, options?: SmolkenOptions);
583
756
  get output(): OutputChannel;
584
757
  get loadProgress(): LoadProgress;
585
- start(sample: NoteEvent$1 | string | number): StopFn;
758
+ start(sample: NoteEvent | string | number): StopFn;
586
759
  stop(target?: StopTarget): void;
587
760
  disconnect(): void;
588
761
  }
@@ -616,7 +789,7 @@ declare class Soundfont {
616
789
  get output(): OutputChannel;
617
790
  loaded(): Promise<this>;
618
791
  disconnect(): void;
619
- start(sample: NoteEvent$1 | string | number): StopFn;
792
+ start(sample: NoteEvent | string | number): StopFn;
620
793
  stop(sample?: StopTarget | string | number): void;
621
794
  }
622
795
  /**
@@ -675,7 +848,7 @@ declare class Soundfont2Sampler {
675
848
  get instrumentNames(): string[];
676
849
  get output(): OutputChannel;
677
850
  loadInstrument(instrumentName: string): Promise<void> | undefined;
678
- start(sample: NoteEvent$1 | string | number): StopFn;
851
+ start(sample: NoteEvent | string | number): StopFn;
679
852
  stop(sample?: StopTarget | string | number): void;
680
853
  disconnect(): void;
681
854
  }
@@ -715,7 +888,7 @@ declare class SplendidGrandPiano {
715
888
  get loadProgress(): LoadProgress;
716
889
  /** @deprecated Use `load` instead. */
717
890
  loaded(): Promise<this>;
718
- start(event: NoteEvent$1): StopFn;
891
+ start(event: NoteEvent): StopFn;
719
892
  stop(target?: StopTarget): void;
720
893
  disconnect(): void;
721
894
  }
@@ -744,4 +917,4 @@ declare const LAYERS: ({
744
917
  cutoff?: undefined;
745
918
  })[];
746
919
 
747
- export { CacheStorage, DrumMachine, type DrumMachineOptions, ElectricPiano, type ElectricPianoOptions, HttpStorage, LAYERS, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, type NoteEvent, Reverb, Sampler, type SamplerConfig, Sequencer, type SequencerInstrument, type SequencerNote, type SequencerOptions, Smolken, type SmolkenConfig, type SmolkenOptions, Soundfont, type Soundfont2Options, Soundfont2Sampler, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type Storage, type StorageResponse, Versilian, type VersilianConfig, type VersilianOptions, drumMachineToSmplrJson, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments, mellotronToSmplrJson, pianoToSmplrJson, samplerToSmplrJson, sf2InstrumentToSmplrJson, soundfontToSmplrJson, spreadKeyRanges };
920
+ export { CacheStorage, DrumMachine, type DrumMachineOptions, ElectricPiano, type ElectricPianoOptions, HttpStorage, LAYERS, type LoadProgress, Mallet, Mellotron, type MellotronConfig, type MellotronOptions, NAME_TO_PATH, type NoteEvent, type PlaybackParams, Reverb, SampleLoader, Sampler, type SamplerConfig, Scheduler, Sequencer, type SequencerInstrument, type SequencerNote, type SequencerNoteEvent, type SequencerOptions, Smolken, type SmolkenConfig, type SmolkenOptions, Smplr, type SmplrGroup, type SmplrJson, type SmplrOptions, type SmplrRegion, type SmplrSamples, Soundfont, type Soundfont2Options, Soundfont2Sampler, type SoundfontOptions, SplendidGrandPiano, type SplendidGrandPianoConfig, type SpreadResult, type StopFn, type StopTarget, type Storage, type StorageResponse, Versilian, type VersilianConfig, type VersilianOptions, type VoiceParams, drumMachineToSmplrJson, getDrumMachineNames, getElectricPianoNames, getMalletNames, getMellotronNames, getSmolkenNames, getSoundfontKits, getSoundfontNames, getVersilianInstruments, mellotronToSmplrJson, pianoToSmplrJson, samplerToSmplrJson, sf2InstrumentToSmplrJson, soundfontToSmplrJson, spreadKeyRanges };