smplr
Version:
A Sampled collection of instruments
604 lines (579 loc) • 18.4 kB
TypeScript
type StorageResponse = {
readonly status: number;
arrayBuffer(): Promise<ArrayBuffer>;
json(): Promise<any>;
text(): Promise<string>;
};
type Storage = {
fetch: (url: string) => Promise<StorageResponse>;
};
declare const HttpStorage: Storage;
declare class CacheStorage implements Storage {
#private;
constructor(name?: string);
fetch(url: string): Promise<StorageResponse>;
}
type AudioBuffers = Record<string | number, AudioBuffer | undefined>;
/**
* A function that downloads audio into a AudioBuffers
*/
type AudioBuffersLoader = (context: BaseAudioContext, buffers: AudioBuffers) => Promise<void>;
/**
* A function to unsubscribe from an event or control
*/
type Unsubscribe = () => void;
/**
* A function that listener to event or control changes
*/
type Listener<T> = (value: T) => void;
/**
* A function to subscribe an trigger or control events
*/
type Subscribe<T> = (listener: Listener<T>) => Unsubscribe;
type StopFn = (time?: number) => void;
/**
* @private
*/
type InternalPlayer = {
readonly buffers: AudioBuffers;
readonly context: BaseAudioContext;
start(sample: SampleStart): StopFn;
stop(sample?: SampleStop): void;
disconnect(): void;
};
type SampleStop = {
stopId?: string | number;
time?: number;
};
/**
* Configurable options for a sample
*/
type SampleOptions = {
decayTime?: number;
detune?: number;
duration?: number | null;
velocity?: number;
lpfCutoffHz?: number;
loop?: boolean;
loopStart?: number;
loopEnd?: number;
gainOffset?: number;
};
/**
* Start a sample with a specific options
*/
type SampleStart = {
name?: string;
note: string | number;
onEnded?: (sample: SampleStart) => void;
onStart?: (sample: SampleStart) => void;
stop?: Subscribe<number>;
stopId?: string | number;
time?: number;
} & SampleOptions;
/**
* Represents a playable region for a sample.
*
* It provides constrains to limit which samples are affected,
* and defaults to apply to the affected samples.
*
*
* Heavily inspired by SFZ format
*/
type SampleRegion = {
sampleName: string;
/**
* This specifies the MIDI key number that corresponds to the sample's original pitch
*/
midiPitch: number;
/**
* This defines the lowest MIDI key number that will trigger this sample.
*/
midiLow?: number;
/**
* This specifies the highest MIDI key number that will trigger this sample.
*/
midiHigh?: number;
velLow?: number;
/**
* This determines the highest MIDI velocity at which this sample will be triggered.
*/
velHigh?: number;
/**
* These define the pitch bend range for the samples in this group. The values are given in cents
*/
bendUp?: number;
bendDown?: number;
/**
* Velocity-based amplitude scaling. [Vel, Gain] tells the sampler to play the sample
* at volume Gain when the note's velocity is Vel
*/
ampVelCurve?: [number, number];
/**
* Amplitude envelope release time in seconds.
* Stored here for convenience (flatness) but needs to be
* copied inside sample options before playback
*/
ampRelease?: number;
/**
* Attack time in seconds. Currently not implemented
*
* @see http://sfzformat.com/opcodes/amp_attack.html
*/
ampAttack?: number;
/**
* seqLength defines how many samples are in the sequence.
* When using the seqPosition and seqLength , you can set up round-robin
* or sequential sample playback.
*/
seqLength?: number;
/**
* Determine the position of this particular sample within the sequence
* 1 means first element of the sequence (not zero based!)
*/
seqPosition?: number;
/**
* This assigns the group to a specific number.
* Group numbers can be used in combination with groupOffBy to implement
* exclusive groups, where playing one sample can stop another sample from playing.
*/
group?: number;
/**
* Triggering a sample in this region will stop (or "turn off") any samples
* currently playing in the specified group number
*/
groupOffBy?: number;
/**
* Start offset (in samples). Not implemented (yet)
*/
offset?: number;
/**
* Adjust the playback pitch of a sample (in semitones)
*/
tune?: number;
/**
* The volume opcode in SFZ defines the default playback volume for a given region.
* It specifies an adjustment to the sample's original amplitude.
* The unit for the volume opcode is decibels (dB).
*/
volume?: number;
/**
* sample options for this particular region
*/
sample?: Partial<SampleOptions>;
};
type RegionGroup = {
regions: SampleRegion[];
};
type SamplerInstrument = {
groups: RegionGroup[];
options: Partial<SampleOptions>;
};
type AudioInsert = {
input: AudioNode;
output: AudioNode;
};
type ChannelConfig = {
destination: AudioNode;
volume: number;
volumeToGain: (volume: number) => number;
};
type OutputChannel = Omit<Channel, "input">;
/**
* An output channel with audio effects
* @private
*/
declare class Channel {
#private;
readonly context: BaseAudioContext;
readonly setVolume: (vol: number) => void;
readonly input: AudioNode;
constructor(context: BaseAudioContext, options?: Partial<ChannelConfig>);
addInsert(effect: AudioNode | AudioInsert): void;
addEffect(name: string, effect: AudioNode | {
input: AudioNode;
}, mixValue: number): void;
sendEffect(name: string, mix: number): void;
disconnect(): void;
}
type SamplerConfig = {
storage?: Storage;
detune: number;
volume: number;
velocity: number;
decayTime?: number;
lpfCutoffHz?: number;
destination: AudioNode;
buffers: Record<string | number, string | AudioBuffers> | AudioBuffersLoader;
volumeToGain: (volume: number) => number;
};
/**
* A Sampler instrument
*
* @private
*/
declare class Sampler {
#private;
readonly context: AudioContext;
private readonly player;
readonly load: Promise<this>;
constructor(context: AudioContext, options?: Partial<SamplerConfig>);
loaded(): Promise<this>;
get output(): OutputChannel;
start(sample: SampleStart | string | number): StopFn;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
type QueuedPlayerConfig = {
disableScheduler: boolean;
scheduleLookaheadMs: number;
scheduleIntervalMs: number;
onStart?: (sample: SampleStart) => void;
onEnded?: (sample: SampleStart) => void;
};
type DefaultPlayerConfig = ChannelConfig & SamplerConfig & QueuedPlayerConfig;
type DrumMachineInstrument = {
baseUrl: string;
name: string;
samples: string[];
groupNames: string[];
nameToSampleName: Record<string, string | undefined>;
sampleGroupVariations: Record<string, string[]>;
};
declare function getDrumMachineNames(): string[];
type DrumMachineConfig = {
instrument: string | DrumMachineInstrument;
url: string;
storage: Storage;
};
type DrumMachineOptions = Partial<DrumMachineConfig & DefaultPlayerConfig>;
declare class DrumMachine {
#private;
private readonly player;
readonly load: Promise<this>;
readonly output: OutputChannel;
constructor(context: AudioContext, options?: DrumMachineOptions);
getSampleNames(): string[];
getGroupNames(): string[];
getSampleNamesForGroup(groupName: string): string[];
start(sample: SampleStart): StopFn;
stop(sample: SampleStop): void;
/** @deprecated */
loaded(): Promise<this>;
/** @deprecated */
get sampleNames(): string[];
/** @deprecated */
getVariations(groupName: string): string[];
}
type SfzInstrument = {
name: string;
formats?: string[];
baseUrl?: string;
websfzUrl: string;
tags?: string[];
};
type Websfz = {
global: Record<string, string | number>;
groups: WebsfzGroup[];
meta: {
name?: string;
description?: string;
license?: string;
source?: string;
baseUrl?: string;
websfzUrl?: string;
formats?: string[];
tags?: string[];
};
};
type WebsfzGroup = {
group_label?: string;
group?: number;
hikey?: number;
hivel?: number;
lokey?: number;
lovel?: number;
off_by?: number;
off_mode?: "normal";
pitch_keycenter?: number;
regions: WebsfzRegion[];
seq_length?: number;
trigger?: "first" | "legato";
volume?: number;
amp_velcurve_83?: number;
locc64?: number;
hicc64?: number;
hicc107?: number;
locc107?: number;
pan_oncc122?: number;
tune_oncc123?: number;
eg06_time1_oncc109?: number;
ampeg_attack_oncc100?: number;
};
type WebsfzRegion = {
end?: number;
group?: number;
hivel?: number;
lovel?: number;
hikey?: number;
key?: number;
lokey?: number;
off_by?: number;
pitch_keycenter?: number;
region_label?: number;
sample: string;
seq_position?: number;
trigger?: "first" | "legato";
volume?: number;
locc64?: number;
hicc64?: number;
ampeg_attack_oncc100?: number;
eg06_time1_oncc109?: number;
pan_oncc122?: number;
tune_oncc123?: number;
};
type SfzSamplerConfig = {
instrument: SfzInstrument | Websfz | string;
storage: Storage;
destination: AudioNode;
volume: number;
velocity: number;
detune: number;
decayTime?: number;
lpfCutoffHz?: number;
};
declare class SfzSampler {
#private;
readonly context: AudioContext;
readonly options: Readonly<SfzSamplerConfig>;
private readonly player;
readonly load: Promise<this>;
constructor(context: AudioContext, options: Partial<SfzSamplerConfig & DefaultPlayerConfig> & Pick<SfzSamplerConfig, "instrument">);
get output(): OutputChannel;
loaded(): Promise<this>;
start(sample: SampleStart | string | number): void;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
declare function getElectricPianoNames(): string[];
declare class ElectricPiano extends SfzSampler {
readonly tremolo: Readonly<{
level: (value: number) => void;
}>;
constructor(context: AudioContext, options: Partial<SfzSamplerConfig> & {
instrument: string;
});
}
declare function getVersilianInstruments(): Promise<string[]>;
declare function VcslInstrumentLoader(instrument: string, buffers: AudioBuffers): (context: BaseAudioContext, storage: Storage) => Promise<RegionGroup>;
type VersilianConfig = {
instrument: string;
storage: Storage;
};
type VersilianOptions = Partial<VersilianConfig & DefaultPlayerConfig>;
/**
* Versilian
*
* The Versilian Community Sample Library is an open CC0 general-purpose sample library created by Versilian Studios LLC
* for the purpose of introducing a set of quality, publicly available samples suitable for use in software and media of all kinds.
*/
declare class Versilian implements InternalPlayer {
private readonly player;
readonly load: Promise<this>;
private config;
constructor(context: BaseAudioContext, options?: VersilianOptions);
get output(): OutputChannel;
get buffers(): AudioBuffers;
get context(): BaseAudioContext;
start(sample: SampleStart | string | number): (time?: number) => void;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
declare function getMalletNames(): MalletName[];
declare class Mallet extends Versilian {
constructor(context: AudioContext, options: VersilianOptions);
}
type MalletName = keyof typeof NAME_TO_PATH;
declare const NAME_TO_PATH: Record<string, string | undefined>;
declare function getMellotronNames(): string[];
type MellotronConfig = {
instrument: string;
storage: Storage;
};
type MellotronOptions = Partial<MellotronConfig & DefaultPlayerConfig>;
declare class Mellotron implements InternalPlayer {
readonly context: BaseAudioContext;
private readonly options;
private readonly config;
private readonly player;
private readonly group;
readonly load: Promise<this>;
constructor(context: BaseAudioContext, options: MellotronOptions);
get buffers(): AudioBuffers;
get output(): OutputChannel;
start(sample: SampleStart | string | number): StopFn;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
declare const PARAMS: readonly ["preDelay", "bandwidth", "inputDiffusion1", "inputDiffusion2", "decay", "decayDiffusion1", "decayDiffusion2", "damping", "excursionRate", "excursionDepth", "wet", "dry"];
declare class Reverb {
#private;
readonly input: AudioNode;
constructor(context: AudioContext);
get paramNames(): readonly ["preDelay", "bandwidth", "inputDiffusion1", "inputDiffusion2", "decay", "decayDiffusion1", "decayDiffusion2", "damping", "excursionRate", "excursionDepth", "wet", "dry"];
getParam(name: (typeof PARAMS)[number]): AudioParam | undefined;
get isReady(): boolean;
ready(): Promise<this>;
connect(output: AudioNode): void;
}
declare function getSmolkenNames(): string[];
type SmolkenConfig = {
instrument: string;
storage: Storage;
};
type SmolkenOptions = Partial<SmolkenConfig & DefaultPlayerConfig>;
declare class Smolken implements InternalPlayer {
private readonly player;
private readonly group;
readonly load: Promise<this>;
private config;
private seqNum;
constructor(context: BaseAudioContext, options?: SmolkenOptions);
get output(): OutputChannel;
get buffers(): AudioBuffers;
get context(): BaseAudioContext;
start(sample: SampleStart | string | number): (time?: number) => void;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
declare function getSoundfontKits(): string[];
declare function getSoundfontNames(): string[];
type SoundfontConfig = {
kit: "FluidR3_GM" | "MusyngKite" | string;
instrument?: string;
instrumentUrl: string;
storage: Storage;
extraGain: number;
loadLoopData: boolean;
loopDataUrl?: string;
};
type SoundfontOptions = Partial<SoundfontConfig & DefaultPlayerConfig>;
declare class Soundfont {
#private;
readonly context: AudioContext;
readonly config: Readonly<SoundfontConfig>;
private readonly player;
readonly load: Promise<this>;
readonly group: RegionGroup;
constructor(context: AudioContext, options: SoundfontOptions);
get output(): OutputChannel;
get hasLoops(): boolean;
loaded(): Promise<this>;
disconnect(): void;
start(sample: SampleStart | string | number): StopFn;
stop(sample?: SampleStop | string | number): void;
}
type RegionPlayerOptions = ChannelConfig & SampleOptions & QueuedPlayerConfig;
/**
* A player with an channel output and a region group to read samples info from
* @private
*/
declare class RegionPlayer implements InternalPlayer {
readonly context: BaseAudioContext;
readonly output: OutputChannel;
instrument: SamplerInstrument;
private readonly player;
private seqNum;
constructor(context: BaseAudioContext, options: Partial<RegionPlayerOptions>);
get buffers(): AudioBuffers;
start(sample: SampleStart | string | number): (time?: number) => void;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
type Sf2 = {
instruments: Sf2Instrument[];
};
type Sf2Instrument = {
header: {
name: string;
};
zones: Sf2Zone[];
};
type Sf2Zone = {
sample: Sf2Sample;
keyRange?: {
lo: number;
hi: number;
};
};
type Sf2Sample = {
data: Int16Array;
header: {
name: string;
sampleRate: number;
originalPitch: number;
pitchCorrection: number;
start: number;
end: number;
startLoop: number;
endLoop: number;
};
};
type Soundfont2Options = Partial<RegionPlayerOptions> & {
url: string;
createSoundfont: (data: Uint8Array) => Sf2;
};
declare class Soundfont2Sampler {
#private;
readonly context: AudioContext;
readonly options: Soundfont2Options;
player: RegionPlayer;
soundfont: Sf2 | undefined;
load: Promise<this>;
constructor(context: AudioContext, options: Soundfont2Options);
get instrumentNames(): string[];
loadInstrument(instrumentName: string): (AudioBuffers | SamplerInstrument)[] | undefined;
get output(): OutputChannel;
start(sample: SampleStart | string | number): (time?: number) => void;
stop(sample?: SampleStop | string | number): void;
disconnect(): void;
}
/**
* Splendid Grand Piano options
*/
type SplendidGrandPianoConfig = {
baseUrl: string;
storage: Storage;
detune: number;
velocity: number;
decayTime: number;
notesToLoad?: {
notes: number[];
velocityRange: [number, number];
};
} & Partial<DefaultPlayerConfig>;
declare class SplendidGrandPiano {
#private;
readonly context: AudioContext;
options: Readonly<SplendidGrandPianoConfig>;
private readonly player;
readonly load: Promise<this>;
constructor(context: AudioContext, options?: Partial<SplendidGrandPianoConfig>);
get output(): OutputChannel;
get buffers(): AudioBuffers;
loaded(): Promise<this>;
start(sampleOrNote: SampleStart | number | string): StopFn;
stop(sample?: SampleStop | number | string): void;
}
declare const LAYERS: ({
name: string;
vel_range: number[];
cutoff: number;
samples: (string | number)[][];
} | {
name: string;
vel_range: number[];
samples: (string | number)[][];
cutoff?: undefined;
})[];
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 };