@blibliki/engine 0.4.2 → 0.5.1

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 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 IPolyModule<T extends ModuleType> = Omit<IModule<T>, "voiceNo"> & {
284
- voices: number;
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 IPolyModuleSerialize<T extends ModuleType> = IPolyModule<T> & {
344
+ type IModuleSerialize<T extends ModuleType> = IModule<T> & {
287
345
  inputs: IIOSerialize[];
288
346
  outputs: IIOSerialize[];
289
347
  };
290
- type IPolyModuleConstructor<T extends ModuleType> = Optional<IPolyModule<T>, "id"> & {
291
- monoModuleConstructor: (engineId: string, params: IModuleConstructor<T>) => Module<T>;
348
+ type IModuleConstructor<T extends ModuleType> = Optional<IModule<T>, "id" | "voiceNo"> & {
349
+ audioNodeConstructor?: (context: Context) => AudioNode;
292
350
  };
293
- declare abstract class PolyModule<T extends ModuleType> implements IPolyModule<T> {
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
- audioModules: Module<T>[];
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
- private _voices;
303
- private _name;
366
+ protected activeNotes: Note[];
304
367
  private pendingUIUpdates;
305
- constructor(engineId: string, params: IPolyModuleConstructor<T>);
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
- get voices(): number;
311
- set voices(value: number);
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: Module<ModuleType> | PolyModule<ModuleType>;
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
- dispose(): void;
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
- findVoice(voiceNo: number): Module<T>;
388
+ dispose(): void;
327
389
  protected registerDefaultIOs(value?: "both" | "in" | "out"): void;
328
- protected registerAudioInput(props: Omit<PolyAudioInputProps, "ioType">): PolyAudioInput;
329
- protected registerAudioOutput(props: Omit<PolyAudioOutputProps, "ioType">): PolyAudioOutput;
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(): _blibliki_utils.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 IModule<T extends ModuleType> = {
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,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(){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?.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)}},U=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},_=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 _(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 _(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||{}),Ue={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 _e={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.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 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:_e,Envelope:De,Filter:ze,Scale:Xe,StereoPanner:et,Inspector:Re,Constant:Ee,MidiMapper:Ue,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 U(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