spessasynth_lib
Version:
MIDI and SoundFont2/DLS library for the browsers with no compromises
1,346 lines (1,319 loc) • 45.8 kB
TypeScript
import { MIDIPatch, KeyModifier, SoundBankManagerListEntry, SynthProcessorEventData, BasicMIDI, MIDITrack, SequencerEvent, MIDIMessage, SpessaSynthProcessor, SpessaSynthSequencer, SynthProcessorOptions, BasicSoundBank, SoundFont2WriteOptions, DLSWriteOptions, RMIDIWriteOptions, SynthesizerSnapshot, SynthMethodOptions, CustomController, MIDIController, MasterParameterType, SynthProcessorEvent, ChannelSnapshot, ChannelProperty, PresetList, WaveWriteOptions } from 'spessasynth_core';
declare class WorkletKeyModifierManagerWrapper {
private keyModifiers;
private synth;
constructor(synth: BasicSynthesizer);
/**
* Modifies a single key.
* @param channel The channel affected. Usually 0-15.
* @param midiNote The MIDI note to change. 0-127.
* @param options The key's modifiers.
*/
addModifier(channel: number, midiNote: number, options: Partial<{
velocity: number;
patch: MIDIPatch;
gain: number;
}>): void;
/**
* Gets a key modifier.
* @param channel The channel affected. Usually 0-15.
* @param midiNote The MIDI note to change. 0-127.
* @returns The key modifier if it exists.
*/
getModifier(channel: number, midiNote: number): KeyModifier | undefined;
/**
* Deletes a key modifier.
* @param channel The channel affected. Usually 0-15.
* @param midiNote The MIDI note to change. 0-127.
*/
deleteModifier(channel: number, midiNote: number): void;
/**
* Clears ALL Modifiers
*/
clearModifiers(): void;
private sendToWorklet;
}
type LibSBKManagerEntry = Omit<SoundBankManagerListEntry, "soundBank">;
declare class SoundBankManager {
/**
* All the sound banks, ordered from the most important to the least.
*/
soundBankList: LibSBKManagerEntry[];
private synth;
/**
* Creates a new instance of the sound bank manager.
*/
constructor(synth: BasicSynthesizer);
/**
* The current sound bank priority order.
* @returns The IDs of the sound banks in the current order.
*/
get priorityOrder(): string[];
/**
* Rearranges the sound banks in a given order.
* @param newList The order of sound banks, a list of identifiers, first overwrites second.
*/
set priorityOrder(newList: string[]);
/**
* Adds a new sound bank buffer with a given ID.
* @param soundBankBuffer The sound bank's buffer
* @param id The sound bank's unique identifier.
* @param bankOffset The sound bank's bank offset. Default is 0.
*/
addSoundBank(soundBankBuffer: ArrayBuffer, id: string, bankOffset?: number): Promise<void>;
/**
* Deletes a sound bank with the given ID.
* @param id The sound bank to delete.
*/
deleteSoundBank(id: string): Promise<void>;
private awaitResponse;
private sendToWorklet;
}
type ProcessorEventCallback<T extends keyof SynthProcessorEventData> = (callbackData: SynthProcessorEventData[T]) => unknown;
declare class SynthEventHandler {
/**
* The time delay before an event is called.
* Set to 0 to disable it.
*/
timeDelay: number;
/**
* The main list of events.
* @private
*/
private readonly events;
/**
* Adds a new event listener.
* @param event The event to listen to.
* @param id The unique identifier for the event. It can be used to overwrite existing callback with the same ID.
* @param callback The callback for the event.
*/
addEvent<T extends keyof SynthProcessorEventData>(event: T, id: string, callback: ProcessorEventCallback<T>): void;
/**
* Removes an event listener
* @param name The event to remove a listener from.
* @param id The unique identifier for the event to remove.
*/
removeEvent<T extends keyof SynthProcessorEventData>(name: T, id: string): void;
/**
* Calls the given event.
* INTERNAL USE ONLY!
*/
callEventInternal<T extends keyof SynthProcessorEventData>(name: T, eventData: SynthProcessorEventData[T]): void;
}
declare const songChangeType: {
readonly shuffleOn: 1;
readonly shuffleOff: 2;
readonly index: 3;
};
type SongChangeType = (typeof songChangeType)[keyof typeof songChangeType];
declare class MIDIDataTrack extends MIDITrack {
/**
* THIS DATA WILL BE EMPTY! USE sequencer.getMIDI() TO GET THE ACTUAL DATA!
*/
events: never[];
constructor(track: MIDITrack);
}
/**
* A simplified version of the MIDI, accessible at all times from the Sequencer.
* Use getMIDI() to get the actual sequence.
* This class contains all properties that MIDI does, except for tracks and the embedded sound bank.
*/
declare class MIDIData extends BasicMIDI {
tracks: MIDIDataTrack[];
/**
* THIS DATA WILL BE EMPTY! USE sequencer.getMIDI() TO GET THE ACTUAL DATA!
*/
embeddedSoundBank: undefined;
/**
* The byte length of the sound bank if it exists.
*/
readonly embeddedSoundBankSize?: number;
constructor(mid: BasicMIDI);
}
interface SequencerOptions {
/**
* If true, the sequencer will skip to the first note.
*/
skipToFirstNoteOn: boolean;
/**
* The initial playback rate, defaults to 1.0 (normal speed).
*/
initialPlaybackRate: number;
}
type SequencerMessage = {
[K in keyof SequencerMessageData]: {
type: K;
data: SequencerMessageData[K];
};
}[keyof SequencerMessageData];
interface SequencerMessageData {
loadNewSongList: SuppliedMIDIData[];
pause: null;
play: null;
setTime: number;
changeMIDIMessageSending: boolean;
setPlaybackRate: number;
setLoopCount: number;
changeSong: {
changeType: SongChangeType;
data?: number;
};
getMIDI: null;
setSkipToFirstNote: boolean;
}
type SequencerReturnMessage = SequencerEvent | {
type: "getMIDI";
data: BasicMIDI;
} | {
type: "midiError";
data: Error;
};
/**
* Sequencer.js
* purpose: plays back the midi file decoded by midi_loader.js, including support for multichannel midis
* (adding channels when more than one midi port is detected)
* note: this is the sequencer class that runs on the main thread
* and only communicates with the worklet sequencer which does the actual playback
*/
type SuppliedMIDIData = BasicMIDI | {
/**
* The binary data of the file.
*/
binary: ArrayBuffer;
/**
* The file name of this file as a fallback.
*/
fileName?: string;
};
interface WorkletSequencerEventType {
/**
* New song.
*/
songChange: MIDIData;
/**
* New time.
*/
timeChange: number;
/**
* No data.
*/
songEnded: null;
metaEvent: {
event: MIDIMessage;
trackNumber: number;
};
textEvent: {
/**
* The raw event.
*/
event: MIDIMessage;
/**
* If the text is a lyric, the index of the lyric in BasicMIDI's "lyrics" property, otherwise -1.
*/
lyricsIndex: number;
};
midiError: Error;
}
type PostMessageSynthCore = (data: BasicSynthesizerReturnMessage, transfer?: Transferable[]) => unknown;
/**
* The interface for the audio processing code that uses spessasynth_core and runs on a separate thread.
*/
declare abstract class BasicSynthesizerCore {
readonly synthesizer: SpessaSynthProcessor;
readonly sequencer: SpessaSynthSequencer;
protected readonly post: PostMessageSynthCore;
/**
* Indicates if the processor is alive.
* @protected
*/
protected alive: boolean;
protected constructor(sampleRate: number, options: SynthProcessorOptions, postMessage: PostMessageSynthCore);
protected postReady<K extends keyof SynthesizerReturn>(type: K, data: SynthesizerReturn[K], transferable?: Transferable[]): void;
protected postProgress<K extends keyof SynthesizerProgress>(type: K, data: SynthesizerProgress[K]): void;
protected destroy(): void;
protected handleMessage(m: BasicSynthesizerMessage): void;
}
declare class WorkerSynthesizerCore extends BasicSynthesizerCore {
/**
* The message port to the playback audio worklet.
*/
readonly workletMessagePort: MessagePort;
protected readonly compressionFunction?: WorkerSampleEncodingFunction;
/**
* Creates a new worker synthesizer core: the synthesizer that runs in the worker.
* Most parameters here are provided with the first message that is posted to the worker by the WorkerSynthesizer.
* @param synthesizerConfiguration The data from the first message sent from WorkerSynthesizer.
* Listen for the first event and use its data to initialize this class.
* @param workletMessagePort The first port from the first message sent from WorkerSynthesizer.
* @param mainThreadCallback postMessage function or similar.
* @param compressionFunction Optional function for compressing SF3 banks.
*/
constructor(synthesizerConfiguration: {
sampleRate: number;
initialTime: number;
}, workletMessagePort: MessagePort, mainThreadCallback: typeof Worker.prototype.postMessage, compressionFunction?: WorkerSampleEncodingFunction);
/**
* Handles a message received from the main thread.
* @param m The message received.
*/
handleMessage(m: BasicSynthesizerMessage): void;
protected getBank(opts: WorkerBankWriteOptions): BasicSoundBank;
protected stopAudioLoop(): void;
protected startAudioLoop(): void;
protected destroy(): void;
protected renderAndSendChunk(): void;
}
interface WorkerRenderAudioOptions {
/**
* Extra fadeout time after the song finishes, in seconds.
*/
extraTime: number;
/**
* If channels should be rendered separately.
*/
separateChannels: boolean;
/**
* The amount of times to loop the song.
*/
loopCount: number;
/**
* The function that tracks the rendering progress.
* @param progress mapped 0 to 1.
* @param stage 0 is a dry pass, 1 is adding effects.
*/
progressCallback?: (progress: number, stage: number) => unknown;
/**
* If the current parameters of the synthesizer should be preserved.
*/
preserveSynthParams: boolean;
/**
* If the effects should be enabled.
*/
enableEffects: boolean;
}
interface PassedProcessorParameters {
/**
* If the synthesizer should send events.
*/
enableEventSystem: boolean;
/**
* If the synth should use one output with 32 channels (2 audio channels for each midi channel).
*/
oneOutput: boolean;
}
interface OfflineRenderWorkletData {
/**
* The MIDI to render.
*/
midiSequence: BasicMIDI;
/**
* The snapshot to apply.
*/
snapshot?: SynthesizerSnapshot;
/**
* The amount times to loop the song.
*/
loopCount: number;
/**
* The list of sound banks to render this file with.
*/
soundBankList: {
bankOffset: number;
soundBankBuffer: ArrayBuffer;
}[];
/**
* The options to pass to the sequencer.
*/
sequencerOptions: Partial<SequencerOptions>;
}
interface WorkletSBKManagerData {
addSoundBank: {
soundBankBuffer: ArrayBuffer;
id: string;
bankOffset: number;
};
deleteSoundBank: string;
rearrangeSoundBanks: string[];
}
interface WorkletKMManagerData {
addMapping: {
channel: number;
midiNote: number;
mapping: KeyModifier;
};
deleteMapping: {
channel: number;
midiNote: number;
};
clearMappings: null;
}
type BasicSynthesizerMessage = {
[K in keyof BasicSynthesizerMessageData]: {
channelNumber: number;
type: K;
data: BasicSynthesizerMessageData[K];
};
}[keyof BasicSynthesizerMessageData];
interface WorkerBankWriteOptions {
/**
* Trim the sound bank to only include samples used in the current MIDI file.
*/
trim: boolean;
/**
* The sound bank ID in the sound bank manager to write.
*/
bankID: string;
/**
* If the embedded sound bank should be written instead if it exists.
*/
writeEmbeddedSoundBank: boolean;
}
type WorkerDLSWriteOptions = Omit<DLSWriteOptions, "progressFunction"> & WorkerBankWriteOptions;
type WorkerSoundFont2WriteOptions = Omit<SoundFont2WriteOptions, "compressionFunction" | "progressFunction"> & WorkerBankWriteOptions & {
/**
* The compression quality to call your provided compressionFunction with, if compressing.
*/
compressionQuality: number;
};
type WorkerSampleEncodingFunction = (audioData: Float32Array, sampleRate: number, quality: number) => Promise<Uint8Array>;
type WorkerRMIDIWriteOptions = Omit<RMIDIWriteOptions, "soundBank"> & (({
format: "sf2";
} & WorkerSoundFont2WriteOptions) | ({
format: "dls";
} & WorkerDLSWriteOptions));
interface BasicSynthesizerMessageData {
workerInitialization: {
sampleRate: number;
currentTime: number;
};
renderAudio: {
sampleRate: number;
options: WorkerRenderAudioOptions;
};
writeSF2: WorkerSoundFont2WriteOptions;
writeDLS: WorkerDLSWriteOptions;
writeRMIDI: WorkerRMIDIWriteOptions;
startOfflineRender: OfflineRenderWorkletData;
midiMessage: {
messageData: Uint8Array;
channelOffset: number;
force: boolean;
options: SynthMethodOptions;
};
ccReset: null;
setChannelVibrato: {
rate: number;
depth: number;
delay: number;
};
stopAll: number;
killNotes: number;
muteChannel: boolean;
addNewChannel: null;
customCcChange: {
ccNumber: CustomController;
ccValue: number;
};
transposeChannel: {
semitones: number;
force: boolean;
};
setDrums: boolean;
lockController: {
controllerNumber: MIDIController | -1;
isLocked: boolean;
};
sequencerSpecific: SequencerMessage;
requestSynthesizerSnapshot: null;
setLogLevel: {
enableInfo: boolean;
enableWarning: boolean;
enableGroup: boolean;
};
setMasterParameter: {
[K in keyof MasterParameterType]: {
type: K;
data: MasterParameterType[K];
};
}[keyof MasterParameterType];
soundBankManager: {
[K in keyof WorkletSBKManagerData]: {
type: K;
data: WorkletSBKManagerData[K];
};
}[keyof WorkletSBKManagerData];
keyModifierManager: {
[K in keyof WorkletKMManagerData]: {
type: K;
data: WorkletKMManagerData[K];
};
}[keyof WorkletKMManagerData];
destroyWorklet: null;
}
interface BasicSynthesizerReturnMessageData {
eventCall: SynthProcessorEvent;
sequencerReturn: SequencerReturnMessage;
isFullyInitialized: {
[K in keyof SynthesizerReturn]: {
type: K;
data: SynthesizerReturn[K];
};
}[keyof SynthesizerReturn];
soundBankError: Error;
renderingProgress: {
[K in keyof SynthesizerProgress]: {
type: K;
data: SynthesizerProgress[K];
};
}[keyof SynthesizerProgress];
}
type BasicSynthesizerReturnMessage = {
[K in keyof BasicSynthesizerReturnMessageData]: {
type: K;
data: BasicSynthesizerReturnMessageData[K];
currentTime: number;
};
}[keyof BasicSynthesizerReturnMessageData];
interface SynthesizerProgress {
renderAudio: number;
workerSynthWriteFile: {
sampleName: string;
sampleIndex: number;
sampleCount: number;
};
}
interface SynthesizerReturn {
sf3Decoder: null;
soundBankManager: null;
startOfflineRender: null;
synthesizerSnapshot: SynthesizerSnapshot;
renderAudio: {
reverb: [Float32Array, Float32Array];
chorus: [Float32Array, Float32Array];
dry: [Float32Array, Float32Array][];
};
workerSynthWriteFile: {
/**
* The binary data of the file.
*/
binary: ArrayBuffer;
/**
* The suggested name of the file.
*/
fileName: string;
};
}
interface SynthConfig {
/**
* If the synth should use one output with 32 channels (2 audio channels for each midi channel).
*/
oneOutput: boolean;
/**
* If the chorus processor should be initialized during creation.
* Note that setting this to false will not allow it to be used later.
* If you want to enable it at some point, set this to true and set the chorus gain to 0.
*/
initializeChorusProcessor: boolean;
/**
* If the reverb processor should be initialized during creation.
* Note that setting this to false will not allow it to be used later.
* If you want to enable it at some point, set this to true and set the reverb gain to 0.
*/
initializeReverbProcessor: boolean;
/**
* Custom audio node creation functions for Web Audio wrappers, such as standardized-audio-context.
* Pass undefined to use the Web Audio API.
*/
audioNodeCreators?: AudioNodeCreators;
/**
* If the event system should be enabled. This can only be set once.
*/
enableEventSystem: boolean;
}
interface BasicEffectConfig {
null?: null;
}
interface AudioNodeCreators {
/**
* A custom creator for an AudioWorkletNode.
* @param context
* @param workletName
* @param options
*/
worklet: (context: BaseAudioContext, workletName: string, options?: AudioWorkletNodeOptions & {
processorOptions: PassedProcessorParameters;
}) => AudioWorkletNode;
}
interface ReverbConfig extends BasicEffectConfig {
/**
* The impulse response for the reverb. Pass undefined to use default one.
*/
impulseResponse?: AudioBuffer;
}
interface ChorusConfig extends BasicEffectConfig {
/**
* The amount of delay nodes (for each channel) and the corresponding oscillators.
*/
nodesAmount: number;
/**
* The initial delay, in seconds.
*/
defaultDelay: number;
/**
* The difference between delays in the delay nodes.
*/
delayVariation: number;
/**
* The difference of delays between two channels (added to the right channel).
*/
stereoDifference: number;
/**
* The initial delay time oscillator frequency, in Hz.
*/
oscillatorFrequency: number;
/**
* The difference between frequencies of oscillators, in Hz.
*/
oscillatorFrequencyVariation: number;
/**
* How much will oscillator alter the delay in delay nodes, in seconds.
*/
oscillatorGain: number;
}
declare abstract class BasicEffectsProcessor {
readonly input: AudioNode;
protected readonly output: AudioNode;
protected constructor(input: AudioNode, output: AudioNode);
abstract get config(): BasicEffectConfig;
abstract update(config: BasicEffectConfig): void;
/**
* Connects the processor to a given node.
* @param destinationNode The node to connect to.
*/
connect(destinationNode: AudioNode): AudioNode;
/**
* Disconnects the processor from a given node.
* @param destinationNode The node to disconnect from.
*/
disconnect(destinationNode?: AudioNode): void;
/**
* Disconnects the effect processor.
*/
delete(): void;
}
/**
* Fancy_chorus.js
* purpose: creates a simple chorus effect node
*/
declare class ChorusProcessor extends BasicEffectsProcessor {
private readonly chorusLeft;
private readonly chorusRight;
/**
* Creates a fancy chorus effect.
* @param context The audio context.
* @param config The configuration for the chorus.
*/
constructor(context: BaseAudioContext, config?: Partial<ChorusConfig>);
private _config;
get config(): ChorusConfig;
/**
* Updates the chorus with a given config.
* @param chorusConfig The config to use.
*/
update(chorusConfig: Partial<ChorusConfig>): void;
/**
* Disconnects and deletes the chorus effect.
*/
delete(): void;
private deleteNodes;
private createChorusNode;
}
declare class ReverbProcessor extends BasicEffectsProcessor {
/**
* Indicates that the reverb is ready.
*/
readonly isReady: Promise<AudioBuffer>;
private conv;
/**
* Creates a new reverb processor.
* @param context The context to use.
* @param config The reverb configuration.
*/
constructor(context: BaseAudioContext, config?: Partial<ReverbConfig>);
private _config;
get config(): ReverbConfig;
/**
* Updates the reverb with a given config.
* @param config The config to use.
*/
update(config: Partial<ReverbConfig>): void;
}
/**
* Extended synthesizer snapshot to contain effects
*/
declare class LibSynthesizerSnapshot extends SynthesizerSnapshot {
/**
* Chorus configuration of this synthesizer.
*/
chorusConfig: ChorusConfig;
/**
* Reverb configuration of this synthesizer.
*/
reverbConfig: ReverbConfig;
constructor(channelSnapshots: ChannelSnapshot[], masterParameters: MasterParameterType, keyMappings: (KeyModifier | undefined)[][], chorusConfig?: ChorusConfig, reverbConfig?: ReverbConfig);
/**
* Retrieves the SynthesizerSnapshot from the lib snapshot.
*/
getCoreSnapshot(): SynthesizerSnapshot;
}
declare abstract class BasicSynthesizer {
/**
* Allows managing the sound bank list.
*/
readonly soundBankManager: SoundBankManager;
/**
* Allows managing key modifications.
*/
readonly keyModifierManager: WorkletKeyModifierManagerWrapper;
/**
* Allows setting up custom event listeners for the synthesizer.
*/
readonly eventHandler: SynthEventHandler;
/**
* Synthesizer's parent AudioContext instance.
*/
readonly context: BaseAudioContext;
/**
* Synth's current channel properties.
*/
readonly channelProperties: ChannelProperty[];
/**
* The current preset list.
*/
presetList: PresetList;
/**
* INTERNAL USE ONLY!
*/
sequencerCallbackFunction?: (m: SequencerReturnMessage) => unknown;
/**
* Resolves when the synthesizer is ready.
*/
readonly isReady: Promise<unknown>;
/**
* Synthesizer's reverb processor.
* Undefined if reverb is disabled.
*/
readonly reverbProcessor?: ReverbProcessor;
/**
* Synthesizer's chorus processor.
* Undefined if chorus is disabled.
*/
readonly chorusProcessor?: ChorusProcessor;
/**
* INTERNAL USE ONLY!
*/
readonly post: (data: BasicSynthesizerMessage, transfer?: Transferable[]) => unknown;
protected readonly worklet: AudioWorkletNode;
/**
* The new channels will have their audio sent to the modulated output by this constant.
* what does that mean?
* e.g., if outputsAmount is 16, then channel's 16 audio data will be sent to channel 0
*/
protected readonly _outputsAmount = 16;
/**
* The current amount of MIDI channels the synthesizer has.
*/
channelsAmount: number;
protected readonly masterParameters: MasterParameterType;
protected resolveMap: Map<keyof SynthesizerReturn, (data: SynthesizerReturn[keyof SynthesizerReturn]) => unknown>;
protected renderingProgressTracker: Map<keyof SynthesizerProgress, ((args: number) => unknown) | ((args: {
sampleName: string;
sampleIndex: number;
sampleCount: number;
}) => unknown)>;
/**
* Creates a new instance of a synthesizer.
* @param worklet The AudioWorkletNode to use.
* @param postFunction The internal post function.
* @param config Optional configuration for the synthesizer.
*/
protected constructor(worklet: AudioWorkletNode, postFunction: (data: BasicSynthesizerMessage, transfer?: Transferable[]) => unknown, config: SynthConfig);
/**
* Current voice amount
*/
protected _voicesAmount: number;
/**
* The current number of voices playing.
*/
get voicesAmount(): number;
/**
* The audioContext's current time.
*/
get currentTime(): number;
/**
* Connects from a given node.
* @param destinationNode The node to connect to.
*/
connect(destinationNode: AudioNode): AudioNode;
/**
* Disconnects from a given node.
* @param destinationNode The node to disconnect from.
*/
disconnect(destinationNode?: AudioNode): AudioNode | undefined;
/**
* Sets the SpessaSynth's log level in the processor.
* @param enableInfo Enable info (verbose)
* @param enableWarning Enable warnings (unrecognized messages)
* @param enableGroup Enable groups (to group a lot of logs)
*/
setLogLevel(enableInfo: boolean, enableWarning: boolean, enableGroup: boolean): void;
/**
* Gets a master parameter from the synthesizer.
* @param type The parameter to get.
* @returns The parameter value.
*/
getMasterParameter<K extends keyof MasterParameterType>(type: K): MasterParameterType[K];
/**
* Sets a master parameter to a given value.
* @param type The parameter to set.
* @param value The value to set.
*/
setMasterParameter<K extends keyof MasterParameterType>(type: K, value: MasterParameterType[K]): void;
/**
* Gets a complete snapshot of the synthesizer, effects.
*/
getSnapshot(): Promise<LibSynthesizerSnapshot>;
/**
* Adds a new channel to the synthesizer.
*/
addNewChannel(): void;
/**
* Sets custom vibrato for the channel.
* @param channel The channel number.
* @param value The vibrato parameters.
*/
setVibrato(channel: number, value: {
delay: number;
depth: number;
rate: number;
}): void;
/**
* Connects the individual audio outputs to the given audio nodes. In the app, it's used by the renderer.
* @param audioNodes Exactly 16 outputs.
*/
connectIndividualOutputs(audioNodes: AudioNode[]): void;
/**
* Disconnects the individual audio outputs to the given audio nodes. In the app, it's used by the renderer.
* @param audioNodes Exactly 16 outputs.
*/
disconnectIndividualOutputs(audioNodes: AudioNode[]): void;
/**
* Disables the GS NRPN parameters like vibrato or drum key tuning.
*/
disableGSNPRNParams(): void;
/**
* Sends a raw MIDI message to the synthesizer.
* @param message the midi message, each number is a byte.
* @param channelOffset the channel offset of the message.
* @param eventOptions additional options for this command.
*/
sendMessage(message: Iterable<number>, channelOffset?: number, eventOptions?: SynthMethodOptions): void;
/**
* Starts playing a note
* @param channel Usually 0-15: the channel to play the note.
* @param midiNote 0-127 the key number of the note.
* @param velocity 0-127 the velocity of the note (generally controls loudness).
* @param eventOptions Additional options for this command.
*/
noteOn(channel: number, midiNote: number, velocity: number, eventOptions?: SynthMethodOptions): void;
/**
* Stops playing a note.
* @param channel Usually 0-15: the channel of the note.
* @param midiNote {number} 0-127 the key number of the note.
* @param force Instantly kills the note if true.
* @param eventOptions Additional options for this command.
*/
noteOff(channel: number, midiNote: number, force?: boolean, eventOptions?: SynthMethodOptions): void;
/**
* Stops all notes.
* @param force If the notes should immediately be stopped, defaults to false.
*/
stopAll(force?: boolean): void;
/**
* Changes the given controller
* @param channel Usually 0-15: the channel to change the controller.
* @param controllerNumber 0-127 the MIDI CC number.
* @param controllerValue 0-127 the controller value.
* @param force Forces the controller-change message, even if it's locked or gm system is set and the cc is bank select.
* @param eventOptions Additional options for this command.
*/
controllerChange(channel: number, controllerNumber: MIDIController, controllerValue: number, force?: boolean, eventOptions?: SynthMethodOptions): void;
/**
* Resets all controllers (for every channel)
*/
resetControllers(): void;
/**
* Causes the given midi channel to ignore controller messages for the given controller number.
* @param channel Usually 0-15: the channel to lock.
* @param controllerNumber 0-127 MIDI CC number.
* @param isLocked True if locked, false if unlocked.
* @remarks
* Controller number -1 locks the preset.
*/
lockController(channel: number, controllerNumber: MIDIController | -1, isLocked: boolean): void;
/**
* Applies pressure to a given channel.
* @param channel Usually 0-15: the channel to change the controller.
* @param pressure 0-127: the pressure to apply.
* @param eventOptions Additional options for this command.
*/
channelPressure(channel: number, pressure: number, eventOptions?: SynthMethodOptions): void;
/**
* Applies pressure to a given note.
* @param channel Usually 0-15: the channel to change the controller.
* @param midiNote 0-127: the MIDI note.
* @param pressure 0-127: the pressure to apply.
* @param eventOptions Additional options for this command.
*/
polyPressure(channel: number, midiNote: number, pressure: number, eventOptions?: SynthMethodOptions): void;
/**
* Sets the pitch of the given channel.
* @param channel Usually 0-15: the channel to change pitch.
* @param value The bend of the MIDI pitch wheel message. 0 - 16384
* @param eventOptions Additional options for this command.
*/
pitchWheel(channel: number, value: number, eventOptions?: SynthMethodOptions): void;
/**
* Sets the channel's pitch wheel range, in semitones.
* @param channel Usually 0-15: the channel to change.
* @param range The bend range in semitones.
*/
pitchWheelRange(channel: number, range: number): void;
/**
* Changes the program for a given channel
* @param channel Usually 0-15: the channel to change.
* @param programNumber 0-127 the MIDI patch number.
* defaults to false
*/
programChange(channel: number, programNumber: number): void;
/**
* Transposes the channel by given number of semitones.
* @param channel The channel number.
* @param semitones The transposition of the channel, it can be a float.
* @param force Defaults to false, if true transposes the channel even if it's a drum channel.
*/
transposeChannel(channel: number, semitones: number, force?: boolean): void;
/**
* Mutes or unmutes the given channel.
* @param channel Usually 0-15: the channel to mute.
* @param isMuted Indicates if the channel is muted.
*/
muteChannel(channel: number, isMuted: boolean): void;
/**
* Sends a MIDI Sysex message to the synthesizer.
* @param messageData The message's data, excluding the F0 byte, but including the F7 at the end.
* @param channelOffset Channel offset for the system exclusive message, defaults to zero.
* @param eventOptions Additional options for this command.
*/
systemExclusive(messageData: number[] | Iterable<number> | Uint8Array, channelOffset?: number, eventOptions?: SynthMethodOptions): void;
/**
* Tune MIDI keys of a given program using the MIDI Tuning Standard.
* @param program 0 - 127 the MIDI program number to use.
* @param tunings The keys and their tunings.
* TargetPitch of -1 sets the tuning for this key to be tuned regularly.
*/
tuneKeys(program: number, tunings: {
sourceKey: number;
targetPitch: number;
}[]): void;
/**
* Toggles drums on a given channel.
* @param channel The channel number.
* @param isDrum If the channel should be drums.
*/
setDrums(channel: number, isDrum: boolean): void;
/**
* Yes please!
*/
reverbateEverythingBecauseWhyNot(): "That's the spirit!";
/**
* INTERNAL USE ONLY!
* @param type INTERNAL USE ONLY!
* @param resolve INTERNAL USE ONLY!
*/
awaitWorkerResponse<K extends keyof SynthesizerReturn>(type: K, resolve: (data: SynthesizerReturn[K]) => unknown): void;
protected assignProgressTracker<K extends keyof SynthesizerProgress>(type: K, progressFunction: (args: SynthesizerProgress[K]) => unknown): void;
protected revokeProgressTracker<K extends keyof SynthesizerProgress>(type: K): void;
protected _sendInternal(message: Iterable<number>, channelOffset: number, force: boolean | undefined, eventOptions: Partial<SynthMethodOptions>): void;
/**
* Handles the messages received from the worklet.
*/
protected handleMessage(m: BasicSynthesizerReturnMessage): void;
protected addNewChannelInternal(post: boolean): void;
protected workletResponds<K extends keyof SynthesizerReturn>(type: K, data: SynthesizerReturn[K]): void;
}
/**
* This synthesizer uses an audio worklet node containing the processor.
*/
declare class WorkletSynthesizer extends BasicSynthesizer {
/**
* Creates a new instance of an AudioWorklet-based synthesizer.
* @param context The audio context.
* @param config Optional configuration for the synthesizer.
*/
constructor(context: BaseAudioContext, config?: Partial<SynthConfig>);
/**
* Starts an offline audio render.
* @param config The configuration to use.
* @remarks
* Call this method immediately after you've set up the synthesizer.
* Do NOT call any other methods after initializing before this one.
* Chromium seems to ignore worklet messages for OfflineAudioContext.
*/
startOfflineRender(config: OfflineRenderWorkletData): Promise<void>;
/**
* Destroys the synthesizer instance.
*/
destroy(): void;
}
type WorkerSynthWriteOptions<K> = K & {
progressFunction?: (args: SynthesizerProgress["workerSynthWriteFile"]) => unknown;
};
/**
* This synthesizer uses a Worker containing the processor and an audio worklet node for playback.
*/
declare class WorkerSynthesizer extends BasicSynthesizer {
/**
* Time offset for syncing with the synth
* @private
*/
private timeOffset;
/**
* Creates a new instance of a Worker-based synthesizer.
* @param context The audio context.
* @param workerPostMessage The postMessage for the worker containing the synthesizer core.
* @param config Optional configuration for the synthesizer.
*/
constructor(context: BaseAudioContext, workerPostMessage: typeof Worker.prototype.postMessage, config?: Partial<SynthConfig>);
get currentTime(): number;
/**
* Registers an audio worklet for the WorkerSynthesizer.
* @param context The context to register the worklet for.
* @param maxQueueSize The maximum amount of 128-sample chunks to store in the worklet. Higher values result in less breakups but higher latency.
*/
static registerPlaybackWorklet(context: BaseAudioContext, maxQueueSize?: number): Promise<void>;
/**
* Handles a return message from the Worker.
* @param e The event received from the Worker.
*/
handleWorkerMessage(e: BasicSynthesizerReturnMessage): void;
/**
* Writes a DLS file directly in the worker.
* @param options Options for writing the file.
* @returns The file array buffer and its corresponding name.
*/
writeDLS(options?: Partial<WorkerSynthWriteOptions<WorkerDLSWriteOptions>>): Promise<SynthesizerReturn["workerSynthWriteFile"]>;
/**
* Writes an SF2/SF3 file directly in the worker.
* @param options Options for writing the file.
* @returns The file array buffer and its corresponding name.
*/
writeSF2(options?: Partial<WorkerSynthWriteOptions<WorkerSoundFont2WriteOptions>>): Promise<SynthesizerReturn["workerSynthWriteFile"]>;
/**
* Writes an embedded MIDI (RMIDI) file directly in the worker.
* @param options Options for writing the file.
* @returns The file array buffer.
*/
writeRMIDI(options?: Partial<WorkerSynthWriteOptions<WorkerRMIDIWriteOptions>>): Promise<ArrayBuffer>;
/**
* Renders the current song in the connected sequencer to Float32 buffers.
* @param sampleRate The sample rate to use, in Hertz.
* @param renderOptions Extra options for the render.
* @returns A single audioBuffer if separate channels were not enabled, otherwise 16.
* @remarks
* This stops the synthesizer.
*/
renderAudio(sampleRate: number, renderOptions?: Partial<WorkerRenderAudioOptions>): Promise<AudioBuffer[]>;
}
type SequencerEventCallback<T extends keyof WorkletSequencerEventType> = (callbackData: WorkletSequencerEventType[T]) => unknown;
declare class SeqEventHandler {
/**
* The time delay before an event is called.
* Set to 0 to disable it.
*/
timeDelay: number;
private readonly events;
/**
* Adds a new event listener.
* @param event The event to listen to.
* @param id The unique identifier for the event. It can be used to overwrite existing callback with the same ID.
* @param callback The callback for the event.
*/
addEvent<T extends keyof WorkletSequencerEventType>(event: T, id: string, callback: SequencerEventCallback<T>): void;
/**
* Removes an event listener
* @param name The event to remove a listener from.
* @param id The unique identifier for the event to remove.
*/
removeEvent<T extends keyof WorkletSequencerEventType>(name: T, id: string): void;
/**
* Calls the given event.
* Internal use only.
*/
callEventInternal<T extends keyof WorkletSequencerEventType>(name: T, eventData: WorkletSequencerEventType[T]): void;
}
declare class Sequencer {
/**
* The current MIDI data, with the exclusion of the embedded sound bank and event data.
*/
midiData?: MIDIData;
/**
* The current MIDI data for all songs, like the midiData property.
*/
songListData: MIDIData[];
/**
* Allows setting up custom event listeners for the sequencer.
*/
eventHandler: SeqEventHandler;
/**
* Indicates whether the sequencer has finished playing a sequence.
*/
isFinished: boolean;
/**
* The synthesizer attached to this sequencer.
*/
readonly synth: BasicSynthesizer;
/**
* The MIDI port to play to.
*/
private midiOut?;
private isLoading;
/**
* Indicates if the sequencer is paused.
* Paused if a number, undefined if playing.
*/
private pausedTime?;
private getMIDICallback?;
private highResTimeOffset;
/**
* Absolute playback startTime, bases on the synth's time.
*/
private absoluteStartTime;
/**
* Creates a new MIDI sequencer for playing back MIDI files.
* @param synth synth to send events to.
* @param options the sequencer's options.
*/
constructor(synth: BasicSynthesizer, options?: Partial<SequencerOptions>);
private _songIndex;
/**
* The current song number in the playlist.
*/
get songIndex(): number;
/**
* The current song number in the playlist.
*/
set songIndex(value: number);
protected _currentTempo: number;
/**
* Current song's tempo in BPM.
*/
get currentTempo(): number;
/**
* The current sequence's length, in seconds.
*/
get duration(): number;
protected _songsAmount: number;
get songsAmount(): number;
protected _skipToFirstNoteOn: boolean;
/**
* Indicates if the sequencer should skip to first note on.
*/
get skipToFirstNoteOn(): boolean;
/**
* Indicates if the sequencer should skip to first note on.
*/
set skipToFirstNoteOn(val: boolean);
/**
* Internal loop count marker (-1 is infinite).
*/
protected _loopCount: number;
/**
* The current remaining number of loops. -1 means infinite looping.
*/
get loopCount(): number;
/**
* The current remaining number of loops. -1 means infinite looping.
*/
set loopCount(val: number);
/**
* Controls the playback's rate.
*/
protected _playbackRate: number;
/**
* Controls the playback's rate.
*/
get playbackRate(): number;
/**
* Controls the playback's rate.
*/
set playbackRate(value: number);
protected _shuffleSongs: boolean;
/**
* Indicates if the song order is random.
*/
get shuffleSongs(): boolean;
/**
* Indicates if the song order is random.
*/
set shuffleSongs(value: boolean);
/**
* Current playback time, in seconds.
*/
get currentTime(): number;
/**
* Current playback time, in seconds.
*/
set currentTime(time: number);
/**
* A smoothed version of currentTime.
* Use for visualization as it's not affected by the audioContext stutter.
*/
get currentHighResolutionTime(): number;
/**
* True if paused, false if playing or stopped.
*/
get paused(): boolean;
/**
* Gets the current MIDI File.
*/
getMIDI(): Promise<BasicMIDI>;
/**
* Loads a new song list.
* @param midiBuffers The MIDI files to play.
*/
loadNewSongList(midiBuffers: SuppliedMIDIData[]): void;
/**
* Connects a given output to the sequencer.
* @param output The output to connect. Pass undefined to use the connected synthesizer.
*/
connectMIDIOutput(output?: {
send: (data: number[]) => unknown;
}): void;
/**
* Pauses the playback.
*/
pause(): void;
/**
* Starts or resumes the playback.
*/
play(): void;
protected handleMessage(m: SequencerReturnMessage): void;
protected callEventInternal<EventType extends keyof WorkletSequencerEventType>(type: EventType, data: WorkletSequencerEventType[EventType]): void;
protected resetMIDIOutput(): void;
private recalculateStartTime;
private sendMessage;
}
interface ExtraWaveOptions extends WaveWriteOptions {
/**
* The channel offset in the AudioBuffer. Defaults to 0.
*/
channelOffset: number;
/**
* The amount of channels from the AudioBuffer to write. Defaults to all.
*/
channelCount: number;
}
/**
* Converts an audio buffer into a wave file.
* @param audioBuffer The audio data channels.
* @param options Additional options for writing the file.
* @returns The binary file.
*/
declare function audioBufferToWav(audioBuffer: AudioBuffer, options?: Partial<ExtraWaveOptions>): Blob;
/**
* Midi_handler.js
* purpose: handles the connection between MIDI devices and synthesizer/sequencer via Web MIDI API
*/
declare class LibMIDIPort {
readonly port: MIDIPort;
protected constructor(port: MIDIPort);
/**
*
*/
get id(): string;
/**
*
*/
get name(): string | null;
/**
*
*/
get manufacturer(): string | null;
/**
*
*/
get version(): string | null;
}
declare class LibMIDIInput extends LibMIDIPort {
private readonly connectedSynths;
constructor(input: MIDIInput);
/**
* Connects the input to a given synth, listening for all incoming events.
* @param synth The synth to connect to.
*/
connect(synth: BasicSynthesizer): void;
/**
* Disconnects the input from a given synth.
* @param synth The synth to disconnect from.
*/
disconnect(synth: BasicSynthesizer): void;
}
declare class LibMIDIOutput extends LibMIDIPort {
readonly port: MIDIOutput;
constructor(output: MIDIOutput);
/**
* Connects a given sequencer to the output, playing back the MIDI file to it.
* @param seq The sequencer to connect.
*/
connect(seq: Sequencer): void;
/**
* Disconnects sequencer from the output, making it play to the attached Synthesizer instead.
* @param seq The sequencer to disconnect.
*/
disconnect(seq: Sequencer): void;
}
/**
* A class for handling physical MIDI devices.
*/
declare class MIDIDeviceHandler {
/**
* The available MIDI inputs. ID maps to the input.
*/
readonly inputs: Map<string, LibMIDIInput>;
/**
* The available MIDI outputs. ID maps to the output.
*/
readonly outputs: Map<string, LibMIDIOutput>;
private constructor();
/**
* Attempts to initialize the MIDI Device Handler.
* @returns The handler.
* @throws An error if the MIDI Devices fail to initialize.
*/
static createMIDIDeviceHandler(): Promise<MIDIDeviceHandler>;
}
/**
* Web_midi_link.js
* purpose: handles the web midi link connection to the synthesizer
* https://www.g200kg.com/en/docs/webmidilink/
*/
declare class WebMIDILinkHandler {
/**
* Initializes support for Web MIDI Link (https://www.g200kg.com/en/docs/webmidilink/)
* @param synth The synthesizer to enable support with.
*/
constructor(synth: BasicSynthesizer);
}
declare const DEFAULT_SYNTH_CONFIG: SynthConfig;
export { BasicSynthesizer, ChorusProcessor, DEFAULT_SYNTH_CONFIG, MIDIDeviceHandler, ReverbProcessor, Sequencer, WebMIDILinkHandler, WorkerSynthesizer, WorkerSynthesizerCore, WorkletSynthesizer, audioBufferToWav };