@blibliki/engine 0.4.0 → 0.5.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.ts +147 -129
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/Engine.ts +4 -0
- package/src/core/Note/index.ts +19 -4
- package/src/core/index.ts +7 -0
- package/src/core/midi/Message.ts +1 -0
- package/src/core/midi/MidiDeviceManager.ts +25 -0
- package/src/core/midi/adapters/WebMidiAdapter.ts +1 -1
- package/src/core/midi/deviceMatcher.ts +203 -0
- package/src/core/module/Module.ts +7 -1
- package/src/core/module/PolyModule.ts +10 -4
- package/src/core/module/VoiceScheduler.ts +9 -3
- package/src/core/module/index.ts +8 -0
- package/src/index.ts +1 -0
- package/src/modules/Inspector.ts +2 -1
- package/src/modules/MidiMapper.ts +3 -0
- package/src/modules/MidiSelector.ts +30 -6
- package/src/processors/filter-processor.ts +17 -7
- package/src/processors/scale-processor.ts +25 -7
package/dist/index.d.ts
CHANGED
|
@@ -143,6 +143,102 @@ declare class ComputerKeyboardInput implements IMidiInput {
|
|
|
143
143
|
private extractNote;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
type AudioInputProps = IOProps & {
|
|
147
|
+
ioType: IOType.AudioInput;
|
|
148
|
+
getAudioNode: () => AudioNode | AudioParam | AudioDestinationNode;
|
|
149
|
+
};
|
|
150
|
+
type AudioOutputProps = IOProps & {
|
|
151
|
+
ioType: IOType.AudioOutput;
|
|
152
|
+
getAudioNode: () => AudioNode;
|
|
153
|
+
};
|
|
154
|
+
declare class AudioInput extends IO<AudioOutput | PolyAudioOutput> implements AudioInputProps {
|
|
155
|
+
ioType: IOType.AudioInput;
|
|
156
|
+
getAudioNode: AudioInputProps["getAudioNode"];
|
|
157
|
+
constructor(module: Module<ModuleType>, props: AudioInputProps);
|
|
158
|
+
}
|
|
159
|
+
declare class AudioOutput extends IO<AudioInput | PolyAudioInput> implements AudioOutputProps {
|
|
160
|
+
ioType: IOType.AudioOutput;
|
|
161
|
+
getAudioNode: AudioOutputProps["getAudioNode"];
|
|
162
|
+
constructor(module: Module<ModuleType>, props: AudioOutputProps);
|
|
163
|
+
plug(io: AudioInput | PolyAudioInput, plugOther?: boolean): void;
|
|
164
|
+
unPlug(io: AudioInput | PolyAudioInput, plugOther?: boolean): void;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
type PolyAudioInputProps = IOProps & {
|
|
168
|
+
ioType: IOType.PolyAudioInput;
|
|
169
|
+
};
|
|
170
|
+
type PolyAudioOutputProps = IOProps & {
|
|
171
|
+
ioType: IOType.PolyAudioOutput;
|
|
172
|
+
};
|
|
173
|
+
declare class PolyAudioInput extends IO<PolyAudioOutput | AudioOutput> implements PolyAudioInputProps {
|
|
174
|
+
ioType: IOType.PolyAudioInput;
|
|
175
|
+
module: PolyModule<ModuleType>;
|
|
176
|
+
plug(io: PolyAudioOutput | AudioOutput, plugOther?: boolean): void;
|
|
177
|
+
unPlug(io: PolyAudioOutput | AudioOutput, plugOther?: boolean): void;
|
|
178
|
+
findIOByVoice(voice: number): AudioInput;
|
|
179
|
+
}
|
|
180
|
+
declare class PolyAudioOutput extends IO<PolyAudioInput | AudioInput> implements PolyAudioOutputProps {
|
|
181
|
+
ioType: IOType.PolyAudioOutput;
|
|
182
|
+
module: PolyModule<ModuleType>;
|
|
183
|
+
plug(io: PolyAudioInput | AudioInput, plugOther?: boolean): void;
|
|
184
|
+
unPlug(io: PolyAudioInput | AudioInput, plugOther?: boolean): void;
|
|
185
|
+
findIOByVoice(voice: number): AudioOutput;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
type IPolyModule<T extends ModuleType> = Omit<IModule<T>, "voiceNo"> & {
|
|
189
|
+
voices: number;
|
|
190
|
+
};
|
|
191
|
+
type IPolyModuleSerialize<T extends ModuleType> = IPolyModule<T> & {
|
|
192
|
+
inputs: IIOSerialize[];
|
|
193
|
+
outputs: IIOSerialize[];
|
|
194
|
+
};
|
|
195
|
+
type IPolyModuleConstructor<T extends ModuleType> = Optional<IPolyModule<T>, "id"> & {
|
|
196
|
+
monoModuleConstructor: (engineId: string, params: IModuleConstructor<T>) => Module<T>;
|
|
197
|
+
};
|
|
198
|
+
declare abstract class PolyModule<T extends ModuleType> implements IPolyModule<T> {
|
|
199
|
+
id: string;
|
|
200
|
+
engineId: string;
|
|
201
|
+
moduleType: T;
|
|
202
|
+
audioModules: Module<T>[];
|
|
203
|
+
inputs: InputCollection;
|
|
204
|
+
outputs: OutputCollection;
|
|
205
|
+
protected monoModuleConstructor: IPolyModuleConstructor<T>["monoModuleConstructor"];
|
|
206
|
+
protected _props: ModuleTypeToPropsMapping[T];
|
|
207
|
+
private _voices;
|
|
208
|
+
private _name;
|
|
209
|
+
private pendingUIUpdates;
|
|
210
|
+
constructor(engineId: string, params: IPolyModuleConstructor<T>);
|
|
211
|
+
get name(): string;
|
|
212
|
+
set name(value: string);
|
|
213
|
+
get props(): ModuleTypeToPropsMapping[T];
|
|
214
|
+
set props(value: Partial<ModuleTypeToPropsMapping[T]>);
|
|
215
|
+
get voices(): number;
|
|
216
|
+
set voices(value: number);
|
|
217
|
+
start(time: ContextTime): void;
|
|
218
|
+
stop(time: ContextTime): void;
|
|
219
|
+
serialize(): IPolyModuleSerialize<T>;
|
|
220
|
+
plug({ audioModule, from, to, }: {
|
|
221
|
+
audioModule: Module<ModuleType> | PolyModule<ModuleType>;
|
|
222
|
+
from: string;
|
|
223
|
+
to: string;
|
|
224
|
+
}): void;
|
|
225
|
+
rePlugAll(callback?: () => void): void;
|
|
226
|
+
protected unPlugAll(): void;
|
|
227
|
+
dispose(): void;
|
|
228
|
+
onMidiEvent: (midiEvent: MidiEvent) => void;
|
|
229
|
+
triggerPropsUpdate: () => void;
|
|
230
|
+
private sheduleTriggerUpdate;
|
|
231
|
+
findVoice(voiceNo: number): Module<T>;
|
|
232
|
+
protected registerDefaultIOs(value?: "both" | "in" | "out"): void;
|
|
233
|
+
protected registerAudioInput(props: Omit<PolyAudioInputProps, "ioType">): PolyAudioInput;
|
|
234
|
+
protected registerAudioOutput(props: Omit<PolyAudioOutputProps, "ioType">): PolyAudioOutput;
|
|
235
|
+
protected registerMidiInput(props: Omit<MidiInputProps, "ioType">): MidiInput;
|
|
236
|
+
protected registerMidiOutput(props: Omit<MidiOutputProps, "ioType">): MidiOutput;
|
|
237
|
+
private adjustNumberOfModules;
|
|
238
|
+
protected get engine(): Engine;
|
|
239
|
+
protected get context(): _blibliki_utils.Context;
|
|
240
|
+
}
|
|
241
|
+
|
|
146
242
|
type MidiInputProps = IOProps & {
|
|
147
243
|
ioType: IOType.MidiInput;
|
|
148
244
|
onMidiEvent: (event: MidiEvent) => void;
|
|
@@ -202,48 +298,6 @@ declare abstract class IO<Connection extends Base> extends Base {
|
|
|
202
298
|
unPlug(io: Connection, plugOther?: boolean): void;
|
|
203
299
|
}
|
|
204
300
|
|
|
205
|
-
type AudioInputProps = IOProps & {
|
|
206
|
-
ioType: IOType.AudioInput;
|
|
207
|
-
getAudioNode: () => AudioNode | AudioParam | AudioDestinationNode;
|
|
208
|
-
};
|
|
209
|
-
type AudioOutputProps = IOProps & {
|
|
210
|
-
ioType: IOType.AudioOutput;
|
|
211
|
-
getAudioNode: () => AudioNode;
|
|
212
|
-
};
|
|
213
|
-
declare class AudioInput extends IO<AudioOutput | PolyAudioOutput> implements AudioInputProps {
|
|
214
|
-
ioType: IOType.AudioInput;
|
|
215
|
-
getAudioNode: AudioInputProps["getAudioNode"];
|
|
216
|
-
constructor(module: Module<ModuleType>, props: AudioInputProps);
|
|
217
|
-
}
|
|
218
|
-
declare class AudioOutput extends IO<AudioInput | PolyAudioInput> implements AudioOutputProps {
|
|
219
|
-
ioType: IOType.AudioOutput;
|
|
220
|
-
getAudioNode: AudioOutputProps["getAudioNode"];
|
|
221
|
-
constructor(module: Module<ModuleType>, props: AudioOutputProps);
|
|
222
|
-
plug(io: AudioInput | PolyAudioInput, plugOther?: boolean): void;
|
|
223
|
-
unPlug(io: AudioInput | PolyAudioInput, plugOther?: boolean): void;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
type PolyAudioInputProps = IOProps & {
|
|
227
|
-
ioType: IOType.PolyAudioInput;
|
|
228
|
-
};
|
|
229
|
-
type PolyAudioOutputProps = IOProps & {
|
|
230
|
-
ioType: IOType.PolyAudioOutput;
|
|
231
|
-
};
|
|
232
|
-
declare class PolyAudioInput extends IO<PolyAudioOutput | AudioOutput> implements PolyAudioInputProps {
|
|
233
|
-
ioType: IOType.PolyAudioInput;
|
|
234
|
-
module: PolyModule<ModuleType>;
|
|
235
|
-
plug(io: PolyAudioOutput | AudioOutput, plugOther?: boolean): void;
|
|
236
|
-
unPlug(io: PolyAudioOutput | AudioOutput, plugOther?: boolean): void;
|
|
237
|
-
findIOByVoice(voice: number): AudioInput;
|
|
238
|
-
}
|
|
239
|
-
declare class PolyAudioOutput extends IO<PolyAudioInput | AudioInput> implements PolyAudioOutputProps {
|
|
240
|
-
ioType: IOType.PolyAudioOutput;
|
|
241
|
-
module: PolyModule<ModuleType>;
|
|
242
|
-
plug(io: PolyAudioInput | AudioInput, plugOther?: boolean): void;
|
|
243
|
-
unPlug(io: PolyAudioInput | AudioInput, plugOther?: boolean): void;
|
|
244
|
-
findIOByVoice(voice: number): AudioOutput;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
301
|
declare enum CollectionType {
|
|
248
302
|
Input = "Input",
|
|
249
303
|
Output = "Output"
|
|
@@ -280,58 +334,65 @@ declare class OutputCollection extends IOCollection<CollectionType.Output> {
|
|
|
280
334
|
constructor(module: Module<ModuleType> | PolyModule<ModuleType>);
|
|
281
335
|
}
|
|
282
336
|
|
|
283
|
-
type
|
|
284
|
-
|
|
337
|
+
type IModule<T extends ModuleType> = {
|
|
338
|
+
id: string;
|
|
339
|
+
name: string;
|
|
340
|
+
voiceNo: number;
|
|
341
|
+
moduleType: T;
|
|
342
|
+
props: ModuleTypeToPropsMapping[T];
|
|
285
343
|
};
|
|
286
|
-
type
|
|
344
|
+
type IModuleSerialize<T extends ModuleType> = IModule<T> & {
|
|
287
345
|
inputs: IIOSerialize[];
|
|
288
346
|
outputs: IIOSerialize[];
|
|
289
347
|
};
|
|
290
|
-
type
|
|
291
|
-
|
|
348
|
+
type IModuleConstructor<T extends ModuleType> = Optional<IModule<T>, "id" | "voiceNo"> & {
|
|
349
|
+
audioNodeConstructor?: (context: Context) => AudioNode;
|
|
292
350
|
};
|
|
293
|
-
|
|
351
|
+
type SetterHooks<P> = {
|
|
352
|
+
[K in keyof P as `onSet${Capitalize<string & K>}`]: (value: P[K]) => P[K];
|
|
353
|
+
} & {
|
|
354
|
+
[K in keyof P as `onAfterSet${Capitalize<string & K>}`]: (value: P[K]) => void;
|
|
355
|
+
};
|
|
356
|
+
declare abstract class Module<T extends ModuleType> implements IModule<T> {
|
|
294
357
|
id: string;
|
|
295
358
|
engineId: string;
|
|
359
|
+
name: string;
|
|
296
360
|
moduleType: T;
|
|
297
|
-
|
|
361
|
+
voiceNo: number;
|
|
362
|
+
audioNode: AudioNode | undefined;
|
|
298
363
|
inputs: InputCollection;
|
|
299
364
|
outputs: OutputCollection;
|
|
300
|
-
protected monoModuleConstructor: IPolyModuleConstructor<T>["monoModuleConstructor"];
|
|
301
365
|
protected _props: ModuleTypeToPropsMapping[T];
|
|
302
|
-
|
|
303
|
-
private _name;
|
|
366
|
+
protected activeNotes: Note[];
|
|
304
367
|
private pendingUIUpdates;
|
|
305
|
-
constructor(engineId: string, params:
|
|
306
|
-
get name(): string;
|
|
307
|
-
set name(value: string);
|
|
368
|
+
constructor(engineId: string, params: IModuleConstructor<T>);
|
|
308
369
|
get props(): ModuleTypeToPropsMapping[T];
|
|
309
370
|
set props(value: Partial<ModuleTypeToPropsMapping[T]>);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
start(time: ContextTime): void;
|
|
313
|
-
stop(time: ContextTime): void;
|
|
314
|
-
serialize(): IPolyModuleSerialize<T>;
|
|
371
|
+
private callPropHook;
|
|
372
|
+
serialize(): IModuleSerialize<T>;
|
|
315
373
|
plug({ audioModule, from, to, }: {
|
|
316
|
-
audioModule:
|
|
374
|
+
audioModule: AnyModule;
|
|
317
375
|
from: string;
|
|
318
376
|
to: string;
|
|
319
377
|
}): void;
|
|
320
|
-
rePlugAll(callback?: () => void): void;
|
|
378
|
+
protected rePlugAll(callback?: () => void): void;
|
|
321
379
|
protected unPlugAll(): void;
|
|
322
|
-
|
|
380
|
+
start(_time: ContextTime): void;
|
|
381
|
+
stop(_time: ContextTime): void;
|
|
382
|
+
triggerAttack(note: Note, _triggeredAt: ContextTime): void;
|
|
383
|
+
triggerRelease(note: Note, _triggeredAt: ContextTime): void;
|
|
384
|
+
handleCC(_event: MidiEvent, _triggeredAt: ContextTime): void;
|
|
323
385
|
onMidiEvent: (midiEvent: MidiEvent) => void;
|
|
324
386
|
triggerPropsUpdate: () => void;
|
|
325
387
|
private sheduleTriggerUpdate;
|
|
326
|
-
|
|
388
|
+
dispose(): void;
|
|
327
389
|
protected registerDefaultIOs(value?: "both" | "in" | "out"): void;
|
|
328
|
-
protected registerAudioInput(props: Omit<
|
|
329
|
-
protected registerAudioOutput(props: Omit<
|
|
390
|
+
protected registerAudioInput(props: Omit<AudioInputProps, "ioType">): AudioInput;
|
|
391
|
+
protected registerAudioOutput(props: Omit<AudioOutputProps, "ioType">): AudioOutput;
|
|
330
392
|
protected registerMidiInput(props: Omit<MidiInputProps, "ioType">): MidiInput;
|
|
331
393
|
protected registerMidiOutput(props: Omit<MidiOutputProps, "ioType">): MidiOutput;
|
|
332
|
-
private adjustNumberOfModules;
|
|
333
394
|
protected get engine(): Engine;
|
|
334
|
-
protected get context():
|
|
395
|
+
protected get context(): Context;
|
|
335
396
|
}
|
|
336
397
|
|
|
337
398
|
type BasePropType = {
|
|
@@ -696,66 +757,7 @@ type ModuleParams = {
|
|
|
696
757
|
[K in ModuleType]: K extends ModuleType.Oscillator | ModuleType.Gain | ModuleType.Envelope | ModuleType.Filter | ModuleType.StereoPanner | ModuleType.VoiceScheduler ? IPolyModuleConstructor<K> : ICreateModule<K>;
|
|
697
758
|
}[ModuleType];
|
|
698
759
|
|
|
699
|
-
type
|
|
700
|
-
id: string;
|
|
701
|
-
name: string;
|
|
702
|
-
voiceNo: number;
|
|
703
|
-
moduleType: T;
|
|
704
|
-
props: ModuleTypeToPropsMapping[T];
|
|
705
|
-
};
|
|
706
|
-
type IModuleSerialize<T extends ModuleType> = IModule<T> & {
|
|
707
|
-
inputs: IIOSerialize[];
|
|
708
|
-
outputs: IIOSerialize[];
|
|
709
|
-
};
|
|
710
|
-
type IModuleConstructor<T extends ModuleType> = Optional<IModule<T>, "id" | "voiceNo"> & {
|
|
711
|
-
audioNodeConstructor?: (context: Context) => AudioNode;
|
|
712
|
-
};
|
|
713
|
-
type SetterHooks<P> = {
|
|
714
|
-
[K in keyof P as `onSet${Capitalize<string & K>}`]: (value: P[K]) => P[K];
|
|
715
|
-
} & {
|
|
716
|
-
[K in keyof P as `onAfterSet${Capitalize<string & K>}`]: (value: P[K]) => void;
|
|
717
|
-
};
|
|
718
|
-
declare abstract class Module<T extends ModuleType> implements IModule<T> {
|
|
719
|
-
id: string;
|
|
720
|
-
engineId: string;
|
|
721
|
-
name: string;
|
|
722
|
-
moduleType: T;
|
|
723
|
-
voiceNo: number;
|
|
724
|
-
audioNode: AudioNode | undefined;
|
|
725
|
-
inputs: InputCollection;
|
|
726
|
-
outputs: OutputCollection;
|
|
727
|
-
protected _props: ModuleTypeToPropsMapping[T];
|
|
728
|
-
protected activeNotes: Note[];
|
|
729
|
-
private pendingUIUpdates;
|
|
730
|
-
constructor(engineId: string, params: IModuleConstructor<T>);
|
|
731
|
-
get props(): ModuleTypeToPropsMapping[T];
|
|
732
|
-
set props(value: Partial<ModuleTypeToPropsMapping[T]>);
|
|
733
|
-
private callPropHook;
|
|
734
|
-
serialize(): IModuleSerialize<T>;
|
|
735
|
-
plug({ audioModule, from, to, }: {
|
|
736
|
-
audioModule: AnyModule;
|
|
737
|
-
from: string;
|
|
738
|
-
to: string;
|
|
739
|
-
}): void;
|
|
740
|
-
protected rePlugAll(callback?: () => void): void;
|
|
741
|
-
protected unPlugAll(): void;
|
|
742
|
-
start(_time: ContextTime): void;
|
|
743
|
-
stop(_time: ContextTime): void;
|
|
744
|
-
triggerAttack(note: Note, _triggeredAt: ContextTime): void;
|
|
745
|
-
triggerRelease(note: Note, _triggeredAt: ContextTime): void;
|
|
746
|
-
handleCC(_event: MidiEvent, _triggeredAt: ContextTime): void;
|
|
747
|
-
onMidiEvent: (midiEvent: MidiEvent) => void;
|
|
748
|
-
triggerPropsUpdate: () => void;
|
|
749
|
-
private sheduleTriggerUpdate;
|
|
750
|
-
dispose(): void;
|
|
751
|
-
protected registerDefaultIOs(value?: "both" | "in" | "out"): void;
|
|
752
|
-
protected registerAudioInput(props: Omit<AudioInputProps, "ioType">): AudioInput;
|
|
753
|
-
protected registerAudioOutput(props: Omit<AudioOutputProps, "ioType">): AudioOutput;
|
|
754
|
-
protected registerMidiInput(props: Omit<MidiInputProps, "ioType">): MidiInput;
|
|
755
|
-
protected registerMidiOutput(props: Omit<MidiOutputProps, "ioType">): MidiOutput;
|
|
756
|
-
protected get engine(): Engine;
|
|
757
|
-
protected get context(): Context;
|
|
758
|
-
}
|
|
760
|
+
type IAnyModuleSerialize = IModuleSerialize<ModuleType> | IPolyModuleSerialize<ModuleType>;
|
|
759
761
|
|
|
760
762
|
type IPlug = {
|
|
761
763
|
moduleId: string;
|
|
@@ -793,6 +795,18 @@ declare class MidiDeviceManager {
|
|
|
793
795
|
initialize(): Promise<void>;
|
|
794
796
|
find(id: string): MidiDevice | ComputerKeyboardInput | undefined;
|
|
795
797
|
findByName(name: string): MidiDevice | ComputerKeyboardInput | undefined;
|
|
798
|
+
/**
|
|
799
|
+
* Finds a device using fuzzy name matching
|
|
800
|
+
* Useful for matching devices across browser/Node.js environments where names differ
|
|
801
|
+
*
|
|
802
|
+
* @param targetName - The device name to match
|
|
803
|
+
* @param threshold - Minimum similarity score (0-1, default: 0.6)
|
|
804
|
+
* @returns The best matching device and confidence score, or null
|
|
805
|
+
*/
|
|
806
|
+
findByFuzzyName(targetName: string, threshold?: number): {
|
|
807
|
+
device: MidiDevice | ComputerKeyboardInput;
|
|
808
|
+
score: number;
|
|
809
|
+
} | null;
|
|
796
810
|
addListener(callback: ListenerCallback): void;
|
|
797
811
|
private initializeDevices;
|
|
798
812
|
private addComputerKeyboard;
|
|
@@ -850,6 +864,10 @@ declare class Engine {
|
|
|
850
864
|
findIO(moduleId: string, ioName: string, type: "input" | "output"): Base;
|
|
851
865
|
findMidiDevice(id: string): MidiDevice | ComputerKeyboardInput | undefined;
|
|
852
866
|
findMidiDeviceByName(name: string): MidiDevice | ComputerKeyboardInput | undefined;
|
|
867
|
+
findMidiDeviceByFuzzyName(name: string, threshold?: number): {
|
|
868
|
+
device: MidiDevice | ComputerKeyboardInput;
|
|
869
|
+
score: number;
|
|
870
|
+
} | null;
|
|
853
871
|
onPropsUpdate(callback: <T extends ModuleType>(params: IModule<T> | IPolyModule<T>) => void): void;
|
|
854
872
|
_triggerPropsUpdate<T extends ModuleType>(params: IModule<T> | IPolyModule<T>): void;
|
|
855
873
|
triggerVirtualMidi(id: string, noteName: string, type: "noteOn" | "noteOff"): void;
|
|
@@ -857,4 +875,4 @@ declare class Engine {
|
|
|
857
875
|
private onStop;
|
|
858
876
|
}
|
|
859
877
|
|
|
860
|
-
export { type ArrayProp, type BooleanProp, Engine, type EnumProp, type ICreateModule, type ICreateRoute, type IEngineSerialize, type IGain, type IIOSerialize, type IMaster, type IMidiDevice, type IMidiMapper, type IMidiMapperProps, type IModule, type IModuleSerialize, type INote, type IOscillator, type IPolyModuleSerialize, type IRoute, type ISequence, type IStepSequencer, type IStepSequencerProps, type IUpdateModule, MidiDevice, type MidiMapping, MidiMappingMode, MidiPortState, type ModuleParams, type ModulePropSchema, ModuleType, type ModuleTypeToPropsMapping, Note, type NumberProp, OscillatorWave, type PropSchema, type StringProp, moduleSchemas };
|
|
878
|
+
export { type ArrayProp, type BooleanProp, Engine, type EnumProp, type IAnyModuleSerialize, type ICreateModule, type ICreateRoute, type IEngineSerialize, type IGain, type IIOSerialize, type IMaster, type IMidiDevice, type IMidiMapper, type IMidiMapperProps, type IModule, type IModuleSerialize, type INote, type IOscillator, type IPolyModuleSerialize, type IRoute, type ISequence, type IStepSequencer, type IStepSequencerProps, type IUpdateModule, MidiDevice, type MidiMapping, MidiMappingMode, MidiPortState, type ModuleParams, type ModulePropSchema, ModuleType, type ModuleTypeToPropsMapping, Note, type NumberProp, OscillatorWave, type PropSchema, type StringProp, moduleSchemas };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Transport as Ft}from"@blibliki/transport";import{assertDefined as it,Context as Gt,pick as qt,uuidv4 as Vt}from"@blibliki/utils";import{upperFirst as lt,uuidv4 as ct}from"@blibliki/utils";import{assertNever as ut}from"@blibliki/utils";import{sortBy as pt}from"es-toolkit";import{deterministicId as rt,uuidv4 as nt}from"@blibliki/utils";var m=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;_voices;_name;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voices:s,monoModuleConstructor:d,props:u}=t;this.audioModules=[],this.monoModuleConstructor=d,this.id=o??nt(),this.engineId=e,this.name=i,this.moduleType=r,this._props=u,this.inputs=new x(this),this.outputs=new S(this),queueMicrotask(()=>{this.voices=s||1,this.props=u})}get name(){return this._name}set name(e){this._name=e,this.audioModules.forEach(t=>t.name=e)}get props(){return this._props}set props(e){this._props={...this._props,...e},this.audioModules.forEach(t=>t.props=e)}get voices(){return this._voices}set voices(e){this._voices=e,this.adjustNumberOfModules(),this.rePlugAll()}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voices:this.voices,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll(),this.audioModules.forEach(e=>{e.dispose()})}onMidiEvent=e=>{let t=e.voiceNo??0;this.findVoice(t).onMidiEvent(e)};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){requestAnimationFrame(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voices:this.voices,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}findVoice(e){let t=this.audioModules.find(o=>o.voiceNo===e);if(!t)throw Error(`Voice ${e} on module ${this.name} not found`);return t}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),(e==="in"||e==="both")&&this.registerAudioInput({name:"in"}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out"})}registerAudioInput(e){return this.inputs.add({...e,ioType:"polyAudioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"polyAudioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}adjustNumberOfModules(){if(this.audioModules.length!==this.voices){if(this.audioModules.length>this.voices)this.audioModules.pop()?.dispose();else{let e=this.audioModules.length,t=rt(this.id,e.toString()),o=this.monoModuleConstructor(this.engineId,{id:t,name:this.name,moduleType:this.moduleType,voiceNo:e,props:{...this.props}});this.audioModules.push(o)}this.adjustNumberOfModules()}}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{AudioParam as Se}from"@blibliki/utils/web-audio-api";import{deterministicId as st}from"@blibliki/utils";var pe=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=st(this.module.id,this.name),this.connections=[]}plug(e,t=!0){this.connections.push(e),t&&e.plug(this,!1)}unPlug(e,t=!0){this.connections=this.connections.filter(o=>o.id!==e.id),t&&e.unPlug(this,!1)}rePlugAll(e){let t=this.connections;this.unPlugAll(),e&&e(),t.forEach(o=>{this.plug(o)})}unPlugAll(){this.connections.forEach(e=>{this.unPlug(e)})}isAudio(){return this.ioType==="audioInput"||this.ioType==="audioOutput"||this.ioType==="polyAudioInput"||this.ioType==="polyAudioOutput"}isMidi(){return this.ioType==="midiInput"||this.ioType==="midiOutput"}serialize(){return{id:this.id,name:this.name,ioType:this.ioType,moduleId:this.module.id}}},g=class extends pe{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var P=class extends g{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof b)&&ee(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof b)&&ee(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},b=class extends g{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof P)&&ee(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof P)&&ee(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function ee(n,e,t){if(e instanceof P||e instanceof b){let o=Math.max(n.module.voices,e.module.voices);for(let i=0;i<o;i++){let r=n.findIOByVoice(i%n.module.voices),s=e.findIOByVoice(i%e.module.voices);t?r.plug(s):r.unPlug(s)}}else for(let o=0;o<n.module.voices;o++){let i=n.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var w=class extends g{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},D=class extends g{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof P)return;let o=e.getAudioNode();o instanceof Se?this.getAudioNode().connect(o):this.getAudioNode().connect(o)}unPlug(e,t=!0){if(super.unPlug(e,t),e instanceof P)return;let o=e.getAudioNode();try{o instanceof Se?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var C=class extends g{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},F=class extends g{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof C)}};var G=class{module;collection=[];collectionType;constructor(e,t){this.collectionType=e,this.module=t}add(e){let t;switch(this.validateUniqName(e.name),e.ioType){case"audioInput":if(this.module instanceof m)throw Error("Not compatible");t=new w(this.module,e);break;case"audioOutput":if(this.module instanceof m)throw Error("Not compatible");t=new D(this.module,e);break;case"polyAudioInput":if(this.module instanceof p)throw Error("Not compatible");t=new P(this.module,e);break;case"polyAudioOutput":if(this.module instanceof p)throw Error("Not compatible");t=new b(this.module,e);break;case"midiInput":t=new C(this.module,e);break;case"midiOutput":t=new F(this.module,e);break;default:ut(e)}return this.collection.push(t),t}unPlugAll(){this.collection.forEach(e=>{e.unPlugAll()})}rePlugAll(e){this.collection.forEach(t=>{t.rePlugAll(e)})}find(e){let t=this.collection.find(o=>o.id===e);if(!t)throw Error(`The io with id ${e} is not exists`);return t}findByName(e){let t=this.collection.find(o=>o.name===e);if(!t)throw Error(`The io with name ${e} is not exists`);return t}serialize(){return pt(this.collection,[e=>e.isMidi()?-1:1]).map(e=>e.serialize())}validateUniqName(e){if(this.collection.some(t=>t.name===e))throw Error(`An io with name ${e} is already exists`)}},x=class extends G{constructor(e){super("Input",e)}},S=class extends G{constructor(e){super("Output",e)}};var dt=new Map([["C0",16.35],["C#0",17.32],["Db0",17.32],["D0",18.35],["D#0",19.45],["Eb0",19.45],["E0",20.6],["F0",21.83],["F#0",23.12],["Gb0",23.12],["G0",24.5],["G#0",25.96],["Ab0",25.96],["A0",27.5],["A#0",29.14],["Bb0",29.14],["B0",30.87],["C1",32.7],["C#1",34.65],["Db1",34.65],["D1",36.71],["D#1",38.89],["Eb1",38.89],["E1",41.2],["F1",43.65],["F#1",46.25],["Gb1",46.25],["G1",49],["G#1",51.91],["Ab1",51.91],["A1",55],["A#1",58.27],["Bb1",58.27],["B1",61.74],["C2",65.41],["C#2",69.3],["Db2",69.3],["D2",73.42],["D#2",77.78],["Eb2",77.78],["E2",82.41],["F2",87.31],["F#2",92.5],["Gb2",92.5],["G2",98],["G#2",103.83],["Ab2",103.83],["A2",110],["A#2",116.54],["Bb2",116.54],["B2",123.47],["C3",130.81],["C#3",138.59],["Db3",138.59],["D3",146.83],["D#3",155.56],["Eb3",155.56],["E3",164.81],["F3",174.61],["F#3",185],["Gb3",185],["G3",196],["G#3",207.65],["Ab3",207.65],["A3",220],["A#3",233.08],["Bb3",233.08],["B3",246.94],["C4",261.63],["C#4",277.18],["Db4",277.18],["D4",293.66],["D#4",311.13],["Eb4",311.13],["E4",329.63],["F4",349.23],["F#4",369.99],["Gb4",369.99],["G4",392],["G#4",415.3],["Ab4",415.3],["A4",440],["A#4",466.16],["Bb4",466.16],["B4",493.88],["C5",523.25],["C#5",554.37],["Db5",554.37],["D5",587.33],["D#5",622.25],["Eb5",622.25],["E5",659.26],["F5",698.46],["F#5",739.99],["Gb5",739.99],["G5",783.99],["G#5",830.61],["Ab5",830.61],["A5",880],["A#5",932.33],["Bb5",932.33],["B5",987.77],["C6",1046.5],["C#6",1108.73],["Db6",1108.73],["D6",1174.66],["D#6",1244.51],["Eb6",1244.51],["E6",1318.51],["F6",1396.91],["F#6",1479.98],["Gb6",1479.98],["G6",1567.98],["G#6",1661.22],["Ab6",1661.22],["A6",1760],["A#6",1864.66],["Bb6",1864.66],["B6",1975.53],["C7",2093],["C#7",2217.46],["Db7",2217.46],["D7",2349.32],["D#7",2489.02],["Eb7",2489.02],["E7",2637.02],["F7",2793.83],["F#7",2959.96],["Gb7",2959.96],["G7",3135.96],["G#7",3322.44],["Ab7",3322.44],["A7",3520],["A#7",3729.31],["Bb7",3729.31],["B7",3951.07],["C8",4186.01],["C#8",4434.92],["Db8",4434.92],["D8",4698.64],["D#8",4978.03],["Eb8",4978.03]]),de=dt;var ae=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],at=2,a=class n{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of de)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new n(t)}static fromEvent(e){let t=ae[e.data[1]%12],o=Math.floor(e.data[1]/12)-2;return new n(`${t}${o}`)}static notes(e=3){return ae.map(t=>new n(`${t}${e}`))}constructor(e){typeof e=="string"?this.fromString(e):this.fromProps(e)}get isSemi(){return this.name.endsWith("#")}get fullName(){return`${this.name}${this.octave}`}get frequency(){return de.get(`${this.name}${this.octave}`)}midiData(e=!0){let t=e?144:128;return new Uint8Array([t,this.midiNumber,this.velocity*100])}get midiNumber(){return(this.octave+at)*12+this.noteIndex}get noteIndex(){return ae.indexOf(this.name)}valueOf(){return this.fullName}serialize(){return{name:this.name,octave:this.octave,frequency:this.frequency,velocity:this.velocity,duration:this.duration}}fromString(e){let t=/(\w#?)(\d)?/.exec(e)??[];this.name=t[1],this.octave=t[2]?parseInt(t[2]):1}fromProps(e){Object.assign(this,e)}};var v=class{data;constructor(e){this.data=e}get dataBytes(){return Array.from(this.data.slice(1))}get type(){switch(this.data[0]&240){case 144:return this.data[2]===0?"noteoff":"noteon";case 128:return"noteoff";case 176:return"controlchange";case 224:return"pitchbend";case 208:return"channelaftertouch";case 160:return"keyaftertouch";case 192:return"programchange";default:return"unknown"}}};var f=class n{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof a?e:new a(e);return new n(new v(i.midiData(t)),o)}static fromCC(e,t,o){return new n(new v(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t,this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}get isCC(){return this.type==="controlchange"}get cc(){if(this.isCC)return this.message.dataBytes[0]}get ccValue(){if(this.isCC)return this.message.dataBytes[1]}defineNotes(){this.isNote&&(this.note||(this.note=a.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new n(this.message,this.triggeredAt);return t.voiceNo=e,t}};var p=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;activeNotes;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voiceNo:s,audioNodeConstructor:d,props:u}=t;this.id=o??ct(),this.engineId=e,this.name=i,this.moduleType=r,this.voiceNo=s??0,this.activeNotes=[],this.audioNode=d?.(this.context),this._props=u,this.inputs=new x(this),this.outputs=new S(this),queueMicrotask(()=>{this.props=u})}get props(){return this._props}set props(e){let t={...e};Object.keys(e).forEach(o=>{let i=e[o];if(i!==void 0){let r=this.callPropHook("onSet",o,i);r!==void 0&&(t[o]=r)}}),this._props={...this._props,...t},Object.keys(t).forEach(o=>{let i=t[o];i!==void 0&&this.callPropHook("onAfterSet",o,i)})}callPropHook(e,t,o){let i=`${e}${lt(t)}`,r=this[i];if(typeof r=="function")return r.call(this,o)}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voiceNo:this.voiceNo,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}start(e){}stop(e){}triggerAttack(e,t){this.activeNotes.some(o=>o.fullName===e.fullName)||this.activeNotes.push(e)}triggerRelease(e,t){this.activeNotes=this.activeNotes.filter(o=>o.fullName!==e.fullName)}handleCC(e,t){}onMidiEvent=e=>{let{note:t,triggeredAt:o}=e;switch(e.type){case"noteon":{this.triggerAttack(t,o);break}case"noteoff":this.triggerRelease(t,o);break;case"controlchange":this.handleCC(e,o);break;default:throw Error("This type is not a note")}};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){requestAnimationFrame(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),this.audioNode&&((e==="in"||e==="both")&&this.registerAudioInput({name:"in",getAudioNode:()=>this.audioNode}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out",getAudioNode:()=>this.audioNode}))}registerAudioInput(e){return this.inputs.add({...e,ioType:"audioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"audioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as mt}from"@blibliki/utils";var q=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??mt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){this.routes.forEach((e,t)=>{this.removeRoute(t)})}replug(){this.routes.forEach((e,t)=>{let{sourceIO:o,destinationIO:i}=this.getIOs(t);o.rePlugAll(),i.rePlugAll()})}serialize(){return Array.from(this.routes.values())}plug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.plug(o)}unPlug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.unPlug(o)}find(e){let t=this.routes.get(e);if(!t)throw Error(`Route with id ${e} not found`);return t}getIOs(e){let t=this.find(e),{source:o,destination:i}=t,r=this.engine.findIO(o.moduleId,o.ioName,"output"),s=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:r,destinationIO:s}}};var te=(t=>(t.connected="connected",t.disconnected="disconnected",t))(te||{}),A=class{id;name;eventListerCallbacks=[];context;input;messageHandler=null;constructor(e,t){this.id=e.id,this.name=e.name,this.input=e,this.context=t,this.connect()}get state(){return this.input.state}connect(){this.messageHandler=e=>{this.processEvent(e)},this.input.addEventListener(this.messageHandler)}disconnect(){this.messageHandler&&(this.input.removeEventListener(this.messageHandler),this.messageHandler=null)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}processEvent(e){let t=new v(e.data),o=new f(t,this.context.browserToContextTime(e.timeStamp));switch(o.type){case"noteon":case"noteoff":case"controlchange":this.eventListerCallbacks.forEach(i=>{i(o)})}}};var Mt={a:new a("C3"),s:new a("D3"),d:new a("E3"),f:new a("F3"),g:new a("G3"),h:new a("A3"),j:new a("B3"),k:new a("C4"),l:new a("D4"),w:new a("C#3"),e:new a("D#3"),t:new a("F#3"),y:new a("G#3"),u:new a("A#3"),o:new a("C#4"),p:new a("D#4")},ht=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),N=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=ht();this.id=t,this.name=o,this.state=i,this.context=e,document.addEventListener("keydown",this.onKeyTrigger(!0)),document.addEventListener("keyup",this.onKeyTrigger(!1))}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}onKeyTrigger=e=>t=>{let o=this.extractNote(t);if(!o)return;let i=f.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(r=>{r(i)})};extractNote(e){if(!e.repeat)return Mt[e.key]}};import{isNode as yt}from"es-toolkit";import{isNode as ft}from"es-toolkit";var ce=class{id;name;portIndex;input;callbacks=new Set;handler=null;_state="disconnected";constructor(e,t,o){this.portIndex=e,this.id=`node-midi-${e}`,this.name=t,this.input=o}get state(){return this._state}setState(e){this._state=e}addEventListener(e){if(this.callbacks.size===0){this.handler=(t,o)=>{let i={data:new Uint8Array(o),timeStamp:performance.now()};this.callbacks.forEach(r=>{r(i)})};try{this.input.isPortOpen()||(this.input.openPort(this.portIndex),this._state="connected"),this.input.on("message",this.handler)}catch(t){console.error(`Error opening MIDI port ${this.portIndex}:`,t)}}this.callbacks.add(e)}removeEventListener(e){if(this.callbacks.delete(e),this.callbacks.size===0&&this.handler){try{this.input.off("message",this.handler),this.input.isPortOpen()&&(this.input.closePort(),this._state="disconnected")}catch(t){console.error(`Error closing MIDI port ${this.portIndex}:`,t)}this.handler=null}}},me=class{ports=new Map;MidiModule;constructor(e){this.MidiModule=e,this.scanPorts()}scanPorts(){try{let e=new this.MidiModule.Input,t=e.getPortCount();for(let o=0;o<t;o++){let i=e.getPortName(o),r=`node-midi-${o}`;if(!this.ports.has(r)){let s=new this.MidiModule.Input,d=new ce(o,i,s);this.ports.set(r,d)}}e.isPortOpen()&&e.closePort()}catch(e){console.error("Error scanning MIDI ports:",e)}}*inputs(){for(let[,e]of this.ports)yield e}addEventListener(e,t){console.warn("Hot-plug detection not supported with node-midi adapter. Restart required for new devices.")}},V=class{async requestMIDIAccess(){try{let e=await import("@julusian/midi"),t="default"in e?e.default:e;return new me(t)}catch(e){return console.error("Error loading node-midi:",e),null}}isSupported(){return ft()}};var oe=class{input;callbacks=new Set;handler=null;constructor(e){this.input=e}get id(){return this.input.id}get name(){return this.input.name??`Device ${this.input.id}`}get state(){return this.input.state}addEventListener(e){this.callbacks.size===0&&(this.handler=t=>{if(!t.data)return;let o={data:t.data,timeStamp:t.timeStamp};this.callbacks.forEach(i=>{i(o)})},this.input.addEventListener("midimessage",this.handler)),this.callbacks.add(e)}removeEventListener(e){this.callbacks.delete(e),this.callbacks.size===0&&this.handler&&(this.input.removeEventListener("midimessage",this.handler),this.handler=null)}},Me=class{midiAccess;portCache=new Map;constructor(e){this.midiAccess=e}*inputs(){for(let[,e]of this.midiAccess.inputs)this.portCache.has(e.id)||this.portCache.set(e.id,new oe(e)),yield this.portCache.get(e.id)}addEventListener(e,t){this.midiAccess.addEventListener(e,o=>{let i=o.port;if(!i||i.type!=="input")return;let r=i;this.portCache.has(r.id)||this.portCache.set(r.id,new oe(r)),t(this.portCache.get(r.id))})}},z=class{async requestMIDIAccess(){try{if(typeof navigator>"u"||typeof navigator.requestMIDIAccess!="function")return null;let e=await navigator.requestMIDIAccess();return new Me(e)}catch(e){return console.error("Error enabling Web MIDI API:",e),null}}isSupported(){return typeof navigator<"u"&&typeof navigator.requestMIDIAccess=="function"}};function Oe(){return yt()?new V:new z}var E=class{devices=new Map;initialized=!1;listeners=[];context;midiAccess=null;adapter=Oe();constructor(e){this.context=e,this.addComputerKeyboard()}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.devices.get(e)}findByName(e){return Array.from(this.devices.values()).find(t=>t.name===e)}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{if(!this.adapter.isSupported()){console.warn("MIDI is not supported on this platform");return}if(this.midiAccess=await this.adapter.requestMIDIAccess(),!this.midiAccess){console.error("Failed to get MIDI access");return}for(let e of this.midiAccess.inputs())this.devices.has(e.id)||this.devices.set(e.id,new A(e,this.context))}catch(e){console.error("Error enabling MIDI:",e)}}addComputerKeyboard(){if(typeof document>"u")return;let e=new N(this.context);this.devices.set(e.id,e)}listenChanges(){this.midiAccess&&this.midiAccess.addEventListener("statechange",e=>{if(e.state==="connected"){if(this.devices.has(e.id))return;let t=new A(e,this.context);this.devices.set(t.id,t),this.listeners.forEach(o=>{o(t)})}else{let t=this.devices.get(e.id);if(!t||t instanceof N)return;t.disconnect(),this.devices.delete(t.id),this.listeners.forEach(o=>{o(t)})}})}};import{assertNever as Dt}from"@blibliki/utils";var be={},Ce={},he=class extends p{activeNote=null;triggeredAt=0;constructor(e,t){let o={...Ce,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let r=o.fullName;switch(i){case"noteon":this.activeNote=r,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},R=class extends m{midiOutput;constructor(e,t){let o={...Ce,...t.props},i=(r,s)=>new he(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerOutputs()}onMidiEvent=e=>{let t;switch(e.type){case"noteon":t=this.findFreeVoice();break;case"noteoff":t=this.audioModules.find(o=>o.activeNote===e.note.fullName);break;default:throw Error("This type is not a note")}t&&(t.midiTriggered(e),e.voiceNo=t.voiceNo,this.midiOutput.onMidiEvent(e))};findFreeVoice(){let e=this.audioModules.find(t=>!t.activeNote);return e??=this.audioModules.sort((t,o)=>t.triggeredAt-o.triggeredAt)[0],e}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{ConstantSourceNode as Ne}from"@blibliki/utils/web-audio-api";var Ee={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},gt={value:1},L=class extends p{isStated=!1;constructor(e,t){let o={...gt,...t.props},i=r=>new Ne(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onAfterSetValue=e=>{this.audioNode.offset.value=e};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new Ne(this.context.audioContext,{offset:this.props.value})}),this.isStated=!1)}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,t),this.start(t)};triggerRelease=()=>{}};import{cancelAndHoldAtTime as ke}from"@blibliki/utils";import{GainNode as It}from"@blibliki/utils/web-audio-api";var we={attack:.01,decay:0,sustain:1,release:0},De={attack:{kind:"number",min:1e-4,max:20,step:.01,exp:3,label:"Attack"},decay:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Release"}},fe=class extends p{constructor(e,t){let o={...we,...t.props},i=r=>{let s=new It(r.audioContext);return s.gain.value=0,s};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let o=this.props.attack,i=this.props.decay,r=this.props.sustain;ke(this.audioNode.gain,t),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,t),this.audioNode.gain.exponentialRampToValueAtTime(1,t+o),r>0?this.audioNode.gain.exponentialRampToValueAtTime(r,t+o+i):this.audioNode.gain.exponentialRampToValueAtTime(.001,t+o+i)}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let o=this.props.release,i=ke(this.audioNode.gain,t);i>=1e-4&&(this.audioNode.gain.setValueAtTime(i,t),this.audioNode.gain.exponentialRampToValueAtTime(1e-4,t+o-1e-4)),this.audioNode.gain.setValueAtTime(0,t+o)}},_=class extends m{constructor(e,t){let o={...we,...t.props},i=(r,s)=>new fe(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{BiquadFilterNode as Tt}from"@blibliki/utils/web-audio-api";import{GainNode as Pt}from"@blibliki/utils/web-audio-api";var Fe={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},Ge={gain:1},U=class extends p{constructor(e,t){let o={...Ge,...t.props},i=r=>new Pt(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetGain=e=>{this.audioNode.gain.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},B=class extends m{constructor(e,t){let o={...Ge,...t.props},i=(r,s)=>new U(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var qe=20,ge=2e4,Ve={cutoff:ge,envelopeAmount:0,type:"lowpass",Q:1},ze={cutoff:{kind:"number",min:qe,max:ge,step:1,exp:5,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},type:{kind:"enum",options:["lowpass","highpass","bandpass"],label:"Type"},Q:{kind:"number",min:1e-4,max:1e3,step:.1,exp:5,label:"Q"}},ye=class extends p{scale;amount;constructor(e,t){let o={...Ve,...t.props},i=r=>new Tt(r.audioContext,{type:o.type,frequency:0,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new U(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=re(e,{name:"scale",moduleType:"Scale",props:{min:qe,max:ge,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.audioNode.frequency),this.registerDefaultIOs(),this.registerInputs()}onAfterSetType=e=>{this.audioNode.type=e};onAfterSetCutoff=e=>{this.scale.props={current:e}};onAfterSetQ=e=>{this.audioNode.Q.value=e};onAfterSetEnvelopeAmount=e=>{this.amount.props={gain:e}};registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.audioNode.frequency}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.audioNode.Q})}},H=class extends m{constructor(e,t){let o={...Ve,...t.props},i=(r,s)=>new ye(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs()}registerInputs(){this.registerAudioInput({name:"cutoff"}),this.registerAudioInput({name:"cutoffMod"}),this.registerAudioInput({name:"Q"})}};import{AnalyserNode as vt}from"@blibliki/utils/web-audio-api";var Re={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},At={fftSize:512},K=class extends p{_buffer;constructor(e,t){let o={...At,...t.props},i=r=>new vt(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onAfterSetFftSize=e=>{this._buffer=new Float32Array(e)};get buffer(){return this._buffer?this._buffer:(this._buffer=new Float32Array(this.props.fftSize),this._buffer)}getValue(){return this.getValues()[0]}getValues(){return this.audioNode.getFloatTimeDomainData(this.buffer),this.buffer}};var xt={},Le={},$=class extends p{constructor(e,t){let o={...xt,...t.props},i=r=>r.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var Ie=(s=>(s.direct="direct",s.directRev="directRev",s.toggleInc="toggleInc",s.toggleDec="toggleDec",s.incDec="incDec",s.incDecRev="incDecRev",s))(Ie||{}),_e={pages:{kind:"array",label:"Midi mapping pages"},activePage:{kind:"number",label:"Active page",min:0,max:100,step:1},globalMappings:{kind:"array",label:"Global midi mappings"}},St={pages:[{name:"Page 1",mappings:[{}]}],activePage:0,globalMappings:[{}]};function Ot({value:n,midiValue:e,propSchema:t,mapping:o}){let i=t.min??0,r=t.max??1,s=t.exp??1,{threshold:d=64,mode:u}=o,c=(n-i)/(r-i),M=Math.pow(c,1/s)*127;return M=e>=d&&u==="incDec"||e<=d&&u==="incDecRev"?M+1:M-1,Math.round(Math.max(0,Math.min(127,M)))}var j=class extends p{constructor(e,t){let o={...St,...t.props};super(e,{...t,props:o}),this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}onSetActivePage=e=>Math.max(Math.min(e,this.props.pages.length-1),0);handleCC=(e,t)=>{this.checkAutoAssign(e);let o=this.props.pages[this.props.activePage];[...this.props.globalMappings.filter(i=>i.cc===e.cc),...o.mappings.filter(i=>i.cc===e.cc)].forEach(i=>{this.forwardMapping(e,i,t)})};forwardMapping=(e,t,o)=>{if(t.moduleId===void 0||t.moduleType===void 0||t.propName===void 0)return;let i=t.propName,r=e.ccValue;if(r===void 0)return;let s=t.mode??"direct";if((s==="toggleInc"||s==="toggleDec")&&r!==127)return;let d=this.engine.findModule(t.moduleId),u=Pe[d.moduleType][i],c;switch(u.kind){case"number":{let h=d.props[i];if(s==="incDec"||s==="incDecRev"?r=Ot({value:h,propSchema:u,mapping:t,midiValue:r}):s==="directRev"&&(r=127-r),s==="toggleInc")c=h+(u.step??1);else if(s==="toggleDec")c=h-(u.step??1);else{let M=u.min??0,I=u.max??1,y=r/127,l=Math.pow(y,u.exp??1);if(c=M+l*(I-M),u.step!==void 0&&(!u.exp||u.exp===1)){let k=Math.round((c-M)/u.step);c=M+k*u.step}}break}case"enum":{let h=Math.floor(r/127*u.options.length),M=Math.min(h,u.options.length-1);c=u.options[M];break}case"boolean":c=r>=64;break;case"string":throw Error("MidiMapper not support string type of values");case"array":throw Error("MidiMapper not support array type of values");default:throw Error("MidiMapper unknown type")}d.props={[i]:c},d.triggerPropsUpdate()};checkAutoAssign(e){if(e.cc===void 0)return;let t=this.props.pages[this.props.activePage],o=this.props.globalMappings.some(({autoAssign:u})=>u),i=t.mappings.some(({autoAssign:u})=>u);if(!o&&!i)return;let r=o?this.props.globalMappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):this.props.globalMappings,s=i?t.mappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):t.mappings,d=this.props.pages.map((u,c)=>c===this.props.activePage?{...u,mappings:s}:u);this.props={pages:d,globalMappings:r},this.triggerPropsUpdate()}};var Ue={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},bt={selectedId:void 0,selectedName:void 0},W=class extends p{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...bt,...t.props};super(e,{...t,props:o});let i=(this.props.selectedId&&this.engine.findMidiDevice(this.props.selectedId))??(this.props.selectedName&&this.engine.findMidiDeviceByName(this.props.selectedName));i&&this.addEventListener(i),this.registerOutputs()}onSetSelectedId=e=>{if(this.removeEventListener(),!e)return e;let t=this.engine.findMidiDevice(e);return t&&(this.props={selectedName:t.name},this.addEventListener(t)),e};get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){e.addEventListener(this.forwardMidiEvent)}removeEventListener(){if(!this.props.selectedId)return;this.engine.findMidiDevice(this.props.selectedId)?.removeEventListener(this.forwardMidiEvent)}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{dbToGain as Be}from"@blibliki/utils";import{GainNode as He,OscillatorNode as Ke}from"@blibliki/utils/web-audio-api";var Te=-18,ne=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(ne||{}),$e={wave:{kind:"enum",options:Object.values(ne),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-1,max:2,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${Te}db Gain`}},je={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},ve=class extends p{isStated=!1;outputGain;detuneGain;constructor(e,t){let o={...je,...t.props},i=r=>new Ke(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.outputGain=new He(this.context.audioContext,{gain:Be(Te)}),this.applyOutputGain(),this.initializeGainDetune(),this.registerInputs(),this.registerOutputs()}onAfterSetWave=e=>{this.audioNode.type=e};onAfterSetFrequency=()=>{this.updateFrequency()};onAfterSetFine=()=>{this.updateFrequency()};onAfterSetCoarse=()=>{this.updateFrequency()};onAfterSetOctave=()=>{this.updateFrequency()};onAfterSetLowGain=e=>{this.outputGain.gain.value=e?Be(Te):1};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new Ke(this.context.audioContext,{type:this.props.wave,frequency:this.finalFrequency}),this.applyOutputGain(),this.detuneGain.connect(this.audioNode.detune)}),this.isStated=!1)}triggerAttack=(e,t)=>{super.triggerAttack(e,t),this.props={frequency:e.frequency},this.updateFrequency(t),this.start(t)};triggerRelease(e,t){super.triggerRelease(e,t);let o=this.activeNotes.length?this.activeNotes[this.activeNotes.length-1]:null;o&&(this.props={frequency:o.frequency},this.updateFrequency(t))}get finalFrequency(){let{frequency:e,coarse:t,octave:o,fine:i}=this.props;return e*Math.pow(2,t/12+o+i/12)}updateFrequency(e){this.finalFrequency!==void 0&&(e?this.audioNode.frequency.setValueAtTime(this.finalFrequency,e):this.audioNode.frequency.value=this.finalFrequency)}applyOutputGain(){this.audioNode.connect(this.outputGain)}initializeGainDetune(){this.detuneGain=new He(this.context.audioContext,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputGain})}},Q=class extends m{constructor(e,t){let o={...je,...t.props},i=(r,s)=>new ve(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs("out")}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}registerInputs(){this.registerAudioInput({name:"detune"})}};import{assertNever as Ct}from"@blibliki/utils";var We=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{s0;s1;constructor(){super(),this.s0=0,this.s1=0}static get parameterDescriptors(){return[{name:"cutoff",defaultValue:1e3,minValue:20,maxValue:2e4},{name:"resonance",defaultValue:0,minValue:0,maxValue:4}]}process(t,o,i){let r=t[0],s=o[0],d=i.cutoff,u=i.resonance;for(let c=0;c<r.length;c++){let h=r[c],M=s[c];for(let I=0;I<h.length;I++){let y=h[I],l=d.length>1?d[I]:d[0],k=Math.max(20,Math.min(2e4,l)),se=Math.log(k/20)/Math.log(2e4/20),T=Math.pow(.5,(1-se)/.125),xe=1-Math.pow(.5,((u.length>1?u[I]:u[0])+.125)/.125)*T;this.s0=xe*this.s0-T*this.s1+T*y,this.s1=xe*this.s1+T*this.s0,M[I]=this.s1}}return!0}}registerProcessor("filter-processor",n)}).toString(),")()"],{type:"application/javascript"}));var Qe=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let r=t[0],s=o[0],d=i.min,u=i.max,c=i.current;if(!r.length||r[0].length===0){for(let h of s){let M=(i.current.length>1,i.current[0]);h.fill(M)}return!0}for(let h=0;h<r.length;h++){let M=r[h],I=s[h];for(let y=0;y<M.length;y++){let l=M[y],k=d.length>1?d[y]:d[0],se=u.length>1?u[y]:u[0],T=c.length>1?c[y]:c[0];l<0?I[y]=T*Math.pow(k/T,-l):I[y]=T*Math.pow(se/T,l)}}return!0}}registerProcessor("scale-processor",n)}).toString(),")()"],{type:"application/javascript"}));async function Ye(n){await n.addModule(Qe),await n.addModule(We)}function Je(n,e){switch(e){case"ScaleProcessor":return n.newAudioWorklet("scale-processor");case"FilterProcessor":return n.newAudioWorklet("filter-processor");default:Ct(e)}}var Xe={min:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Min"},max:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Max"},current:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Current"}},Nt={min:0,max:1,current:.5},Y=class extends p{constructor(e,t){let o={...Nt,...t.props},i=r=>Je(r,"ScaleProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}get current(){return this.audioNode.parameters.get("current")}get min(){return this.audioNode.parameters.get("min")}get max(){return this.audioNode.parameters.get("max")}onAfterSetMin=e=>{this.min.value=e};onAfterSetMax=e=>{this.max.value=e};onAfterSetCurrent=e=>{this.current.value=e}};var Ze={steps:{kind:"number",min:1,max:16,step:1,label:"Steps"},bars:{kind:"number",min:1,max:16,step:1,label:"Steps"}},Et={sequences:[],steps:16,bars:1},J=class extends p{midiOutput;constructor(e,t){let o={...Et,...t.props};super(e,{...t,props:o})}};import{StereoPannerNode as kt}from"@blibliki/utils/web-audio-api";var et={pan:{kind:"number",min:-1,max:1,step:.01,label:"Pan"}},tt={pan:0},Ae=class extends p{constructor(e,t){let o={...tt,...t.props},i=r=>new kt(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetPan=e=>{this.audioNode.pan.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"pan",getAudioNode:()=>this.audioNode.pan})}},X=class extends m{constructor(e,t){let o={...tt,...t.props},i=(r,s)=>new Ae(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"pan"})}};var ot={activeNotes:{kind:"array",label:"Active notes"}},wt={activeNotes:[]},Z=class extends p{midiOutput;constructor(e,t){let o={...wt,...t.props};super(e,{...t,props:o}),this.registerInputs(),this.registerOutputs()}sendMidi(e){this.midiOutput.onMidiEvent(e)}triggerAttack=(e,t)=>{this.props={activeNotes:[...this.props.activeNotes,e.fullName]},this.triggerPropsUpdate(),this.sendMidi(f.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(f.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var ie=(l=>(l.Master="Master",l.Oscillator="Oscillator",l.Gain="Gain",l.MidiSelector="MidiSelector",l.Envelope="Envelope",l.Filter="Filter",l.Scale="Scale",l.StereoPanner="StereoPanner",l.Inspector="Inspector",l.Constant="Constant",l.MidiMapper="MidiMapper",l.VirtualMidi="VirtualMidi",l.StepSequencer="StepSequencer",l.VoiceScheduler="VoiceScheduler",l))(ie||{}),Pe={Oscillator:$e,Gain:Fe,Master:Le,MidiSelector:Ue,Envelope:De,Filter:ze,Scale:Xe,StereoPanner:et,Inspector:Re,Constant:Ee,MidiMapper:_e,VirtualMidi:ot,StepSequencer:Ze,VoiceScheduler:be};function re(n,e){switch(e.moduleType){case"Oscillator":return new Q(n,e);case"Gain":return new B(n,e);case"Master":return new $(n,e);case"MidiSelector":return new W(n,e);case"Envelope":return new _(n,e);case"Filter":return new H(n,e);case"Scale":return new Y(n,e);case"StereoPanner":return new X(n,e);case"Inspector":return new K(n,e);case"Constant":return new L(n,e);case"MidiMapper":return new j(n,e);case"VirtualMidi":return new Z(n,e);case"StepSequencer":return new J(n,e);case"VoiceScheduler":return new R(n,e);default:Dt(e)}}var O=class n{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=n._engines.get(e);return it(t),t}static get current(){return it(this._currentId),this.getById(this._currentId)}static async load(e){let{bpm:t,timeSignature:o,modules:i,routes:r}=e,s=new Gt,d=new n(s);return await d.initialize(),d.timeSignature=o,d.bpm=t,i.forEach(u=>{d.addModule(u)}),r.forEach(u=>{d.addRoute(u)}),d}constructor(e){this.id=Vt(),this.context=e,this.transport=new Ft(this.context,{generator:(t,o)=>[],consumer:t=>{},onJump:t=>{},onStart:this.onStart,onStop:this.onStop,silence:t=>{}}),this.routes=new q(this),this.modules=new Map,this.midiDeviceManager=new E(this.context),n._engines.set(this.id,this),n._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await Ye(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=re(this.id,e);return this.modules.set(t.id,t),t.serialize()}updateModule(e){let t=this.findModule(e.id);if(t.moduleType!==e.moduleType)throw Error(`The module id ${e.id} isn't moduleType ${e.moduleType}`);let o=qt(e.changes,["name","props"]);return Object.assign(t,o),t instanceof m&&e.changes.voices!==void 0&&(t.voices=e.changes.voices),t.serialize()}removeModule(e){this.modules.delete(e)}addRoute(e){return this.routes.addRoute(e)}removeRoute(e){this.routes.removeRoute(e)}validRoute(e){let{source:t,destination:o}=e,i=this.findIO(t.moduleId,t.ioName,"output"),r=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&r.isMidi()||i.isAudio()&&r.isAudio()}async start(){await this.resume(),this.transport.start()}stop(){this.transport.stop(),this.transport.reset()}pause(){this.transport.stop()}get bpm(){return this.transport.bpm}set bpm(e){this.transport.bpm=e}get timeSignature(){return this.transport.timeSignature}set timeSignature(e){this.transport.timeSignature=e}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear()}serialize(){return{bpm:this.bpm,timeSignature:this.timeSignature,modules:Array.from(this.modules.values()).map(e=>e.serialize()),routes:this.routes.serialize()}}findModule(e){let t=this.modules.get(e);if(!t)throw Error(`The module with id ${e} is not exists`);return t}findIO(e,t,o){return this.findModule(e)[`${o}s`].findByName(t)}findMidiDevice(e){return this.midiDeviceManager.find(e)}findMidiDeviceByName(e){return this.midiDeviceManager.findByName(e)}onPropsUpdate(e){this.propsUpdateCallbacks.push(e)}_triggerPropsUpdate(e){this.propsUpdateCallbacks.forEach(t=>{t(e)})}triggerVirtualMidi(e,t,o){let i=this.findModule(e);if(i.moduleType!=="VirtualMidi")throw Error("This is not a virtual mid");i.sendMidi(f.fromNote(t,o==="noteOn",this.context.currentTime))}onStart=e=>{this.modules.forEach(t=>{t.start(e)})};onStop=e=>{this.modules.forEach(t=>{t.stop(e)})}};import{TransportState as Es}from"@blibliki/transport";import{Context as ws}from"@blibliki/utils";export{ws as Context,O as Engine,A as MidiDevice,Ie as MidiMappingMode,te as MidiPortState,ie as ModuleType,a as Note,ne as OscillatorWave,Es as TransportState,Pe as moduleSchemas};
|
|
1
|
+
import{Transport as Ut}from"@blibliki/transport";import{assertDefined as dt,Context as _t,pick as Ht,uuidv4 as $t}from"@blibliki/utils";import{upperFirst as gt,uuidv4 as It,requestAnimationFrame as Pt}from"@blibliki/utils";import{assertNever as Mt}from"@blibliki/utils";import{sortBy as ht}from"es-toolkit";import{deterministicId as at,uuidv4 as lt,requestAnimationFrame as ct}from"@blibliki/utils";var h=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;_voices;_name;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voices:s,monoModuleConstructor:p,props:u}=t;this.audioModules=[],this.monoModuleConstructor=p,this.id=o??lt(),this.engineId=e,this.name=i,this.moduleType=r,this._props=u,this.inputs=new b(this),this.outputs=new C(this),queueMicrotask(()=>{this.voices=s||1,this.props=u,this.triggerPropsUpdate()})}get name(){return this._name}set name(e){this._name=e,this.audioModules.forEach(t=>t.name=e)}get props(){return this._props}set props(e){this._props={...this._props,...e},this.audioModules.forEach(t=>t.props=e)}get voices(){return this._voices}set voices(e){this._voices=e,this.adjustNumberOfModules(),this.rePlugAll()}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voices:this.voices,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll(),this.audioModules.forEach(e=>{e.dispose()})}onMidiEvent=e=>{let t=e.voiceNo??0;this.findVoice(t).onMidiEvent(e)};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){ct(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voices:this.voices,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}findVoice(e){let t=this.audioModules.find(o=>o.voiceNo===e);if(!t)throw Error(`Voice ${e} on module ${this.name} not found`);return t}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),(e==="in"||e==="both")&&this.registerAudioInput({name:"in"}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out"})}registerAudioInput(e){return this.inputs.add({...e,ioType:"polyAudioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"polyAudioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}adjustNumberOfModules(){if(this.audioModules.length!==this.voices){if(this.audioModules.length>this.voices)this.audioModules.pop()?.dispose();else{let e=this.audioModules.length,t=at(this.id,e.toString()),o=this.monoModuleConstructor(this.engineId,{id:t,name:this.name,moduleType:this.moduleType,voiceNo:e,props:{...this.props}});this.audioModules.push(o)}this.adjustNumberOfModules()}}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{AudioParam as Ne}from"@blibliki/utils/web-audio-api";import{deterministicId as mt}from"@blibliki/utils";var ae=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=mt(this.module.id,this.name),this.connections=[]}plug(e,t=!0){this.connections.push(e),t&&e.plug(this,!1)}unPlug(e,t=!0){this.connections=this.connections.filter(o=>o.id!==e.id),t&&e.unPlug(this,!1)}rePlugAll(e){let t=this.connections;this.unPlugAll(),e&&e(),t.forEach(o=>{this.plug(o)})}unPlugAll(){this.connections.forEach(e=>{this.unPlug(e)})}isAudio(){return this.ioType==="audioInput"||this.ioType==="audioOutput"||this.ioType==="polyAudioInput"||this.ioType==="polyAudioOutput"}isMidi(){return this.ioType==="midiInput"||this.ioType==="midiOutput"}serialize(){return{id:this.id,name:this.name,ioType:this.ioType,moduleId:this.module.id}}},I=class extends ae{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var P=class extends I{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof N)&&oe(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof N)&&oe(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},N=class extends I{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof P)&&oe(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof P)&&oe(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function oe(n,e,t){if(e instanceof P||e instanceof N){let o=Math.max(n.module.voices,e.module.voices);for(let i=0;i<o;i++){let r=n.findIOByVoice(i%n.module.voices),s=e.findIOByVoice(i%e.module.voices);t?r.plug(s):r.unPlug(s)}}else for(let o=0;o<n.module.voices;o++){let i=n.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var D=class extends I{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},F=class extends I{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof P)return;let o=e.getAudioNode();o instanceof Ne?this.getAudioNode().connect(o):this.getAudioNode().connect(o)}unPlug(e,t=!0){if(super.unPlug(e,t),e instanceof P)return;let o=e.getAudioNode();try{o instanceof Ne?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var E=class extends I{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},z=class extends I{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof E)}};var G=class{module;collection=[];collectionType;constructor(e,t){this.collectionType=e,this.module=t}add(e){let t;switch(this.validateUniqName(e.name),e.ioType){case"audioInput":if(this.module instanceof h)throw Error("Not compatible");t=new D(this.module,e);break;case"audioOutput":if(this.module instanceof h)throw Error("Not compatible");t=new F(this.module,e);break;case"polyAudioInput":if(this.module instanceof d)throw Error("Not compatible");t=new P(this.module,e);break;case"polyAudioOutput":if(this.module instanceof d)throw Error("Not compatible");t=new N(this.module,e);break;case"midiInput":t=new E(this.module,e);break;case"midiOutput":t=new z(this.module,e);break;default:Mt(e)}return this.collection.push(t),t}unPlugAll(){this.collection.forEach(e=>{e.unPlugAll()})}rePlugAll(e){this.collection.forEach(t=>{t.rePlugAll(e)})}find(e){let t=this.collection.find(o=>o.id===e);if(!t)throw Error(`The io with id ${e} is not exists`);return t}findByName(e){let t=this.collection.find(o=>o.name===e);if(!t)throw Error(`The io with name ${e} is not exists`);return t}serialize(){return ht(this.collection,[e=>e.isMidi()?-1:1]).map(e=>e.serialize())}validateUniqName(e){if(this.collection.some(t=>t.name===e))throw Error(`An io with name ${e} is already exists`)}},b=class extends G{constructor(e){super("Input",e)}},C=class extends G{constructor(e){super("Output",e)}};var ft=new Map([["C0",16.35],["C#0",17.32],["Db0",17.32],["D0",18.35],["D#0",19.45],["Eb0",19.45],["E0",20.6],["F0",21.83],["F#0",23.12],["Gb0",23.12],["G0",24.5],["G#0",25.96],["Ab0",25.96],["A0",27.5],["A#0",29.14],["Bb0",29.14],["B0",30.87],["C1",32.7],["C#1",34.65],["Db1",34.65],["D1",36.71],["D#1",38.89],["Eb1",38.89],["E1",41.2],["F1",43.65],["F#1",46.25],["Gb1",46.25],["G1",49],["G#1",51.91],["Ab1",51.91],["A1",55],["A#1",58.27],["Bb1",58.27],["B1",61.74],["C2",65.41],["C#2",69.3],["Db2",69.3],["D2",73.42],["D#2",77.78],["Eb2",77.78],["E2",82.41],["F2",87.31],["F#2",92.5],["Gb2",92.5],["G2",98],["G#2",103.83],["Ab2",103.83],["A2",110],["A#2",116.54],["Bb2",116.54],["B2",123.47],["C3",130.81],["C#3",138.59],["Db3",138.59],["D3",146.83],["D#3",155.56],["Eb3",155.56],["E3",164.81],["F3",174.61],["F#3",185],["Gb3",185],["G3",196],["G#3",207.65],["Ab3",207.65],["A3",220],["A#3",233.08],["Bb3",233.08],["B3",246.94],["C4",261.63],["C#4",277.18],["Db4",277.18],["D4",293.66],["D#4",311.13],["Eb4",311.13],["E4",329.63],["F4",349.23],["F#4",369.99],["Gb4",369.99],["G4",392],["G#4",415.3],["Ab4",415.3],["A4",440],["A#4",466.16],["Bb4",466.16],["B4",493.88],["C5",523.25],["C#5",554.37],["Db5",554.37],["D5",587.33],["D#5",622.25],["Eb5",622.25],["E5",659.26],["F5",698.46],["F#5",739.99],["Gb5",739.99],["G5",783.99],["G#5",830.61],["Ab5",830.61],["A5",880],["A#5",932.33],["Bb5",932.33],["B5",987.77],["C6",1046.5],["C#6",1108.73],["Db6",1108.73],["D6",1174.66],["D#6",1244.51],["Eb6",1244.51],["E6",1318.51],["F6",1396.91],["F#6",1479.98],["Gb6",1479.98],["G6",1567.98],["G#6",1661.22],["Ab6",1661.22],["A6",1760],["A#6",1864.66],["Bb6",1864.66],["B6",1975.53],["C7",2093],["C#7",2217.46],["Db7",2217.46],["D7",2349.32],["D#7",2489.02],["Eb7",2489.02],["E7",2637.02],["F7",2793.83],["F#7",2959.96],["Gb7",2959.96],["G7",3135.96],["G#7",3322.44],["Ab7",3322.44],["A7",3520],["A#7",3729.31],["Bb7",3729.31],["B7",3951.07],["C8",4186.01],["C#8",4434.92],["Db8",4434.92],["D8",4698.64],["D#8",4978.03],["Eb8",4978.03]]),le=ft;var ce=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],yt=2,m=class n{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of le)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new n(t)}static fromEvent(e){let t=e.data[1];if(t===void 0)throw new Error("Invalid MIDI message: missing data byte");let o=ce[t%12];if(!o)throw new Error(`Invalid MIDI note number: ${t}`);let i=Math.floor(t/12)-2;return new n(`${o}${i}`)}static notes(e=3){return ce.map(t=>new n(`${t}${e}`))}constructor(e){typeof e=="string"?this.fromString(e):this.fromProps(e)}get isSemi(){return this.name.endsWith("#")}get fullName(){return`${this.name}${this.octave}`}get frequency(){return le.get(`${this.name}${this.octave}`)}midiData(e=!0){let t=e?144:128;return new Uint8Array([t,this.midiNumber,this.velocity*100])}get midiNumber(){return(this.octave+yt)*12+this.noteIndex}get noteIndex(){return ce.indexOf(this.name)}valueOf(){return this.fullName}serialize(){return{name:this.name,octave:this.octave,frequency:this.frequency,velocity:this.velocity,duration:this.duration}}fromString(e){let t=/(\w#?)(\d)?/.exec(e);if(!t)throw new Error(`Invalid note string: ${e}`);let o=t[1];if(!o)throw new Error(`Invalid note name in: ${e}`);this.name=o,this.octave=t[2]?parseInt(t[2]):1}fromProps(e){Object.assign(this,e)}};var S=class{data;constructor(e){this.data=e}get dataBytes(){return Array.from(this.data.slice(1))}get type(){let e=this.data[0];if(e===void 0)return"unknown";switch(e&240){case 144:return this.data[2]===0?"noteoff":"noteon";case 128:return"noteoff";case 176:return"controlchange";case 224:return"pitchbend";case 208:return"channelaftertouch";case 160:return"keyaftertouch";case 192:return"programchange";default:return"unknown"}}};var y=class n{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof m?e:new m(e);return new n(new S(i.midiData(t)),o)}static fromCC(e,t,o){return new n(new S(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t,this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}get isCC(){return this.type==="controlchange"}get cc(){if(this.isCC)return this.message.dataBytes[0]}get ccValue(){if(this.isCC)return this.message.dataBytes[1]}defineNotes(){this.isNote&&(this.note||(this.note=m.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new n(this.message,this.triggeredAt);return t.voiceNo=e,t}};var d=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;activeNotes;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voiceNo:s,audioNodeConstructor:p,props:u}=t;this.id=o??It(),this.engineId=e,this.name=i,this.moduleType=r,this.voiceNo=s??0,this.activeNotes=[],this.audioNode=p?.(this.context),this._props=u,this.inputs=new b(this),this.outputs=new C(this),queueMicrotask(()=>{this.props=u})}get props(){return this._props}set props(e){let t={...e};Object.keys(e).forEach(o=>{let i=e[o];if(i!==void 0){let r=this.callPropHook("onSet",o,i);r!==void 0&&(t[o]=r)}}),this._props={...this._props,...t},Object.keys(t).forEach(o=>{let i=t[o];i!==void 0&&this.callPropHook("onAfterSet",o,i)})}callPropHook(e,t,o){let i=`${e}${gt(t)}`,r=this[i];if(typeof r=="function")return r.call(this,o)}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voiceNo:this.voiceNo,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}start(e){}stop(e){}triggerAttack(e,t){this.activeNotes.some(o=>o.fullName===e.fullName)||this.activeNotes.push(e)}triggerRelease(e,t){this.activeNotes=this.activeNotes.filter(o=>o.fullName!==e.fullName)}handleCC(e,t){}onMidiEvent=e=>{let{note:t,triggeredAt:o}=e;switch(e.type){case"noteon":{this.triggerAttack(t,o);break}case"noteoff":this.triggerRelease(t,o);break;case"controlchange":this.handleCC(e,o);break;default:throw Error("This type is not a note")}};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){Pt(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),this.audioNode&&((e==="in"||e==="both")&&this.registerAudioInput({name:"in",getAudioNode:()=>this.audioNode}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out",getAudioNode:()=>this.audioNode}))}registerAudioInput(e){return this.inputs.add({...e,ioType:"audioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"audioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as Tt}from"@blibliki/utils";var V=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??Tt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){this.routes.forEach((e,t)=>{this.removeRoute(t)})}replug(){this.routes.forEach((e,t)=>{let{sourceIO:o,destinationIO:i}=this.getIOs(t);o.rePlugAll(),i.rePlugAll()})}serialize(){return Array.from(this.routes.values())}plug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.plug(o)}unPlug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.unPlug(o)}find(e){let t=this.routes.get(e);if(!t)throw Error(`Route with id ${e} not found`);return t}getIOs(e){let t=this.find(e),{source:o,destination:i}=t,r=this.engine.findIO(o.moduleId,o.ioName,"output"),s=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:r,destinationIO:s}}};var ie=(t=>(t.connected="connected",t.disconnected="disconnected",t))(ie||{}),A=class{id;name;eventListerCallbacks=[];context;input;messageHandler=null;constructor(e,t){this.id=e.id,this.name=e.name,this.input=e,this.context=t,this.connect()}get state(){return this.input.state}connect(){this.messageHandler=e=>{this.processEvent(e)},this.input.addEventListener(this.messageHandler)}disconnect(){this.messageHandler&&(this.input.removeEventListener(this.messageHandler),this.messageHandler=null)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}processEvent(e){let t=new S(e.data),o=new y(t,this.context.browserToContextTime(e.timeStamp));switch(o.type){case"noteon":case"noteoff":case"controlchange":this.eventListerCallbacks.forEach(i=>{i(o)})}}};var vt={a:new m("C3"),s:new m("D3"),d:new m("E3"),f:new m("F3"),g:new m("G3"),h:new m("A3"),j:new m("B3"),k:new m("C4"),l:new m("D4"),w:new m("C#3"),e:new m("D#3"),t:new m("F#3"),y:new m("G#3"),u:new m("A#3"),o:new m("C#4"),p:new m("D#4")},St=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),w=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=St();this.id=t,this.name=o,this.state=i,this.context=e,document.addEventListener("keydown",this.onKeyTrigger(!0)),document.addEventListener("keyup",this.onKeyTrigger(!1))}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}onKeyTrigger=e=>t=>{let o=this.extractNote(t);if(!o)return;let i=y.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(r=>{r(i)})};extractNote(e){if(!e.repeat)return vt[e.key]}};import{isNode as xt}from"es-toolkit";import{isNode as At}from"es-toolkit";var Me=class{id;name;portIndex;input;callbacks=new Set;handler=null;_state="disconnected";constructor(e,t,o){this.portIndex=e,this.id=`node-midi-${e}`,this.name=t,this.input=o}get state(){return this._state}setState(e){this._state=e}addEventListener(e){if(this.callbacks.size===0){this.handler=(t,o)=>{let i={data:new Uint8Array(o),timeStamp:performance.now()};this.callbacks.forEach(r=>{r(i)})};try{this.input.isPortOpen()||(this.input.openPort(this.portIndex),this._state="connected"),this.input.on("message",this.handler)}catch(t){console.error(`Error opening MIDI port ${this.portIndex}:`,t)}}this.callbacks.add(e)}removeEventListener(e){if(this.callbacks.delete(e),this.callbacks.size===0&&this.handler){try{this.input.off("message",this.handler),this.input.isPortOpen()&&(this.input.closePort(),this._state="disconnected")}catch(t){console.error(`Error closing MIDI port ${this.portIndex}:`,t)}this.handler=null}}},he=class{ports=new Map;MidiModule;constructor(e){this.MidiModule=e,this.scanPorts()}scanPorts(){try{let e=new this.MidiModule.Input,t=e.getPortCount();for(let o=0;o<t;o++){let i=e.getPortName(o),r=`node-midi-${o}`;if(!this.ports.has(r)){let s=new this.MidiModule.Input,p=new Me(o,i,s);this.ports.set(r,p)}}e.isPortOpen()&&e.closePort()}catch(e){console.error("Error scanning MIDI ports:",e)}}*inputs(){for(let[,e]of this.ports)yield e}addEventListener(e,t){console.warn("Hot-plug detection not supported with node-midi adapter. Restart required for new devices.")}},q=class{async requestMIDIAccess(){try{let e=await import("@julusian/midi"),t="default"in e?e.default:e;return new he(t)}catch(e){return console.error("Error loading node-midi:",e),null}}isSupported(){return At()}};var re=class{input;callbacks=new Set;handler=null;constructor(e){this.input=e}get id(){return this.input.id}get name(){return this.input.name??`Device ${this.input.id}`}get state(){return this.input.state}addEventListener(e){this.callbacks.size===0&&(this.handler=t=>{if(!t.data)return;let o={data:t.data,timeStamp:t.timeStamp};this.callbacks.forEach(i=>{i(o)})},this.input.addEventListener("midimessage",this.handler)),this.callbacks.add(e)}removeEventListener(e){this.callbacks.delete(e),this.callbacks.size===0&&this.handler&&(this.input.removeEventListener("midimessage",this.handler),this.handler=null)}},fe=class{midiAccess;portCache=new Map;constructor(e){this.midiAccess=e}*inputs(){for(let[,e]of this.midiAccess.inputs)this.portCache.has(e.id)||this.portCache.set(e.id,new re(e)),yield this.portCache.get(e.id)}addEventListener(e,t){this.midiAccess.addEventListener(e,o=>{let i=o.port;if(i?.type!=="input")return;let r=i;this.portCache.has(r.id)||this.portCache.set(r.id,new re(r)),t(this.portCache.get(r.id))})}},R=class{async requestMIDIAccess(){try{if(typeof navigator>"u"||typeof navigator.requestMIDIAccess!="function")return null;let e=await navigator.requestMIDIAccess();return new fe(e)}catch(e){return console.error("Error enabling Web MIDI API:",e),null}}isSupported(){return typeof navigator<"u"&&typeof navigator.requestMIDIAccess=="function"}};function Ee(){return xt()?new q:new R}function ne(n){let e=n.toLowerCase();e=e.replace(/\s+\d+:\d+\s*$/g,"");let t=e.split(":");return t.length>1&&(e=t.reduce((o,i)=>i.length>o.length?i:o)),e=e.replace(/\s+(midi|input|output|port)(\s+\d+)?$/gi,""),e=e.replace(/^device\s+/gi,""),e=e.replace(/\s+/g," ").trim(),e}function ye(n){let t=ne(n).split(/[\s\-_:]+/),o=new Set(["midi","input","output","port","device","in","out"]);return t.filter(i=>i.length>1&&!o.has(i))}function we(n,e){let t=n.length,o=e.length,i=[];for(let s=0;s<=t;s++)i[s]=[s];for(let s=0;s<=o;s++)i[0]&&(i[0][s]=s);for(let s=1;s<=t;s++)for(let p=1;p<=o;p++){let u=n[s-1]===e[p-1]?0:1,l=i[s-1],f=i[s],c=f?.[p-1],M=l?.[p-1],g=l?.[p];f&&g!==void 0&&c!==void 0&&M!==void 0&&(f[p]=Math.min(g+1,c+1,M+u))}return i[t]?.[o]??0}function ke(n,e){let t=ne(n),o=ne(e);if(t===o)return 1;let i=new Set(ye(n)),r=new Set(ye(e));if(i.size===0||r.size===0){let a=Math.max(t.length,o.length);return a===0?0:1-we(t,o)/a}let s=new Set([...i].filter(a=>r.has(a))),p=new Set([...i,...r]),u=s.size/p.size,l=Math.max(t.length,o.length),c=1-we(t,o)/l,M=t.includes(o)||o.includes(t)?.8:0;return u*.5+c*.3+M*.2}function ge(n,e,t=.6){let o=null;for(let i of e){let r=ke(n,i);r>=t&&(!o||r>o.score)&&(o={name:i,score:r})}return o}var k=class{devices=new Map;initialized=!1;listeners=[];context;midiAccess=null;adapter=Ee();constructor(e){this.context=e,this.addComputerKeyboard()}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.devices.get(e)}findByName(e){return Array.from(this.devices.values()).find(t=>t.name===e)}findByFuzzyName(e,t=.6){let o=Array.from(this.devices.values()),i=o.map(p=>p.name),r=ge(e,i,t);if(!r)return null;let s=o.find(p=>p.name===r.name);return s?{device:s,score:r.score}:null}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{if(!this.adapter.isSupported()){console.warn("MIDI is not supported on this platform");return}if(this.midiAccess=await this.adapter.requestMIDIAccess(),!this.midiAccess){console.error("Failed to get MIDI access");return}for(let e of this.midiAccess.inputs())this.devices.has(e.id)||this.devices.set(e.id,new A(e,this.context))}catch(e){console.error("Error enabling MIDI:",e)}}addComputerKeyboard(){if(typeof document>"u")return;let e=new w(this.context);this.devices.set(e.id,e)}listenChanges(){this.midiAccess&&this.midiAccess.addEventListener("statechange",e=>{if(e.state==="connected"){if(this.devices.has(e.id))return;let t=new A(e,this.context);this.devices.set(t.id,t),this.listeners.forEach(o=>{o(t)})}else{let t=this.devices.get(e.id);if(!t||t instanceof w)return;t.disconnect(),this.devices.delete(t.id),this.listeners.forEach(o=>{o(t)})}})}};import{assertNever as Bt}from"@blibliki/utils";var De={},Fe={},Ie=class extends d{activeNote=null;triggeredAt=0;constructor(e,t){let o={...Fe,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let r=o.fullName;switch(i){case"noteon":this.activeNote=r,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},L=class extends h{midiOutput;constructor(e,t){let o={...Fe,...t.props},i=(r,s)=>new Ie(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerOutputs()}onMidiEvent=e=>{let t;switch(e.type){case"noteon":t=this.findFreeVoice();break;case"noteoff":t=this.audioModules.find(o=>o.activeNote===e.note.fullName);break;default:throw Error("This type is not a note")}t&&(t.midiTriggered(e),e.voiceNo=t.voiceNo,this.midiOutput.onMidiEvent(e))};findFreeVoice(){let e=this.audioModules.find(t=>!t.activeNote);if(!e&&(e=this.audioModules.sort((o,i)=>o.triggeredAt-i.triggeredAt)[0],!e))throw new Error("No voices available in voice scheduler");return e}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{ConstantSourceNode as ze}from"@blibliki/utils/web-audio-api";var Ge={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},bt={value:1},B=class extends d{isStated=!1;constructor(e,t){let o={...bt,...t.props},i=r=>new ze(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onAfterSetValue=e=>{this.audioNode.offset.value=e};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new ze(this.context.audioContext,{offset:this.props.value})}),this.isStated=!1)}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,t),this.start(t)};triggerRelease=()=>{}};import{cancelAndHoldAtTime as Ve}from"@blibliki/utils";import{GainNode as Ct}from"@blibliki/utils/web-audio-api";var qe={attack:.01,decay:0,sustain:1,release:0},Re={attack:{kind:"number",min:1e-4,max:20,step:.01,exp:3,label:"Attack"},decay:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Release"}},Pe=class extends d{constructor(e,t){let o={...qe,...t.props},i=r=>{let s=new Ct(r.audioContext);return s.gain.value=0,s};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let o=this.props.attack,i=this.props.decay,r=this.props.sustain;Ve(this.audioNode.gain,t),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,t),this.audioNode.gain.exponentialRampToValueAtTime(1,t+o),r>0?this.audioNode.gain.exponentialRampToValueAtTime(r,t+o+i):this.audioNode.gain.exponentialRampToValueAtTime(.001,t+o+i)}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let o=this.props.release,i=Ve(this.audioNode.gain,t);i>=1e-4&&(this.audioNode.gain.setValueAtTime(i,t),this.audioNode.gain.exponentialRampToValueAtTime(1e-4,t+o-1e-4)),this.audioNode.gain.setValueAtTime(0,t+o)}},U=class extends h{constructor(e,t){let o={...qe,...t.props},i=(r,s)=>new Pe(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{BiquadFilterNode as Nt}from"@blibliki/utils/web-audio-api";import{GainNode as Ot}from"@blibliki/utils/web-audio-api";var Le={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},Be={gain:1},_=class extends d{constructor(e,t){let o={...Be,...t.props},i=r=>new Ot(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetGain=e=>{this.audioNode.gain.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},H=class extends h{constructor(e,t){let o={...Be,...t.props},i=(r,s)=>new _(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var Ue=20,ve=2e4,_e={cutoff:ve,envelopeAmount:0,type:"lowpass",Q:1},He={cutoff:{kind:"number",min:Ue,max:ve,step:1,exp:5,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},type:{kind:"enum",options:["lowpass","highpass","bandpass"],label:"Type"},Q:{kind:"number",min:1e-4,max:1e3,step:.1,exp:5,label:"Q"}},Te=class extends d{scale;amount;constructor(e,t){let o={..._e,...t.props},i=r=>new Nt(r.audioContext,{type:o.type,frequency:0,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new _(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=ue(e,{name:"scale",moduleType:"Scale",props:{min:Ue,max:ve,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.audioNode.frequency),this.registerDefaultIOs(),this.registerInputs()}onAfterSetType=e=>{this.audioNode.type=e};onAfterSetCutoff=e=>{this.scale.props={current:e}};onAfterSetQ=e=>{this.audioNode.Q.value=e};onAfterSetEnvelopeAmount=e=>{this.amount.props={gain:e}};registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.audioNode.frequency}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.audioNode.Q})}},$=class extends h{constructor(e,t){let o={..._e,...t.props},i=(r,s)=>new Te(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs()}registerInputs(){this.registerAudioInput({name:"cutoff"}),this.registerAudioInput({name:"cutoffMod"}),this.registerAudioInput({name:"Q"})}};import{AnalyserNode as Et}from"@blibliki/utils/web-audio-api";var $e={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},wt={fftSize:512},K=class extends d{_buffer;constructor(e,t){let o={...wt,...t.props},i=r=>new Et(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onAfterSetFftSize=e=>{this._buffer=new Float32Array(e)};get buffer(){return this._buffer?this._buffer:(this._buffer=new Float32Array(this.props.fftSize),this._buffer)}getValue(){return this.getValues()[0]??0}getValues(){return this.audioNode.getFloatTimeDomainData(this.buffer),this.buffer}};var kt={},Ke={},j=class extends d{constructor(e,t){let o={...kt,...t.props},i=r=>r.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var Se=(s=>(s.direct="direct",s.directRev="directRev",s.toggleInc="toggleInc",s.toggleDec="toggleDec",s.incDec="incDec",s.incDecRev="incDecRev",s))(Se||{}),je={pages:{kind:"array",label:"Midi mapping pages"},activePage:{kind:"number",label:"Active page",min:0,max:100,step:1},globalMappings:{kind:"array",label:"Global midi mappings"}},Dt={pages:[{name:"Page 1",mappings:[{}]}],activePage:0,globalMappings:[{}]};function Ft({value:n,midiValue:e,propSchema:t,mapping:o}){let i=t.min??0,r=t.max??1,s=t.exp??1,{threshold:p=64,mode:u}=o,l=(n-i)/(r-i),c=Math.pow(l,1/s)*127;return c=e>=p&&u==="incDec"||e<=p&&u==="incDecRev"?c+1:c-1,Math.round(Math.max(0,Math.min(127,c)))}var W=class extends d{constructor(e,t){let o={...Dt,...t.props};super(e,{...t,props:o}),this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}onSetActivePage=e=>Math.max(Math.min(e,this.props.pages.length-1),0);handleCC=(e,t)=>{this.checkAutoAssign(e);let o=this.props.pages[this.props.activePage];o&&[...this.props.globalMappings.filter(i=>i.cc===e.cc),...o.mappings.filter(i=>i.cc===e.cc)].forEach(i=>{this.forwardMapping(e,i,t)})};forwardMapping=(e,t,o)=>{if(t.moduleId===void 0||t.moduleType===void 0||t.propName===void 0)return;let i=t.propName,r=e.ccValue;if(r===void 0)return;let s=t.mode??"direct";if((s==="toggleInc"||s==="toggleDec")&&r!==127)return;let p=this.engine.findModule(t.moduleId),u=Ae[p.moduleType][i],l;switch(u.kind){case"number":{let f=p.props[i];if(s==="incDec"||s==="incDecRev"?r=Ft({value:f,propSchema:u,mapping:t,midiValue:r}):s==="directRev"&&(r=127-r),s==="toggleInc")l=f+(u.step??1);else if(s==="toggleDec")l=f-(u.step??1);else{let c=u.min??0,M=u.max??1,g=r/127,a=Math.pow(g,u.exp??1);if(l=c+a*(M-c),u.step!==void 0&&(!u.exp||u.exp===1)){let T=Math.round((l-c)/u.step);l=c+T*u.step}}break}case"enum":{let f=Math.floor(r/127*u.options.length),c=Math.min(f,u.options.length-1);l=u.options[c];break}case"boolean":l=r>=64;break;case"string":throw Error("MidiMapper not support string type of values");case"array":throw Error("MidiMapper not support array type of values");default:throw Error("MidiMapper unknown type")}p.props={[i]:l},p.triggerPropsUpdate()};checkAutoAssign(e){if(e.cc===void 0)return;let t=this.props.pages[this.props.activePage];if(!t)return;let o=this.props.globalMappings.some(({autoAssign:u})=>u),i=t.mappings.some(({autoAssign:u})=>u);if(!o&&!i)return;let r=o?this.props.globalMappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):this.props.globalMappings,s=i?t.mappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):t.mappings,p=this.props.pages.map((u,l)=>l===this.props.activePage?{...u,mappings:s}:u);this.props={pages:p,globalMappings:r},this.triggerPropsUpdate()}};var We={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},zt={selectedId:void 0,selectedName:void 0},Q=class extends d{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...zt,...t.props};super(e,{...t,props:o});let i=this.props.selectedId&&this.engine.findMidiDevice(this.props.selectedId);if(!i&&this.props.selectedName&&(i=this.engine.findMidiDeviceByName(this.props.selectedName),!i)){let r=this.engine.findMidiDeviceByFuzzyName(this.props.selectedName,.6);r&&(i=r.device,console.log(`MIDI device fuzzy matched: "${this.props.selectedName}" -> "${i.name}" (confidence: ${Math.round(r.score*100)}%)`))}i&&this.addEventListener(i),this.registerOutputs()}onSetSelectedId=e=>{if(this.removeEventListener(),!e)return e;let t=this.engine.findMidiDevice(e);return t&&(this.props.selectedName!==t.name&&(this.props={selectedName:t.name},this.triggerPropsUpdate()),this.addEventListener(t)),e};get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){e.addEventListener(this.forwardMidiEvent)}removeEventListener(){if(!this.props.selectedId)return;this.engine.findMidiDevice(this.props.selectedId)?.removeEventListener(this.forwardMidiEvent)}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{dbToGain as Qe}from"@blibliki/utils";import{GainNode as Ye,OscillatorNode as Je}from"@blibliki/utils/web-audio-api";var xe=-18,pe=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(pe||{}),Xe={wave:{kind:"enum",options:Object.values(pe),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-1,max:2,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${xe}db Gain`}},Ze={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},be=class extends d{isStated=!1;outputGain;detuneGain;constructor(e,t){let o={...Ze,...t.props},i=r=>new Je(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.outputGain=new Ye(this.context.audioContext,{gain:Qe(xe)}),this.applyOutputGain(),this.initializeGainDetune(),this.registerInputs(),this.registerOutputs()}onAfterSetWave=e=>{this.audioNode.type=e};onAfterSetFrequency=()=>{this.updateFrequency()};onAfterSetFine=()=>{this.updateFrequency()};onAfterSetCoarse=()=>{this.updateFrequency()};onAfterSetOctave=()=>{this.updateFrequency()};onAfterSetLowGain=e=>{this.outputGain.gain.value=e?Qe(xe):1};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new Je(this.context.audioContext,{type:this.props.wave,frequency:this.finalFrequency}),this.applyOutputGain(),this.detuneGain.connect(this.audioNode.detune)}),this.isStated=!1)}triggerAttack=(e,t)=>{super.triggerAttack(e,t),this.props={frequency:e.frequency},this.updateFrequency(t),this.start(t)};triggerRelease(e,t){super.triggerRelease(e,t);let o=this.activeNotes.length?this.activeNotes[this.activeNotes.length-1]:null;o&&(this.props={frequency:o.frequency},this.updateFrequency(t))}get finalFrequency(){let{frequency:e,coarse:t,octave:o,fine:i}=this.props;return e*Math.pow(2,t/12+o+i/12)}updateFrequency(e){this.finalFrequency!==void 0&&(e?this.audioNode.frequency.setValueAtTime(this.finalFrequency,e):this.audioNode.frequency.value=this.finalFrequency)}applyOutputGain(){this.audioNode.connect(this.outputGain)}initializeGainDetune(){this.detuneGain=new Ye(this.context.audioContext,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputGain})}},Y=class extends h{constructor(e,t){let o={...Ze,...t.props},i=(r,s)=>new be(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs("out")}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}registerInputs(){this.registerAudioInput({name:"detune"})}};import{assertNever as Gt}from"@blibliki/utils";var et=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{s0;s1;constructor(){super(),this.s0=0,this.s1=0}static get parameterDescriptors(){return[{name:"cutoff",defaultValue:1e3,minValue:20,maxValue:2e4},{name:"resonance",defaultValue:0,minValue:0,maxValue:4}]}process(t,o,i){let r=t[0],s=o[0];if(!r||!s)return!0;let p=i.cutoff,u=i.resonance;if(!p||!u)return!0;for(let l=0;l<r.length;l++){let f=r[l],c=s[l];if(!(!f||!c))for(let M=0;M<f.length;M++){let g=f[M];if(g===void 0)continue;let a=p.length>1?p[M]??p[0]:p[0];if(a===void 0)continue;let T=Math.max(20,Math.min(2e4,a)),te=Math.log(T/20)/Math.log(2e4/20),x=Math.pow(.5,(1-te)/.125),v=u.length>1?u[M]??u[0]:u[0];if(v===void 0)continue;let Oe=1-Math.pow(.5,(v+.125)/.125)*x;this.s0=Oe*this.s0-x*this.s1+x*g,this.s1=Oe*this.s1+x*this.s0,c[M]=this.s1}}return!0}}registerProcessor("filter-processor",n)}).toString(),")()"],{type:"application/javascript"}));var tt=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let r=t[0],s=o[0];if(!r||!s)return!0;let p=i.min,u=i.max,l=i.current;if(!p||!u||!l)return!0;let f=r[0];if(!f||f.length===0){for(let c of s){let M=(l.length>1,l[0]??.5);c.fill(M)}return!0}for(let c=0;c<r.length;c++){let M=r[c],g=s[c];if(!(!M||!g))for(let a=0;a<M.length;a++){let T=M[a];if(T===void 0)continue;let te=p.length>1?p[a]??p[0]:p[0],x=u.length>1?u[a]??u[0]:u[0],v=l.length>1?l[a]??l[0]:l[0];te===void 0||x===void 0||v===void 0||(T<0?g[a]=v*Math.pow(te/v,-T):g[a]=v*Math.pow(x/v,T))}}return!0}}registerProcessor("scale-processor",n)}).toString(),")()"],{type:"application/javascript"}));async function ot(n){await n.addModule(tt),await n.addModule(et)}function it(n,e){switch(e){case"ScaleProcessor":return n.newAudioWorklet("scale-processor");case"FilterProcessor":return n.newAudioWorklet("filter-processor");default:Gt(e)}}var rt={min:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Min"},max:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Max"},current:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Current"}},Vt={min:0,max:1,current:.5},J=class extends d{constructor(e,t){let o={...Vt,...t.props},i=r=>it(r,"ScaleProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}get current(){return this.audioNode.parameters.get("current")}get min(){return this.audioNode.parameters.get("min")}get max(){return this.audioNode.parameters.get("max")}onAfterSetMin=e=>{this.min.value=e};onAfterSetMax=e=>{this.max.value=e};onAfterSetCurrent=e=>{this.current.value=e}};var nt={steps:{kind:"number",min:1,max:16,step:1,label:"Steps"},bars:{kind:"number",min:1,max:16,step:1,label:"Steps"}},qt={sequences:[],steps:16,bars:1},X=class extends d{midiOutput;constructor(e,t){let o={...qt,...t.props};super(e,{...t,props:o})}};import{StereoPannerNode as Rt}from"@blibliki/utils/web-audio-api";var st={pan:{kind:"number",min:-1,max:1,step:.01,label:"Pan"}},ut={pan:0},Ce=class extends d{constructor(e,t){let o={...ut,...t.props},i=r=>new Rt(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetPan=e=>{this.audioNode.pan.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"pan",getAudioNode:()=>this.audioNode.pan})}},Z=class extends h{constructor(e,t){let o={...ut,...t.props},i=(r,s)=>new Ce(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"pan"})}};var pt={activeNotes:{kind:"array",label:"Active notes"}},Lt={activeNotes:[]},ee=class extends d{midiOutput;constructor(e,t){let o={...Lt,...t.props};super(e,{...t,props:o}),this.registerInputs(),this.registerOutputs()}sendMidi(e){this.midiOutput.onMidiEvent(e)}triggerAttack=(e,t)=>{this.props={activeNotes:[...this.props.activeNotes,e.fullName]},this.triggerPropsUpdate(),this.sendMidi(y.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(y.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var se=(a=>(a.Master="Master",a.Oscillator="Oscillator",a.Gain="Gain",a.MidiSelector="MidiSelector",a.Envelope="Envelope",a.Filter="Filter",a.Scale="Scale",a.StereoPanner="StereoPanner",a.Inspector="Inspector",a.Constant="Constant",a.MidiMapper="MidiMapper",a.VirtualMidi="VirtualMidi",a.StepSequencer="StepSequencer",a.VoiceScheduler="VoiceScheduler",a))(se||{}),Ae={Oscillator:Xe,Gain:Le,Master:Ke,MidiSelector:We,Envelope:Re,Filter:He,Scale:rt,StereoPanner:st,Inspector:$e,Constant:Ge,MidiMapper:je,VirtualMidi:pt,StepSequencer:nt,VoiceScheduler:De};function ue(n,e){switch(e.moduleType){case"Oscillator":return new Y(n,e);case"Gain":return new H(n,e);case"Master":return new j(n,e);case"MidiSelector":return new Q(n,e);case"Envelope":return new U(n,e);case"Filter":return new $(n,e);case"Scale":return new J(n,e);case"StereoPanner":return new Z(n,e);case"Inspector":return new K(n,e);case"Constant":return new B(n,e);case"MidiMapper":return new W(n,e);case"VirtualMidi":return new ee(n,e);case"StepSequencer":return new X(n,e);case"VoiceScheduler":return new L(n,e);default:Bt(e)}}var O=class n{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=n._engines.get(e);return dt(t),t}static get current(){return dt(this._currentId),this.getById(this._currentId)}static async load(e){let{bpm:t,timeSignature:o,modules:i,routes:r}=e,s=new _t,p=new n(s);return await p.initialize(),p.timeSignature=o,p.bpm=t,i.forEach(u=>{p.addModule(u)}),r.forEach(u=>{p.addRoute(u)}),p}constructor(e){this.id=$t(),this.context=e,this.transport=new Ut(this.context,{generator:(t,o)=>[],consumer:t=>{},onJump:t=>{},onStart:this.onStart,onStop:this.onStop,silence:t=>{}}),this.routes=new V(this),this.modules=new Map,this.midiDeviceManager=new k(this.context),n._engines.set(this.id,this),n._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await ot(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=ue(this.id,e);return this.modules.set(t.id,t),t.serialize()}updateModule(e){let t=this.findModule(e.id);if(t.moduleType!==e.moduleType)throw Error(`The module id ${e.id} isn't moduleType ${e.moduleType}`);let o=Ht(e.changes,["name","props"]);return Object.assign(t,o),t instanceof h&&e.changes.voices!==void 0&&(t.voices=e.changes.voices),t.serialize()}removeModule(e){this.modules.delete(e)}addRoute(e){return this.routes.addRoute(e)}removeRoute(e){this.routes.removeRoute(e)}validRoute(e){let{source:t,destination:o}=e,i=this.findIO(t.moduleId,t.ioName,"output"),r=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&r.isMidi()||i.isAudio()&&r.isAudio()}async start(){await this.resume(),this.transport.start()}stop(){this.transport.stop(),this.transport.reset()}pause(){this.transport.stop()}get bpm(){return this.transport.bpm}set bpm(e){this.transport.bpm=e}get timeSignature(){return this.transport.timeSignature}set timeSignature(e){this.transport.timeSignature=e}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear()}serialize(){return{bpm:this.bpm,timeSignature:this.timeSignature,modules:Array.from(this.modules.values()).map(e=>e.serialize()),routes:this.routes.serialize()}}findModule(e){let t=this.modules.get(e);if(!t)throw Error(`The module with id ${e} is not exists`);return t}findIO(e,t,o){return this.findModule(e)[`${o}s`].findByName(t)}findMidiDevice(e){return this.midiDeviceManager.find(e)}findMidiDeviceByName(e){return this.midiDeviceManager.findByName(e)}findMidiDeviceByFuzzyName(e,t){return this.midiDeviceManager.findByFuzzyName(e,t)}onPropsUpdate(e){this.propsUpdateCallbacks.push(e)}_triggerPropsUpdate(e){this.propsUpdateCallbacks.forEach(t=>{t(e)})}triggerVirtualMidi(e,t,o){let i=this.findModule(e);if(i.moduleType!=="VirtualMidi")throw Error("This is not a virtual mid");i.sendMidi(y.fromNote(t,o==="noteOn",this.context.currentTime))}onStart=e=>{this.modules.forEach(t=>{t.start(e)})};onStop=e=>{this.modules.forEach(t=>{t.stop(e)})}};import{TransportState as Bs}from"@blibliki/transport";import{Context as _s}from"@blibliki/utils";export{_s as Context,O as Engine,A as MidiDevice,Se as MidiMappingMode,ie as MidiPortState,se as ModuleType,m as Note,pe as OscillatorWave,Bs as TransportState,Ae as moduleSchemas};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|