@blibliki/engine 0.5.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/index.d.ts +501 -107
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/Engine.ts +46 -29
- package/src/core/index.ts +11 -2
- package/src/core/midi/BaseMidiDevice.ts +47 -0
- package/src/core/midi/ComputerKeyboardDevice.ts +2 -1
- package/src/core/midi/MidiDeviceManager.ts +125 -31
- package/src/core/midi/{MidiDevice.ts → MidiInputDevice.ts} +6 -30
- package/src/core/midi/MidiOutputDevice.ts +23 -0
- package/src/core/midi/adapters/NodeMidiAdapter.ts +99 -13
- package/src/core/midi/adapters/WebMidiAdapter.ts +68 -10
- package/src/core/midi/adapters/types.ts +13 -4
- package/src/core/midi/controllers/BaseController.ts +14 -0
- package/src/core/module/Module.ts +121 -13
- package/src/core/module/PolyModule.ts +36 -0
- package/src/core/module/VoiceScheduler.ts +150 -10
- package/src/core/module/index.ts +9 -4
- package/src/index.ts +27 -3
- package/src/modules/Chorus.ts +222 -0
- package/src/modules/Constant.ts +2 -2
- package/src/modules/Delay.ts +347 -0
- package/src/modules/Distortion.ts +182 -0
- package/src/modules/Envelope.ts +158 -92
- package/src/modules/Filter.ts +7 -7
- package/src/modules/Gain.ts +2 -2
- package/src/modules/LFO.ts +287 -0
- package/src/modules/LegacyEnvelope.ts +146 -0
- package/src/modules/{MidiSelector.ts → MidiInput.ts} +26 -19
- package/src/modules/MidiMapper.ts +59 -4
- package/src/modules/MidiOutput.ts +121 -0
- package/src/modules/Noise.ts +259 -0
- package/src/modules/Oscillator.ts +9 -3
- package/src/modules/Reverb.ts +379 -0
- package/src/modules/Scale.ts +49 -4
- package/src/modules/StepSequencer.ts +410 -22
- package/src/modules/StereoPanner.ts +1 -1
- package/src/modules/index.ts +142 -29
- package/src/processors/custom-envelope-processor.ts +125 -0
- package/src/processors/index.ts +10 -0
- package/src/processors/lfo-processor.ts +123 -0
- package/src/processors/scale-processor.ts +42 -5
- package/src/utils/WetDryMixer.ts +123 -0
- package/src/utils/expandPatternSequence.ts +18 -0
- package/src/utils/index.ts +2 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Engine.ts","../src/core/module/Module.ts","../src/core/IO/Collection.ts","../src/core/module/PolyModule.ts","../src/core/IO/AudioIO.ts","../src/core/IO/Base.ts","../src/core/IO/PolyAudioIO.ts","../src/core/IO/MidiIO.ts","../src/core/Note/frequencyTable.ts","../src/core/Note/index.ts","../src/core/midi/Message.ts","../src/core/midi/MidiEvent.ts","../src/core/Route.ts","../src/core/midi/MidiDevice.ts","../src/core/midi/ComputerKeyboardDevice.ts","../src/core/midi/adapters/index.ts","../src/core/midi/adapters/NodeMidiAdapter.ts","../src/core/midi/adapters/WebMidiAdapter.ts","../src/core/midi/deviceMatcher.ts","../src/core/midi/MidiDeviceManager.ts","../src/modules/index.ts","../src/core/module/VoiceScheduler.ts","../src/modules/Constant.ts","../src/modules/Envelope.ts","../src/modules/Filter.ts","../src/modules/Gain.ts","../src/modules/Inspector.ts","../src/modules/Master.ts","../src/modules/MidiMapper.ts","../src/modules/MidiSelector.ts","../src/modules/Oscillator.ts","../src/processors/index.ts","../src/processors/filter-processor.ts","../src/processors/scale-processor.ts","../src/modules/Scale.ts","../src/modules/StepSequencer.ts","../src/modules/StereoPanner.ts","../src/modules/VirtualMidi.ts","../src/index.ts"],"sourcesContent":["import {\n BPM,\n ContextTime,\n Ticks,\n TimeSignature,\n Transport,\n TransportEvent,\n} from \"@blibliki/transport\";\nimport {\n assertDefined,\n Context,\n Optional,\n pick,\n uuidv4,\n} from \"@blibliki/utils\";\nimport {\n IRoute,\n Routes,\n MidiDeviceManager,\n IModule,\n MidiEvent,\n IModuleSerialize,\n} from \"@/core\";\nimport {\n ICreateModule,\n ModuleParams,\n ModuleType,\n ModuleTypeToModuleMapping,\n createModule,\n} from \"@/modules\";\nimport {\n IPolyModule,\n IPolyModuleSerialize,\n PolyModule,\n} from \"./core/module/PolyModule\";\nimport { loadProcessors } from \"./processors\";\n\nexport type IUpdateModule<T extends ModuleType> = {\n id: string;\n moduleType: T;\n changes: Partial<Omit<ICreateModule<T>, \"id\" | \"moduleType\" | \"voice\">> & {\n voices?: number;\n };\n};\n\nexport type ICreateRoute = Optional<IRoute, \"id\">;\n\nexport interface IEngineSerialize {\n bpm: BPM;\n timeSignature: TimeSignature;\n modules: (IModuleSerialize<ModuleType> | IPolyModuleSerialize<ModuleType>)[];\n routes: IRoute[];\n}\n\nexport class Engine {\n private static _engines = new Map<string, Engine>();\n private static _currentId: string | undefined;\n private propsUpdateCallbacks: (<T extends ModuleType>(\n params: IModule<T> | IPolyModule<T>,\n ) => void)[] = [];\n\n readonly id: string;\n context: Context;\n isInitialized = false;\n routes: Routes;\n transport: Transport<TransportEvent>;\n modules: Map<\n string,\n ModuleTypeToModuleMapping[keyof ModuleTypeToModuleMapping]\n >;\n\n midiDeviceManager: MidiDeviceManager;\n\n static getById(id: string): Engine {\n const engine = Engine._engines.get(id);\n assertDefined(engine);\n\n return engine;\n }\n\n static get current(): Engine {\n assertDefined(this._currentId);\n\n return this.getById(this._currentId);\n }\n\n static async load(data: IEngineSerialize): Promise<Engine> {\n const { bpm, timeSignature, modules, routes } = data;\n const context = new Context();\n const engine = new Engine(context);\n await engine.initialize();\n\n engine.timeSignature = timeSignature;\n engine.bpm = bpm;\n modules.forEach((m) => {\n engine.addModule(m);\n });\n routes.forEach((r) => {\n engine.addRoute(r);\n });\n\n return engine;\n }\n\n constructor(context: Context) {\n this.id = uuidv4();\n\n this.context = context;\n this.transport = new Transport(this.context, {\n generator: (_start: Ticks, _end: Ticks) => {\n return [] as TransportEvent[];\n },\n consumer: (_event: TransportEvent) => {\n return;\n },\n onJump: (_ticks: Ticks) => {\n return;\n },\n onStart: this.onStart,\n onStop: this.onStop,\n silence: (_actionAt: ContextTime) => {\n return;\n },\n });\n this.routes = new Routes(this);\n this.modules = new Map();\n this.midiDeviceManager = new MidiDeviceManager(this.context);\n\n Engine._engines.set(this.id, this);\n Engine._currentId = this.id;\n }\n\n get state() {\n return this.transport.state;\n }\n\n async initialize() {\n if (this.isInitialized) return;\n\n await loadProcessors(this.context);\n await this.midiDeviceManager.initialize();\n this.isInitialized = true;\n }\n\n addModule<T extends ModuleType>(params: ICreateModule<T>) {\n const module = createModule(this.id, params as ModuleParams);\n this.modules.set(module.id, module);\n\n return module.serialize();\n }\n\n updateModule<T extends ModuleType>(params: IUpdateModule<T>) {\n const module = this.findModule(params.id);\n if (module.moduleType !== params.moduleType) {\n throw Error(\n `The module id ${params.id} isn't moduleType ${params.moduleType}`,\n );\n }\n\n const updates = pick(params.changes, [\"name\", \"props\"]);\n Object.assign(module, updates);\n\n if (module instanceof PolyModule && params.changes.voices !== undefined) {\n module.voices = params.changes.voices;\n }\n\n return module.serialize();\n }\n\n removeModule(id: string) {\n this.modules.delete(id);\n }\n\n addRoute(props: ICreateRoute): IRoute {\n return this.routes.addRoute(props);\n }\n\n removeRoute(id: string) {\n this.routes.removeRoute(id);\n }\n\n validRoute(props: Optional<IRoute, \"id\">): boolean {\n const { source, destination } = props;\n\n const output = this.findIO(source.moduleId, source.ioName, \"output\");\n const input = this.findIO(\n destination.moduleId,\n destination.ioName,\n \"input\",\n );\n\n return (\n (output.isMidi() && input.isMidi()) ||\n (output.isAudio() && input.isAudio())\n );\n }\n\n async start() {\n await this.resume();\n this.transport.start();\n }\n\n stop() {\n this.transport.stop();\n this.transport.reset();\n }\n\n pause() {\n this.transport.stop();\n }\n\n get bpm() {\n return this.transport.bpm;\n }\n\n set bpm(value: number) {\n this.transport.bpm = value;\n }\n\n get timeSignature() {\n return this.transport.timeSignature;\n }\n\n set timeSignature(value: TimeSignature) {\n this.transport.timeSignature = value;\n }\n\n async resume() {\n await this.context.resume();\n }\n\n dispose() {\n this.stop();\n this.routes.clear();\n this.modules.forEach((module) => {\n module.dispose();\n });\n this.modules.clear();\n }\n\n serialize(): IEngineSerialize {\n return {\n bpm: this.bpm,\n timeSignature: this.timeSignature,\n modules: Array.from(this.modules.values()).map((m) => m.serialize()),\n routes: this.routes.serialize(),\n };\n }\n\n findModule(\n id: string,\n ): ModuleTypeToModuleMapping[keyof ModuleTypeToModuleMapping] {\n const module = this.modules.get(id);\n if (!module) throw Error(`The module with id ${id} is not exists`);\n\n return module;\n }\n\n findIO(moduleId: string, ioName: string, type: \"input\" | \"output\") {\n const module = this.findModule(moduleId);\n return module[`${type}s`].findByName(ioName);\n }\n\n findMidiDevice(id: string) {\n return this.midiDeviceManager.find(id);\n }\n\n findMidiDeviceByName(name: string) {\n return this.midiDeviceManager.findByName(name);\n }\n\n findMidiDeviceByFuzzyName(name: string, threshold?: number) {\n return this.midiDeviceManager.findByFuzzyName(name, threshold);\n }\n\n onPropsUpdate(\n callback: <T extends ModuleType>(\n params: IModule<T> | IPolyModule<T>,\n ) => void,\n ) {\n this.propsUpdateCallbacks.push(callback);\n }\n\n _triggerPropsUpdate<T extends ModuleType>(\n params: IModule<T> | IPolyModule<T>,\n ) {\n this.propsUpdateCallbacks.forEach((callback) => {\n callback(params);\n });\n }\n\n // TODO: Find better way to support this\n triggerVirtualMidi(id: string, noteName: string, type: \"noteOn\" | \"noteOff\") {\n const virtualMidi = this.findModule(id);\n if (virtualMidi.moduleType !== ModuleType.VirtualMidi)\n throw Error(\"This is not a virtual mid\");\n\n virtualMidi.sendMidi(\n MidiEvent.fromNote(noteName, type === \"noteOn\", this.context.currentTime),\n );\n }\n\n // actionAt is context time\n private onStart = (actionAt: ContextTime) => {\n this.modules.forEach((module) => {\n module.start(actionAt);\n });\n };\n\n // actionAt is context time\n private onStop = (actionAt: ContextTime) => {\n this.modules.forEach((module) => {\n module.stop(actionAt);\n });\n };\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport {\n Context,\n Optional,\n upperFirst,\n uuidv4,\n requestAnimationFrame,\n} from \"@blibliki/utils\";\nimport { Engine } from \"@/Engine\";\nimport { AnyModule, ModuleType, ModuleTypeToPropsMapping } from \"@/modules\";\nimport {\n AudioInputProps,\n AudioOutputProps,\n IIOSerialize,\n IOType,\n InputCollection,\n OutputCollection,\n MidiInputProps,\n MidiOutputProps,\n} from \"../IO\";\nimport Note from \"../Note\";\nimport MidiEvent, { MidiEventType } from \"../midi/MidiEvent\";\n\nexport type IModule<T extends ModuleType> = {\n id: string;\n name: string;\n voiceNo: number;\n moduleType: T;\n props: ModuleTypeToPropsMapping[T];\n};\n\nexport type IModuleSerialize<T extends ModuleType> = IModule<T> & {\n inputs: IIOSerialize[];\n outputs: IIOSerialize[];\n};\n\nexport type IModuleConstructor<T extends ModuleType> = Optional<\n IModule<T>,\n \"id\" | \"voiceNo\"\n> & {\n audioNodeConstructor?: (context: Context) => AudioNode;\n};\n\nexport type SetterHooks<P> = {\n [K in keyof P as `onSet${Capitalize<string & K>}`]: (value: P[K]) => P[K];\n} & {\n [K in keyof P as `onAfterSet${Capitalize<string & K>}`]: (\n value: P[K],\n ) => void;\n};\n\nexport abstract class Module<T extends ModuleType> implements IModule<T> {\n id: string;\n engineId: string;\n name: string;\n moduleType: T;\n voiceNo: number;\n audioNode: AudioNode | undefined;\n inputs: InputCollection;\n outputs: OutputCollection;\n protected _props!: ModuleTypeToPropsMapping[T];\n protected activeNotes: Note[];\n private pendingUIUpdates = false;\n\n constructor(engineId: string, params: IModuleConstructor<T>) {\n const { id, name, moduleType, voiceNo, audioNodeConstructor, props } =\n params;\n\n this.id = id ?? uuidv4();\n this.engineId = engineId;\n this.name = name;\n this.moduleType = moduleType;\n this.voiceNo = voiceNo ?? 0;\n this.activeNotes = [];\n this.audioNode = audioNodeConstructor?.(this.context);\n this._props = props;\n\n this.inputs = new InputCollection(this);\n this.outputs = new OutputCollection(this);\n\n // Defer hook calls until after subclass is fully initialized\n queueMicrotask(() => {\n this.props = props;\n });\n }\n\n get props(): ModuleTypeToPropsMapping[T] {\n return this._props;\n }\n\n set props(value: Partial<ModuleTypeToPropsMapping[T]>) {\n const updatedValue = { ...value };\n\n (Object.keys(value) as (keyof ModuleTypeToPropsMapping[T])[]).forEach(\n (key) => {\n const propValue = value[key];\n if (propValue !== undefined) {\n const result = this.callPropHook(\"onSet\", key, propValue);\n if (result !== undefined) {\n updatedValue[key] = result;\n }\n }\n },\n );\n\n this._props = { ...this._props, ...updatedValue };\n\n (\n Object.keys(updatedValue) as (keyof ModuleTypeToPropsMapping[T])[]\n ).forEach((key) => {\n const propValue = updatedValue[key];\n if (propValue !== undefined) {\n this.callPropHook(\"onAfterSet\", key, propValue);\n }\n });\n }\n\n private callPropHook<K extends keyof ModuleTypeToPropsMapping[T]>(\n hookType: \"onSet\" | \"onAfterSet\",\n key: K,\n value: ModuleTypeToPropsMapping[T][K],\n ): ModuleTypeToPropsMapping[T][K] | undefined {\n const hookName = `${hookType}${upperFirst(key as string)}`;\n const hook = this[hookName as keyof this];\n\n if (typeof hook === \"function\") {\n const result = (\n hook as (\n value: ModuleTypeToPropsMapping[T][K],\n ) => ModuleTypeToPropsMapping[T][K] | undefined\n ).call(this, value);\n return result;\n }\n return undefined;\n }\n\n serialize(): IModuleSerialize<T> {\n return {\n id: this.id,\n name: this.name,\n moduleType: this.moduleType,\n voiceNo: this.voiceNo,\n props: this.props,\n inputs: this.inputs.serialize(),\n outputs: this.outputs.serialize(),\n };\n }\n\n plug({\n audioModule,\n from,\n to,\n }: {\n audioModule: AnyModule;\n from: string;\n to: string;\n }) {\n const output = this.outputs.findByName(from);\n const input = audioModule.inputs.findByName(to);\n\n output.plug(input);\n }\n\n protected rePlugAll(callback?: () => void) {\n this.inputs.rePlugAll(callback);\n this.outputs.rePlugAll(callback);\n }\n\n protected unPlugAll() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n }\n\n start(_time: ContextTime): void {\n // Optional implementation in modules\n }\n\n stop(_time: ContextTime): void {\n // Optional implementation in modules\n }\n\n triggerAttack(note: Note, _triggeredAt: ContextTime): void {\n if (this.activeNotes.some((n) => n.fullName === note.fullName)) return;\n\n this.activeNotes.push(note);\n }\n\n triggerRelease(note: Note, _triggeredAt: ContextTime): void {\n this.activeNotes = this.activeNotes.filter(\n (n) => n.fullName !== note.fullName,\n );\n }\n\n handleCC(_event: MidiEvent, _triggeredAt: ContextTime): void {\n // Optional implementation in modules\n }\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n const { note, triggeredAt } = midiEvent;\n\n switch (midiEvent.type) {\n case MidiEventType.noteOn: {\n this.triggerAttack(note!, triggeredAt);\n break;\n }\n case MidiEventType.noteOff:\n this.triggerRelease(note!, triggeredAt);\n break;\n case MidiEventType.cc:\n this.handleCC(midiEvent, triggeredAt);\n break;\n default:\n throw Error(\"This type is not a note\");\n }\n };\n\n triggerPropsUpdate = () => {\n if (this.pendingUIUpdates) return;\n\n this.pendingUIUpdates = true;\n this.sheduleTriggerUpdate();\n };\n\n private sheduleTriggerUpdate() {\n requestAnimationFrame(() => {\n this.engine._triggerPropsUpdate({\n id: this.id,\n moduleType: this.moduleType,\n voiceNo: this.voiceNo,\n name: this.name,\n props: this.props,\n });\n this.pendingUIUpdates = false;\n });\n }\n\n dispose() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n }\n\n protected registerDefaultIOs(value: \"both\" | \"in\" | \"out\" = \"both\") {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n\n if (!this.audioNode) return;\n\n if (value === \"in\" || value === \"both\") {\n this.registerAudioInput({\n name: \"in\",\n getAudioNode: () => this.audioNode!,\n });\n }\n\n if (value === \"out\" || value === \"both\") {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.audioNode!,\n });\n }\n }\n\n protected registerAudioInput(props: Omit<AudioInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.AudioInput });\n }\n\n protected registerAudioOutput(props: Omit<AudioOutputProps, \"ioType\">) {\n return this.outputs.add({ ...props, ioType: IOType.AudioOutput });\n }\n\n protected registerMidiInput(props: Omit<MidiInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.MidiInput });\n }\n\n protected registerMidiOutput(props: Omit<MidiOutputProps, \"ioType\">) {\n return this.outputs.add({\n ...props,\n ioType: IOType.MidiOutput,\n });\n }\n\n protected get engine() {\n return Engine.getById(this.engineId);\n }\n\n protected get context() {\n return this.engine.context;\n }\n}\n","import { assertNever } from \"@blibliki/utils\";\nimport { sortBy } from \"es-toolkit\";\nimport { ModuleType } from \"@/modules\";\nimport { Module } from \"../module\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport {\n AudioInput,\n AudioInputProps,\n AudioOutput,\n AudioOutputProps,\n} from \"./AudioIO\";\nimport { Base, IOType } from \"./Base\";\nimport {\n MidiInput,\n MidiInputProps,\n MidiOutput,\n MidiOutputProps,\n} from \"./MidiIO\";\nimport {\n PolyAudioInput,\n PolyAudioInputProps,\n PolyAudioOutput,\n PolyAudioOutputProps,\n} from \"./PolyAudioIO\";\n\nexport enum CollectionType {\n Input = \"Input\",\n Output = \"Output\",\n}\n\ntype IMappedIOProps = {\n [CollectionType.Input]:\n | AudioInputProps\n | PolyAudioInputProps\n | MidiInputProps;\n [CollectionType.Output]:\n | AudioOutputProps\n | PolyAudioOutputProps\n | MidiOutputProps;\n};\n\ntype IIOTypeTOClass = {\n [IOType.AudioInput]: AudioInput;\n [IOType.AudioOutput]: AudioOutput;\n [IOType.PolyAudioInput]: PolyAudioInput;\n [IOType.PolyAudioOutput]: PolyAudioOutput;\n [IOType.MidiInput]: MidiInput;\n [IOType.MidiOutput]: MidiOutput;\n};\n\nexport default abstract class IOCollection<T extends CollectionType> {\n module: Module<ModuleType> | PolyModule<ModuleType>;\n collection: Base[] = [];\n collectionType: T;\n\n constructor(\n collectionType: T,\n module: Module<ModuleType> | PolyModule<ModuleType>,\n ) {\n this.collectionType = collectionType;\n this.module = module;\n }\n\n add<TT extends IMappedIOProps[T]>(props: TT): IIOTypeTOClass[TT[\"ioType\"]] {\n let io:\n | AudioInput\n | AudioOutput\n | PolyAudioInput\n | PolyAudioOutput\n | MidiInput\n | MidiOutput;\n this.validateUniqName(props.name);\n\n switch (props.ioType) {\n case IOType.AudioInput:\n if (this.module instanceof PolyModule) throw Error(\"Not compatible\");\n io = new AudioInput(this.module, props);\n break;\n case IOType.AudioOutput:\n if (this.module instanceof PolyModule) throw Error(\"Not compatible\");\n io = new AudioOutput(this.module, props);\n break;\n case IOType.PolyAudioInput:\n if (this.module instanceof Module) throw Error(\"Not compatible\");\n io = new PolyAudioInput(this.module, props);\n break;\n case IOType.PolyAudioOutput:\n if (this.module instanceof Module) throw Error(\"Not compatible\");\n io = new PolyAudioOutput(this.module, props);\n break;\n case IOType.MidiInput:\n io = new MidiInput(this.module, props);\n break;\n case IOType.MidiOutput:\n io = new MidiOutput(this.module, props);\n break;\n default:\n assertNever(props);\n }\n\n this.collection.push(io);\n\n return io as IIOTypeTOClass[TT[\"ioType\"]];\n }\n\n unPlugAll() {\n this.collection.forEach((io) => {\n io.unPlugAll();\n });\n }\n\n rePlugAll(callback?: () => void) {\n this.collection.forEach((io) => {\n io.rePlugAll(callback);\n });\n }\n\n find(id: string) {\n const io = this.collection.find((io) => io.id === id);\n if (!io) throw Error(`The io with id ${id} is not exists`);\n\n return io;\n }\n\n findByName(name: string) {\n const io = this.collection.find((io) => io.name === name);\n if (!io) throw Error(`The io with name ${name} is not exists`);\n\n return io;\n }\n\n serialize() {\n return sortBy(this.collection, [(io) => (io.isMidi() ? -1 : 1)]).map((io) =>\n io.serialize(),\n );\n }\n\n private validateUniqName(name: string) {\n if (this.collection.some((io) => io.name === name)) {\n throw Error(`An io with name ${name} is already exists`);\n }\n }\n}\n\nexport class InputCollection extends IOCollection<CollectionType.Input> {\n constructor(module: Module<ModuleType> | PolyModule<ModuleType>) {\n super(CollectionType.Input, module);\n }\n}\n\nexport class OutputCollection extends IOCollection<CollectionType.Output> {\n constructor(module: Module<ModuleType> | PolyModule<ModuleType>) {\n super(CollectionType.Output, module);\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport {\n deterministicId,\n Optional,\n uuidv4,\n requestAnimationFrame,\n} from \"@blibliki/utils\";\nimport { Engine } from \"@/Engine\";\nimport { ModuleType, ModuleTypeToPropsMapping } from \"@/modules\";\nimport {\n IIOSerialize,\n InputCollection,\n IOType,\n MidiInputProps,\n MidiOutputProps,\n OutputCollection,\n} from \"../IO\";\nimport { PolyAudioInputProps, PolyAudioOutputProps } from \"../IO/PolyAudioIO\";\nimport MidiEvent from \"../midi/MidiEvent\";\nimport { IModule, IModuleConstructor, Module } from \"./Module\";\n\nexport type IPolyModule<T extends ModuleType> = Omit<IModule<T>, \"voiceNo\"> & {\n voices: number;\n};\n\nexport type IPolyModuleSerialize<T extends ModuleType> = IPolyModule<T> & {\n inputs: IIOSerialize[];\n outputs: IIOSerialize[];\n};\n\nexport type IPolyModuleConstructor<T extends ModuleType> = Optional<\n IPolyModule<T>,\n \"id\"\n> & {\n monoModuleConstructor: (\n engineId: string,\n params: IModuleConstructor<T>,\n ) => Module<T>;\n};\n\nexport abstract class PolyModule<\n T extends ModuleType,\n> implements IPolyModule<T> {\n id: string;\n engineId: string;\n moduleType: T;\n audioModules!: Module<T>[];\n inputs: InputCollection;\n outputs: OutputCollection;\n protected monoModuleConstructor: IPolyModuleConstructor<T>[\"monoModuleConstructor\"];\n protected _props!: ModuleTypeToPropsMapping[T];\n private _voices!: number;\n private _name!: string;\n private pendingUIUpdates = false;\n\n constructor(engineId: string, params: IPolyModuleConstructor<T>) {\n const { id, name, moduleType, voices, monoModuleConstructor, props } =\n params;\n\n this.audioModules = [];\n\n this.monoModuleConstructor = monoModuleConstructor;\n this.id = id ?? uuidv4();\n this.engineId = engineId;\n this.name = name;\n this.moduleType = moduleType;\n this._props = props;\n\n this.inputs = new InputCollection(\n this as unknown as PolyModule<ModuleType>,\n );\n this.outputs = new OutputCollection(\n this as unknown as PolyModule<ModuleType>,\n );\n\n // Defer hook calls until after subclass is fully initialized\n queueMicrotask(() => {\n this.voices = voices || 1;\n this.props = props;\n this.triggerPropsUpdate();\n });\n }\n\n get name() {\n return this._name;\n }\n\n set name(value: string) {\n this._name = value;\n this.audioModules.forEach((m) => (m.name = value));\n }\n\n get props(): ModuleTypeToPropsMapping[T] {\n return this._props;\n }\n\n set props(value: Partial<ModuleTypeToPropsMapping[T]>) {\n this._props = { ...this._props, ...value };\n this.audioModules.forEach((m) => (m.props = value));\n }\n\n get voices() {\n return this._voices;\n }\n\n set voices(value: number) {\n this._voices = value;\n this.adjustNumberOfModules();\n this.rePlugAll();\n }\n\n start(time: ContextTime): void {\n this.audioModules.forEach((m) => {\n m.start(time);\n });\n }\n\n stop(time: ContextTime): void {\n this.audioModules.forEach((m) => {\n m.stop(time);\n });\n }\n\n serialize(): IPolyModuleSerialize<T> {\n return {\n id: this.id,\n name: this.name,\n moduleType: this.moduleType,\n voices: this.voices,\n props: this.props,\n inputs: this.inputs.serialize(),\n outputs: this.outputs.serialize(),\n };\n }\n\n plug({\n audioModule,\n from,\n to,\n }: {\n audioModule: Module<ModuleType> | PolyModule<ModuleType>;\n from: string;\n to: string;\n }) {\n const output = this.outputs.findByName(from);\n const input = audioModule.inputs.findByName(to);\n\n output.plug(input);\n }\n\n rePlugAll(callback?: () => void) {\n this.inputs.rePlugAll(callback);\n this.outputs.rePlugAll(callback);\n }\n\n protected unPlugAll() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n }\n\n dispose() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n this.audioModules.forEach((m) => {\n m.dispose();\n });\n }\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n const voiceNo = midiEvent.voiceNo ?? 0;\n const audioModule = this.findVoice(voiceNo);\n audioModule.onMidiEvent(midiEvent);\n };\n\n triggerPropsUpdate = () => {\n if (this.pendingUIUpdates) return;\n\n this.pendingUIUpdates = true;\n this.sheduleTriggerUpdate();\n };\n\n private sheduleTriggerUpdate() {\n requestAnimationFrame(() => {\n this.engine._triggerPropsUpdate({\n id: this.id,\n moduleType: this.moduleType,\n voices: this.voices,\n name: this.name,\n props: this.props,\n });\n this.pendingUIUpdates = false;\n });\n }\n\n findVoice(voiceNo: number) {\n const moduleByVoice = this.audioModules.find((m) => m.voiceNo === voiceNo);\n if (!moduleByVoice)\n throw Error(`Voice ${voiceNo} on module ${this.name} not found`);\n\n return moduleByVoice;\n }\n\n protected registerDefaultIOs(value: \"both\" | \"in\" | \"out\" = \"both\") {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n\n if (value === \"in\" || value === \"both\") {\n this.registerAudioInput({\n name: \"in\",\n });\n }\n\n if (value === \"out\" || value === \"both\") {\n this.registerAudioOutput({\n name: \"out\",\n });\n }\n }\n\n protected registerAudioInput(props: Omit<PolyAudioInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.PolyAudioInput });\n }\n\n protected registerAudioOutput(props: Omit<PolyAudioOutputProps, \"ioType\">) {\n return this.outputs.add({ ...props, ioType: IOType.PolyAudioOutput });\n }\n\n protected registerMidiInput(props: Omit<MidiInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.MidiInput });\n }\n\n protected registerMidiOutput(props: Omit<MidiOutputProps, \"ioType\">) {\n return this.outputs.add({\n ...props,\n ioType: IOType.MidiOutput,\n });\n }\n\n private adjustNumberOfModules() {\n if (this.audioModules.length === this.voices) return;\n\n if (this.audioModules.length > this.voices) {\n const audioModule = this.audioModules.pop();\n audioModule?.dispose();\n } else {\n const voiceNo = this.audioModules.length;\n const id = deterministicId(this.id, voiceNo.toString());\n\n const audioModule = this.monoModuleConstructor(this.engineId, {\n id,\n name: this.name,\n moduleType: this.moduleType,\n voiceNo,\n props: { ...this.props },\n });\n\n this.audioModules.push(audioModule);\n }\n\n this.adjustNumberOfModules();\n }\n\n protected get engine() {\n return Engine.getById(this.engineId);\n }\n\n protected get context() {\n return this.engine.context;\n }\n}\n","import { AudioParam } from \"@blibliki/utils/web-audio-api\";\nimport { ModuleType } from \"@/modules\";\nimport { Module } from \"../module\";\nimport IO, { IOProps, IOType } from \"./Base\";\nimport { PolyAudioInput, PolyAudioOutput } from \"./PolyAudioIO\";\n\nexport type AudioIO = AudioInput | AudioOutput;\n\nexport type AudioInputProps = IOProps & {\n ioType: IOType.AudioInput;\n getAudioNode: () => AudioNode | AudioParam | AudioDestinationNode;\n};\n\nexport type AudioOutputProps = IOProps & {\n ioType: IOType.AudioOutput;\n getAudioNode: () => AudioNode;\n};\n\nexport class AudioInput\n extends IO<AudioOutput | PolyAudioOutput>\n implements AudioInputProps\n{\n declare ioType: IOType.AudioInput;\n getAudioNode: AudioInputProps[\"getAudioNode\"];\n\n constructor(module: Module<ModuleType>, props: AudioInputProps) {\n super(module, props);\n this.getAudioNode = props.getAudioNode;\n }\n}\n\nexport class AudioOutput\n extends IO<AudioInput | PolyAudioInput>\n implements AudioOutputProps\n{\n declare ioType: IOType.AudioOutput;\n getAudioNode!: AudioOutputProps[\"getAudioNode\"];\n\n constructor(module: Module<ModuleType>, props: AudioOutputProps) {\n super(module, props);\n this.getAudioNode = props.getAudioNode;\n }\n\n plug(io: AudioInput | PolyAudioInput, plugOther = true) {\n super.plug(io, plugOther);\n if (io instanceof PolyAudioInput) return;\n\n const input = io.getAudioNode();\n\n if (input instanceof AudioParam) {\n this.getAudioNode().connect(input);\n } else {\n this.getAudioNode().connect(input);\n }\n }\n\n unPlug(io: AudioInput | PolyAudioInput, plugOther = true) {\n super.unPlug(io, plugOther);\n if (io instanceof PolyAudioInput) return;\n\n const input = io.getAudioNode();\n\n try {\n if (input instanceof AudioParam) {\n this.getAudioNode().disconnect(input);\n } else {\n this.getAudioNode().disconnect(input);\n }\n } catch {\n // Ignore disconnect errors\n }\n }\n}\n","import { deterministicId } from \"@blibliki/utils\";\nimport { ModuleType } from \"@/modules\";\nimport { Module } from \"../module\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport { AudioInput, AudioOutput } from \"./AudioIO\";\nimport { MidiInput, MidiOutput } from \"./MidiIO\";\nimport { PolyAudioInput, PolyAudioOutput } from \"./PolyAudioIO\";\n\nexport type IOProps = {\n name: string;\n ioType: IOType;\n};\n\nexport type IIOSerialize = IOProps & {\n id: string;\n moduleId: string;\n};\n\nexport enum IOType {\n AudioInput = \"audioInput\",\n AudioOutput = \"audioOutput\",\n PolyAudioInput = \"polyAudioInput\",\n PolyAudioOutput = \"polyAudioOutput\",\n MidiOutput = \"midiOutput\",\n MidiInput = \"midiInput\",\n}\n\nexport type IIO = {\n id: string;\n module: Module<ModuleType> | PolyModule<ModuleType>;\n} & IOProps;\n\nexport abstract class Base implements IIO {\n id: string;\n ioType: IOType;\n name: string;\n module: Module<ModuleType> | PolyModule<ModuleType>;\n connections: Base[];\n\n constructor(\n module: Module<ModuleType> | PolyModule<ModuleType>,\n props: IOProps,\n ) {\n this.module = module;\n this.name = props.name;\n this.ioType = props.ioType;\n this.id = deterministicId(this.module.id, this.name);\n this.connections = [];\n }\n\n plug(io: Base, plugOther = true) {\n this.connections.push(io);\n if (plugOther) io.plug(this, false);\n }\n\n unPlug(io: Base, plugOther = true) {\n this.connections = this.connections.filter(\n (currentIO) => currentIO.id !== io.id,\n );\n if (plugOther) io.unPlug(this, false);\n }\n\n rePlugAll(callback?: () => void) {\n const connections = this.connections;\n this.unPlugAll();\n if (callback) callback();\n\n connections.forEach((otherIO) => {\n this.plug(otherIO);\n });\n }\n\n unPlugAll() {\n this.connections.forEach((otherIO) => {\n this.unPlug(otherIO);\n });\n }\n\n isAudio(): this is\n | AudioInput\n | AudioOutput\n | PolyAudioInput\n | PolyAudioOutput {\n return (\n this.ioType === IOType.AudioInput ||\n this.ioType === IOType.AudioOutput ||\n this.ioType === IOType.PolyAudioInput ||\n this.ioType === IOType.PolyAudioOutput\n );\n }\n\n isMidi(): this is MidiInput | MidiOutput {\n return (\n this.ioType === IOType.MidiInput || this.ioType === IOType.MidiOutput\n );\n }\n\n serialize(): IIOSerialize {\n return {\n id: this.id,\n name: this.name,\n ioType: this.ioType,\n moduleId: this.module.id,\n };\n }\n}\n\nexport default abstract class IO<Connection extends Base> extends Base {\n declare connections: Connection[];\n\n plug(io: Connection, plugOther?: boolean): void {\n super.plug(io, plugOther);\n }\n\n unPlug(io: Connection, plugOther?: boolean): void {\n super.unPlug(io, plugOther);\n }\n}\n","import { ModuleType } from \"@/modules\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport { AudioInput, AudioOutput } from \"./AudioIO\";\nimport IO, { IOProps, IOType } from \"./Base\";\n\nexport type PolyAudioIO = PolyAudioInput | PolyAudioOutput;\n\nexport type PolyAudioInputProps = IOProps & {\n ioType: IOType.PolyAudioInput;\n};\n\nexport type PolyAudioOutputProps = IOProps & {\n ioType: IOType.PolyAudioOutput;\n};\n\nexport class PolyAudioInput\n extends IO<PolyAudioOutput | AudioOutput>\n implements PolyAudioInputProps\n{\n declare ioType: IOType.PolyAudioInput;\n declare module: PolyModule<ModuleType>;\n\n plug(io: PolyAudioOutput | AudioOutput, plugOther = true) {\n super.plug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioOutput) return;\n\n plugOrUnplug(this, io, true);\n }\n\n unPlug(io: PolyAudioOutput | AudioOutput, plugOther = true) {\n super.unPlug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioOutput) return;\n\n plugOrUnplug(this, io, false);\n }\n\n findIOByVoice(voice: number): AudioInput {\n return this.module\n .findVoice(voice)\n .inputs.findByName(this.name) as AudioInput;\n }\n}\n\nexport class PolyAudioOutput\n extends IO<PolyAudioInput | AudioInput>\n implements PolyAudioOutputProps\n{\n declare ioType: IOType.PolyAudioOutput;\n declare module: PolyModule<ModuleType>;\n\n plug(io: PolyAudioInput | AudioInput, plugOther = true) {\n super.plug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioInput) return;\n\n plugOrUnplug(this, io, true);\n }\n\n unPlug(io: PolyAudioInput | AudioInput, plugOther = true) {\n super.unPlug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioInput) return;\n\n plugOrUnplug(this, io, false);\n }\n\n findIOByVoice(voice: number): AudioOutput {\n return this.module\n .findVoice(voice)\n .outputs.findByName(this.name) as AudioOutput;\n }\n}\n\nfunction plugOrUnplug(\n thisIO: PolyAudioInput,\n otherIO: PolyAudioOutput | AudioOutput,\n isPlug: boolean,\n): void;\nfunction plugOrUnplug(\n thisIO: PolyAudioOutput,\n otherIO: PolyAudioInput | AudioInput,\n isPlug: boolean,\n): void;\nfunction plugOrUnplug(\n thisIO: PolyAudioInput | PolyAudioOutput,\n otherIO: PolyAudioOutput | AudioOutput | PolyAudioInput | AudioInput,\n isPlug: boolean,\n) {\n if (otherIO instanceof PolyAudioInput || otherIO instanceof PolyAudioOutput) {\n const maxVoices = Math.max(thisIO.module.voices, otherIO.module.voices);\n\n for (let voice = 0; voice < maxVoices; voice++) {\n const thisMonoIO = thisIO.findIOByVoice(voice % thisIO.module.voices);\n const otherMonoIO = otherIO.findIOByVoice(voice % otherIO.module.voices);\n\n if (isPlug) {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.plug(otherMonoIO);\n } else {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.unPlug(otherMonoIO);\n }\n }\n } else {\n for (let voice = 0; voice < thisIO.module.voices; voice++) {\n const thisMonoIO = thisIO.findIOByVoice(voice);\n\n if (isPlug) {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.plug(otherIO);\n } else {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.unPlug(otherIO);\n }\n }\n }\n}\n","import { ModuleType } from \"@/modules\";\nimport MidiEvent from \"../midi/MidiEvent\";\nimport { Module } from \"../module\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport IO, { IOProps, IOType } from \"./Base\";\n\nexport type MidiIO = MidiInput | MidiOutput;\n\nexport type MidiInputProps = IOProps & {\n ioType: IOType.MidiInput;\n onMidiEvent: (event: MidiEvent) => void;\n};\n\nexport type MidiOutputProps = IOProps & {\n ioType: IOType.MidiOutput;\n};\n\nexport class MidiInput extends IO<MidiOutput> implements MidiInputProps {\n declare ioType: IOType.MidiInput;\n onMidiEvent: MidiInputProps[\"onMidiEvent\"];\n\n constructor(\n module: Module<ModuleType> | PolyModule<ModuleType>,\n props: MidiInputProps,\n ) {\n super(module, props);\n this.onMidiEvent = props.onMidiEvent;\n }\n}\n\nexport class MidiOutput extends IO<MidiInput> implements MidiOutputProps {\n declare ioType: IOType.MidiOutput;\n\n onMidiEvent = (event: MidiEvent) => {\n this.midiConnections.forEach((input) => {\n input.onMidiEvent(event);\n });\n };\n\n private get midiConnections() {\n return this.connections.filter((input) => input instanceof MidiInput);\n }\n}\n","const frequencyTable = new Map<string, number>([\n [\"C0\", 16.35],\n [\"C#0\", 17.32],\n [\"Db0\", 17.32],\n [\"D0\", 18.35],\n [\"D#0\", 19.45],\n [\"Eb0\", 19.45],\n [\"E0\", 20.6],\n [\"F0\", 21.83],\n [\"F#0\", 23.12],\n [\"Gb0\", 23.12],\n [\"G0\", 24.5],\n [\"G#0\", 25.96],\n [\"Ab0\", 25.96],\n [\"A0\", 27.5],\n [\"A#0\", 29.14],\n [\"Bb0\", 29.14],\n [\"B0\", 30.87],\n [\"C1\", 32.7],\n [\"C#1\", 34.65],\n [\"Db1\", 34.65],\n [\"D1\", 36.71],\n [\"D#1\", 38.89],\n [\"Eb1\", 38.89],\n [\"E1\", 41.2],\n [\"F1\", 43.65],\n [\"F#1\", 46.25],\n [\"Gb1\", 46.25],\n [\"G1\", 49.0],\n [\"G#1\", 51.91],\n [\"Ab1\", 51.91],\n [\"A1\", 55.0],\n [\"A#1\", 58.27],\n [\"Bb1\", 58.27],\n [\"B1\", 61.74],\n [\"C2\", 65.41],\n [\"C#2\", 69.3],\n [\"Db2\", 69.3],\n [\"D2\", 73.42],\n [\"D#2\", 77.78],\n [\"Eb2\", 77.78],\n [\"E2\", 82.41],\n [\"F2\", 87.31],\n [\"F#2\", 92.5],\n [\"Gb2\", 92.5],\n [\"G2\", 98.0],\n [\"G#2\", 103.83],\n [\"Ab2\", 103.83],\n [\"A2\", 110.0],\n [\"A#2\", 116.54],\n [\"Bb2\", 116.54],\n [\"B2\", 123.47],\n [\"C3\", 130.81],\n [\"C#3\", 138.59],\n [\"Db3\", 138.59],\n [\"D3\", 146.83],\n [\"D#3\", 155.56],\n [\"Eb3\", 155.56],\n [\"E3\", 164.81],\n [\"F3\", 174.61],\n [\"F#3\", 185.0],\n [\"Gb3\", 185.0],\n [\"G3\", 196.0],\n [\"G#3\", 207.65],\n [\"Ab3\", 207.65],\n [\"A3\", 220.0],\n [\"A#3\", 233.08],\n [\"Bb3\", 233.08],\n [\"B3\", 246.94],\n [\"C4\", 261.63],\n [\"C#4\", 277.18],\n [\"Db4\", 277.18],\n [\"D4\", 293.66],\n [\"D#4\", 311.13],\n [\"Eb4\", 311.13],\n [\"E4\", 329.63],\n [\"F4\", 349.23],\n [\"F#4\", 369.99],\n [\"Gb4\", 369.99],\n [\"G4\", 392.0],\n [\"G#4\", 415.3],\n [\"Ab4\", 415.3],\n [\"A4\", 440.0],\n [\"A#4\", 466.16],\n [\"Bb4\", 466.16],\n [\"B4\", 493.88],\n [\"C5\", 523.25],\n [\"C#5\", 554.37],\n [\"Db5\", 554.37],\n [\"D5\", 587.33],\n [\"D#5\", 622.25],\n [\"Eb5\", 622.25],\n [\"E5\", 659.26],\n [\"F5\", 698.46],\n [\"F#5\", 739.99],\n [\"Gb5\", 739.99],\n [\"G5\", 783.99],\n [\"G#5\", 830.61],\n [\"Ab5\", 830.61],\n [\"A5\", 880.0],\n [\"A#5\", 932.33],\n [\"Bb5\", 932.33],\n [\"B5\", 987.77],\n [\"C6\", 1046.5],\n [\"C#6\", 1108.73],\n [\"Db6\", 1108.73],\n [\"D6\", 1174.66],\n [\"D#6\", 1244.51],\n [\"Eb6\", 1244.51],\n [\"E6\", 1318.51],\n [\"F6\", 1396.91],\n [\"F#6\", 1479.98],\n [\"Gb6\", 1479.98],\n [\"G6\", 1567.98],\n [\"G#6\", 1661.22],\n [\"Ab6\", 1661.22],\n [\"A6\", 1760.0],\n [\"A#6\", 1864.66],\n [\"Bb6\", 1864.66],\n [\"B6\", 1975.53],\n [\"C7\", 2093.0],\n [\"C#7\", 2217.46],\n [\"Db7\", 2217.46],\n [\"D7\", 2349.32],\n [\"D#7\", 2489.02],\n [\"Eb7\", 2489.02],\n [\"E7\", 2637.02],\n [\"F7\", 2793.83],\n [\"F#7\", 2959.96],\n [\"Gb7\", 2959.96],\n [\"G7\", 3135.96],\n [\"G#7\", 3322.44],\n [\"Ab7\", 3322.44],\n [\"A7\", 3520.0],\n [\"A#7\", 3729.31],\n [\"Bb7\", 3729.31],\n [\"B7\", 3951.07],\n [\"C8\", 4186.01],\n [\"C#8\", 4434.92],\n [\"Db8\", 4434.92],\n [\"D8\", 4698.64],\n [\"D#8\", 4978.03],\n [\"Eb8\", 4978.03],\n]);\n\nexport default frequencyTable;\n","import { Seconds } from \"@blibliki/transport\";\nimport Message from \"../midi/Message\";\nimport frequencyTable from \"./frequencyTable\";\n\nconst Notes = [\"C\", \"C#\", \"D\", \"D#\", \"E\", \"F\", \"F#\", \"G\", \"G#\", \"A\", \"A#\", \"B\"];\n\nconst MIDI_OCTAVE_SYTSTEM = 2;\n\nexport type INote = {\n name: string;\n octave: number;\n frequency: number;\n duration?: Seconds;\n velocity?: number;\n};\n\nexport default class Note implements INote {\n static _notes: Note[];\n name!: string;\n octave!: number;\n velocity = 1;\n duration?: Seconds;\n\n static fromFrequency(frequency: number) {\n let noteName: string | undefined;\n\n for (const [note, freq] of frequencyTable) {\n if (freq !== frequency) continue;\n\n noteName = note;\n break;\n }\n\n if (!noteName) throw Error(\"Not matching frequency with a note\");\n\n return new Note(noteName);\n }\n\n static fromEvent(message: Message) {\n const dataByte = message.data[1];\n if (dataByte === undefined) {\n throw new Error(\"Invalid MIDI message: missing data byte\");\n }\n const name = Notes[dataByte % 12];\n if (!name) {\n throw new Error(`Invalid MIDI note number: ${dataByte}`);\n }\n const octave = Math.floor(dataByte / 12) - 2;\n\n return new Note(`${name}${octave}`);\n }\n\n static notes(octave = 3) {\n return Notes.map((note: string) => new Note(`${note}${octave}`));\n }\n\n constructor(note: Omit<INote, \"frequency\"> | string) {\n if (typeof note === \"string\") {\n this.fromString(note);\n } else {\n this.fromProps(note);\n }\n }\n\n get isSemi() {\n return this.name.endsWith(\"#\");\n }\n\n get fullName() {\n return `${this.name}${this.octave}`;\n }\n\n get frequency(): number {\n return frequencyTable.get(`${this.name}${this.octave}`)!;\n }\n\n midiData(noteOn = true): Uint8Array {\n const statusByte = noteOn ? 0x90 : 0x80;\n return new Uint8Array([statusByte, this.midiNumber, this.velocity * 100]);\n }\n\n get midiNumber(): number {\n return (this.octave + MIDI_OCTAVE_SYTSTEM) * 12 + this.noteIndex;\n }\n\n get noteIndex(): number {\n return Notes.indexOf(this.name);\n }\n\n valueOf() {\n return this.fullName;\n }\n\n serialize(): INote {\n return {\n name: this.name,\n octave: this.octave,\n frequency: this.frequency,\n velocity: this.velocity,\n duration: this.duration,\n };\n }\n\n private fromString(string: string) {\n const matches = /(\\w#?)(\\d)?/.exec(string);\n if (!matches) {\n throw new Error(`Invalid note string: ${string}`);\n }\n\n const name = matches[1];\n if (!name) {\n throw new Error(`Invalid note name in: ${string}`);\n }\n\n this.name = name;\n this.octave = matches[2] ? parseInt(matches[2]) : 1;\n }\n\n private fromProps(props: Omit<INote, \"frequency\">) {\n Object.assign(this, props);\n }\n}\n","/**\n * Simple wrapper around MIDI message data (Uint8Array)\n * Replaces the webmidi Message class with native Web MIDI API data\n */\nexport default class Message {\n public readonly data: Uint8Array;\n\n constructor(data: Uint8Array) {\n this.data = data;\n }\n\n /**\n * Returns the data bytes (excluding the status byte)\n */\n get dataBytes(): number[] {\n return Array.from(this.data.slice(1));\n }\n\n /**\n * Returns the MIDI message type based on the status byte\n */\n get type(): string {\n const statusByte = this.data[0];\n if (statusByte === undefined) return \"unknown\";\n const messageType = statusByte & 0xf0;\n\n switch (messageType) {\n case 0x90: // Note On\n // Check if velocity is 0 (which is actually Note Off)\n return this.data[2] === 0 ? \"noteoff\" : \"noteon\";\n case 0x80: // Note Off\n return \"noteoff\";\n case 0xb0: // Control Change\n return \"controlchange\";\n case 0xe0: // Pitch Bend\n return \"pitchbend\";\n case 0xd0: // Channel Pressure (Aftertouch)\n return \"channelaftertouch\";\n case 0xa0: // Polyphonic Key Pressure\n return \"keyaftertouch\";\n case 0xc0: // Program Change\n return \"programchange\";\n default:\n return \"unknown\";\n }\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport Note, { INote } from \"../Note\";\nimport Message from \"./Message\";\n\nexport enum MidiEventType {\n noteOn = \"noteon\",\n noteOff = \"noteoff\",\n cc = \"controlchange\",\n}\n\nexport default class MidiEvent {\n note?: Note;\n voiceNo?: number;\n readonly triggeredAt: ContextTime;\n private message: Message;\n\n static fromNote(\n noteName: string | Note | Omit<INote, \"frequency\">,\n noteOn = true,\n triggeredAt: ContextTime,\n ): MidiEvent {\n const note = noteName instanceof Note ? noteName : new Note(noteName);\n\n return new MidiEvent(new Message(note.midiData(noteOn)), triggeredAt);\n }\n\n static fromCC(\n cc: number,\n value: number,\n triggeredAt: ContextTime,\n ): MidiEvent {\n return new MidiEvent(\n new Message(new Uint8Array([0xb0, cc, value])),\n triggeredAt,\n );\n }\n\n constructor(message: Message, triggeredAt: ContextTime) {\n this.message = message;\n this.triggeredAt = triggeredAt;\n this.defineNotes();\n }\n\n get type() {\n return this.message.type as MidiEventType;\n }\n\n get isNote() {\n return (\n this.type === MidiEventType.noteOn || this.type === MidiEventType.noteOff\n );\n }\n\n get isCC() {\n return this.type === MidiEventType.cc;\n }\n\n get cc(): number | undefined {\n if (!this.isCC) return;\n\n return this.message.dataBytes[0];\n }\n\n get ccValue(): number | undefined {\n if (!this.isCC) return;\n\n return this.message.dataBytes[1];\n }\n\n defineNotes() {\n if (!this.isNote) return;\n if (this.note) return;\n\n this.note = Note.fromEvent(this.message);\n }\n\n get rawMessage() {\n return this.message;\n }\n\n clone(voiceNo?: number) {\n const newEvent = new MidiEvent(this.message, this.triggeredAt);\n newEvent.voiceNo = voiceNo;\n\n return newEvent;\n }\n}\n","import { Optional, uuidv4 } from \"@blibliki/utils\";\nimport { Engine } from \"@/Engine\";\n\ntype IPlug = {\n moduleId: string;\n ioName: string;\n};\n\nexport type IRoute = {\n id: string;\n source: IPlug;\n destination: IPlug;\n};\n\nexport class Routes {\n engine: Engine;\n routes: Map<string, IRoute>;\n\n constructor(engine: Engine) {\n this.engine = engine;\n this.routes = new Map();\n }\n\n addRoute(props: Optional<IRoute, \"id\">): IRoute {\n const id = props.id ?? uuidv4();\n const route = { ...props, id };\n this.routes.set(id, route);\n\n this.plug(id);\n\n return route;\n }\n\n removeRoute(id: string) {\n this.unPlug(id);\n this.routes.delete(id);\n }\n\n clear() {\n this.routes.forEach((_, id) => {\n this.removeRoute(id);\n });\n }\n\n replug() {\n this.routes.forEach((_, id) => {\n const { sourceIO, destinationIO } = this.getIOs(id);\n sourceIO.rePlugAll();\n destinationIO.rePlugAll();\n });\n }\n\n serialize(): IRoute[] {\n return Array.from(this.routes.values());\n }\n\n private plug(id: string) {\n const { sourceIO, destinationIO } = this.getIOs(id);\n sourceIO.plug(destinationIO);\n }\n\n private unPlug(id: string) {\n const { sourceIO, destinationIO } = this.getIOs(id);\n sourceIO.unPlug(destinationIO);\n }\n\n private find(id: string): IRoute {\n const route = this.routes.get(id);\n if (!route) throw Error(`Route with id ${id} not found`);\n\n return route;\n }\n\n private getIOs(id: string) {\n const route = this.find(id);\n const { source, destination } = route;\n\n const sourceIO = this.engine.findIO(\n source.moduleId,\n source.ioName,\n \"output\",\n );\n const destinationIO = this.engine.findIO(\n destination.moduleId,\n destination.ioName,\n \"input\",\n );\n\n return { sourceIO, destinationIO };\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport Message from \"./Message\";\nimport MidiEvent, { MidiEventType } from \"./MidiEvent\";\nimport type { IMidiInputPort, IMidiMessageEvent } from \"./adapters\";\n\nexport enum MidiPortState {\n connected = \"connected\",\n disconnected = \"disconnected\",\n}\n\nexport type IMidiDevice = {\n id: string;\n name: string;\n state: MidiPortState;\n};\n\nexport type IMidiInput = IMidiDevice & {\n eventListerCallbacks: EventListerCallback[];\n};\n\nexport type EventListerCallback = (event: MidiEvent) => void;\n\nexport default class MidiDevice implements IMidiDevice {\n id: string;\n name: string;\n eventListerCallbacks: EventListerCallback[] = [];\n\n private context: Readonly<Context>;\n private input: IMidiInputPort;\n private messageHandler: ((event: IMidiMessageEvent) => void) | null = null;\n\n constructor(input: IMidiInputPort, context: Context) {\n this.id = input.id;\n this.name = input.name;\n this.input = input;\n this.context = context;\n\n this.connect();\n }\n\n get state() {\n return this.input.state as MidiPortState;\n }\n\n connect() {\n this.messageHandler = (e: IMidiMessageEvent) => {\n this.processEvent(e);\n };\n this.input.addEventListener(this.messageHandler);\n }\n\n disconnect() {\n if (this.messageHandler) {\n this.input.removeEventListener(this.messageHandler);\n this.messageHandler = null;\n }\n }\n\n serialize() {\n const { id, name, state } = this;\n\n return { id, name, state };\n }\n\n addEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks.push(callback);\n }\n\n removeEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks = this.eventListerCallbacks.filter(\n (c) => c !== callback,\n );\n }\n\n private processEvent(event: IMidiMessageEvent) {\n const message = new Message(event.data);\n const midiEvent = new MidiEvent(\n message,\n this.context.browserToContextTime(event.timeStamp),\n );\n\n switch (midiEvent.type) {\n case MidiEventType.noteOn:\n case MidiEventType.noteOff:\n case MidiEventType.cc:\n this.eventListerCallbacks.forEach((callback) => {\n callback(midiEvent);\n });\n }\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport Note from \"../Note\";\nimport { EventListerCallback, IMidiInput, MidiPortState } from \"./MidiDevice\";\nimport MidiEvent from \"./MidiEvent\";\n\nconst MAP_KEYS: Record<string, Note> = {\n a: new Note(\"C3\"),\n s: new Note(\"D3\"),\n d: new Note(\"E3\"),\n f: new Note(\"F3\"),\n g: new Note(\"G3\"),\n h: new Note(\"A3\"),\n j: new Note(\"B3\"),\n k: new Note(\"C4\"),\n l: new Note(\"D4\"),\n w: new Note(\"C#3\"),\n e: new Note(\"D#3\"),\n t: new Note(\"F#3\"),\n y: new Note(\"G#3\"),\n u: new Note(\"A#3\"),\n o: new Note(\"C#4\"),\n p: new Note(\"D#4\"),\n};\n\nconst computerKeyboardData = () => ({\n id: \"computer_keyboard\",\n name: \"Computer Keyboard\",\n state: MidiPortState.connected,\n});\n\nexport default class ComputerKeyboardInput implements IMidiInput {\n id: string;\n name: string;\n state: MidiPortState;\n eventListerCallbacks: EventListerCallback[] = [];\n private context: Readonly<Context>;\n\n constructor(context: Context) {\n const { id, name, state } = computerKeyboardData();\n this.id = id;\n this.name = name;\n this.state = state;\n this.context = context;\n\n document.addEventListener(\"keydown\", this.onKeyTrigger(true));\n document.addEventListener(\"keyup\", this.onKeyTrigger(false));\n }\n\n addEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks.push(callback);\n }\n\n removeEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks = this.eventListerCallbacks.filter(\n (c) => c !== callback,\n );\n }\n\n serialize() {\n const { id, name, state } = this;\n\n return { id, name, state };\n }\n\n onKeyTrigger = (noteOn: boolean) => (event: KeyboardEvent) => {\n const note = this.extractNote(event);\n if (!note) return;\n\n const midiEvent = MidiEvent.fromNote(\n note,\n noteOn,\n this.context.browserToContextTime(event.timeStamp),\n );\n this.eventListerCallbacks.forEach((callback) => {\n callback(midiEvent);\n });\n };\n\n private extractNote(event: KeyboardEvent): Note | undefined {\n if (event.repeat) return;\n\n return MAP_KEYS[event.key];\n }\n}\n","/**\n * MIDI adapter factory\n * Automatically selects the correct MIDI implementation based on the platform\n */\nimport { isNode } from \"es-toolkit\";\nimport NodeMidiAdapter from \"./NodeMidiAdapter\";\nimport WebMidiAdapter from \"./WebMidiAdapter\";\nimport type { IMidiAdapter } from \"./types\";\n\nexport * from \"./types\";\n\n/**\n * Creates the appropriate MIDI adapter for the current platform\n * @returns The MIDI adapter (Web MIDI API for browsers, node-midi for Node.js)\n */\nexport function createMidiAdapter(): IMidiAdapter {\n if (isNode()) {\n return new NodeMidiAdapter();\n }\n\n // Default to Web MIDI API for browsers\n return new WebMidiAdapter();\n}\n","/**\n * node-midi adapter for Node.js\n */\nimport { isNode } from \"es-toolkit\";\nimport type {\n IMidiAccess,\n IMidiAdapter,\n IMidiInputPort,\n MidiMessageCallback,\n} from \"./types\";\n\n// Dynamic import type for node-midi\ntype NodeMidiInput = {\n getPortCount(): number;\n getPortName(port: number): string;\n openPort(port: number): void;\n closePort(): void;\n on(\n event: \"message\",\n callback: (deltaTime: number, message: number[]) => void,\n ): void;\n off(\n event: \"message\",\n callback: (deltaTime: number, message: number[]) => void,\n ): void;\n isPortOpen(): boolean;\n};\n\ntype NodeMidiModule = {\n Input: new () => NodeMidiInput;\n};\n\nclass NodeMidiInputPort implements IMidiInputPort {\n readonly id: string;\n readonly name: string;\n private portIndex: number;\n private input: NodeMidiInput;\n private callbacks = new Set<MidiMessageCallback>();\n private handler: ((deltaTime: number, message: number[]) => void) | null =\n null;\n private _state: \"connected\" | \"disconnected\" = \"disconnected\";\n\n constructor(portIndex: number, name: string, input: NodeMidiInput) {\n this.portIndex = portIndex;\n this.id = `node-midi-${portIndex}`;\n this.name = name;\n this.input = input;\n }\n\n get state(): \"connected\" | \"disconnected\" {\n return this._state;\n }\n\n setState(state: \"connected\" | \"disconnected\"): void {\n this._state = state;\n }\n\n addEventListener(callback: MidiMessageCallback): void {\n if (this.callbacks.size === 0) {\n this.handler = (_deltaTime: number, message: number[]) => {\n const event = {\n data: new Uint8Array(message),\n timeStamp: performance.now(),\n };\n\n this.callbacks.forEach((cb) => {\n cb(event);\n });\n };\n\n try {\n if (!this.input.isPortOpen()) {\n this.input.openPort(this.portIndex);\n this._state = \"connected\";\n }\n this.input.on(\"message\", this.handler);\n } catch (err) {\n console.error(`Error opening MIDI port ${this.portIndex}:`, err);\n }\n }\n this.callbacks.add(callback);\n }\n\n removeEventListener(callback: MidiMessageCallback): void {\n this.callbacks.delete(callback);\n\n if (this.callbacks.size === 0 && this.handler) {\n try {\n this.input.off(\"message\", this.handler);\n if (this.input.isPortOpen()) {\n this.input.closePort();\n this._state = \"disconnected\";\n }\n } catch (err) {\n console.error(`Error closing MIDI port ${this.portIndex}:`, err);\n }\n this.handler = null;\n }\n }\n}\n\nclass NodeMidiAccess implements IMidiAccess {\n private ports = new Map<string, NodeMidiInputPort>();\n private MidiModule: NodeMidiModule;\n\n constructor(MidiModule: NodeMidiModule) {\n this.MidiModule = MidiModule;\n this.scanPorts();\n }\n\n private scanPorts(): void {\n try {\n const input = new this.MidiModule.Input();\n const portCount = input.getPortCount();\n\n for (let i = 0; i < portCount; i++) {\n const portName = input.getPortName(i);\n const id = `node-midi-${i}`;\n\n if (!this.ports.has(id)) {\n // Create a new input instance for each port\n const portInput = new this.MidiModule.Input();\n const port = new NodeMidiInputPort(i, portName, portInput);\n this.ports.set(id, port);\n }\n }\n\n // Clean up the scanning input\n if (input.isPortOpen()) {\n input.closePort();\n }\n } catch (err) {\n console.error(\"Error scanning MIDI ports:\", err);\n }\n }\n\n *inputs(): IterableIterator<IMidiInputPort> {\n for (const [, port] of this.ports) {\n yield port;\n }\n }\n\n addEventListener(\n _event: \"statechange\",\n _callback: (port: IMidiInputPort) => void,\n ): void {\n // node-midi doesn't support hot-plugging detection\n // This could be implemented with polling if needed\n console.warn(\n \"Hot-plug detection not supported with node-midi adapter. Restart required for new devices.\",\n );\n }\n}\n\nexport default class NodeMidiAdapter implements IMidiAdapter {\n async requestMIDIAccess(): Promise<IMidiAccess | null> {\n try {\n // Dynamic import to avoid bundling in browser builds\n const midi = (await import(\"@julusian/midi\")) as\n | NodeMidiModule\n | { default: NodeMidiModule };\n const midiModule = \"default\" in midi ? midi.default : midi;\n return new NodeMidiAccess(midiModule);\n } catch (err) {\n console.error(\"Error loading node-midi:\", err);\n return null;\n }\n }\n\n isSupported(): boolean {\n // Check if we're in Node.js environment\n return isNode();\n }\n}\n","/**\n * Web MIDI API adapter for browsers\n */\nimport type {\n IMidiAccess,\n IMidiAdapter,\n IMidiInputPort,\n MidiMessageCallback,\n} from \"./types\";\n\nclass WebMidiInputPort implements IMidiInputPort {\n private input: MIDIInput;\n private callbacks = new Set<MidiMessageCallback>();\n private handler: ((e: MIDIMessageEvent) => void) | null = null;\n\n constructor(input: MIDIInput) {\n this.input = input;\n }\n\n get id(): string {\n return this.input.id;\n }\n\n get name(): string {\n return this.input.name ?? `Device ${this.input.id}`;\n }\n\n get state(): \"connected\" | \"disconnected\" {\n return this.input.state as \"connected\" | \"disconnected\";\n }\n\n addEventListener(callback: MidiMessageCallback): void {\n if (this.callbacks.size === 0) {\n this.handler = (e: MIDIMessageEvent) => {\n if (!e.data) return;\n\n const event = {\n data: e.data,\n timeStamp: e.timeStamp,\n };\n\n this.callbacks.forEach((cb) => {\n cb(event);\n });\n };\n this.input.addEventListener(\"midimessage\", this.handler);\n }\n this.callbacks.add(callback);\n }\n\n removeEventListener(callback: MidiMessageCallback): void {\n this.callbacks.delete(callback);\n\n if (this.callbacks.size === 0 && this.handler) {\n this.input.removeEventListener(\"midimessage\", this.handler);\n this.handler = null;\n }\n }\n}\n\nclass WebMidiAccess implements IMidiAccess {\n private midiAccess: MIDIAccess;\n private portCache = new Map<string, WebMidiInputPort>();\n\n constructor(midiAccess: MIDIAccess) {\n this.midiAccess = midiAccess;\n }\n\n *inputs(): IterableIterator<IMidiInputPort> {\n for (const [, input] of this.midiAccess.inputs) {\n if (!this.portCache.has(input.id)) {\n this.portCache.set(input.id, new WebMidiInputPort(input));\n }\n yield this.portCache.get(input.id)!;\n }\n }\n\n addEventListener(\n event: \"statechange\",\n callback: (port: IMidiInputPort) => void,\n ): void {\n this.midiAccess.addEventListener(event, (e) => {\n const port = e.port;\n if (port?.type !== \"input\") return;\n\n const input = port as MIDIInput;\n if (!this.portCache.has(input.id)) {\n this.portCache.set(input.id, new WebMidiInputPort(input));\n }\n\n callback(this.portCache.get(input.id)!);\n });\n }\n}\n\nexport default class WebMidiAdapter implements IMidiAdapter {\n async requestMIDIAccess(): Promise<IMidiAccess | null> {\n try {\n if (\n typeof navigator === \"undefined\" ||\n typeof navigator.requestMIDIAccess !== \"function\"\n ) {\n return null;\n }\n\n const midiAccess = await navigator.requestMIDIAccess();\n return new WebMidiAccess(midiAccess);\n } catch (err) {\n console.error(\"Error enabling Web MIDI API:\", err);\n return null;\n }\n }\n\n isSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n typeof navigator.requestMIDIAccess === \"function\"\n );\n }\n}\n","/**\n * Utilities for fuzzy matching MIDI device names across different platforms\n *\n * MIDI devices can have different names in browser vs Node.js:\n * - Browser: \"Launchkey Mini MK3 MIDI 1\"\n * - Node.js: \"Launchkey Mini MK3:Launchkey Mini MK3 MIDI 1 20:0\"\n *\n * This module provides fuzzy matching to find the same physical device\n * across platforms based on name similarity.\n */\n\n/**\n * Normalizes a MIDI device name by:\n * - Converting to lowercase\n * - Removing common suffixes/prefixes\n * - Removing port numbers and ALSA identifiers\n * - Removing extra whitespace\n * - Removing special characters\n */\nexport function normalizeDeviceName(name: string): string {\n let normalized = name.toLowerCase();\n\n // First, remove ALSA port numbers (e.g., \"20:0\" at the very end)\n // Do this BEFORE splitting by colons\n normalized = normalized.replace(/\\s+\\d+:\\d+\\s*$/g, \"\");\n\n // Remove colon-separated duplicates (Node.js format: \"Device:Device Port\")\n const parts = normalized.split(\":\");\n if (parts.length > 1) {\n // Take the longest part as it usually has more info\n normalized = parts.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n }\n\n // Remove common port descriptors (but keep the number if it's part of the device name)\n // This regex only matches MIDI/Input/Output/Port at the END of the string\n normalized = normalized.replace(\n /\\s+(midi|input|output|port)(\\s+\\d+)?$/gi,\n \"\",\n );\n\n // Remove \"device\" prefix if present at the start\n normalized = normalized.replace(/^device\\s+/gi, \"\");\n\n // Remove multiple spaces\n normalized = normalized.replace(/\\s+/g, \" \").trim();\n\n return normalized;\n}\n\n/**\n * Extracts core device name tokens for matching\n * Removes generic words and focuses on manufacturer/model identifiers\n */\nexport function extractCoreTokens(name: string): string[] {\n const normalized = normalizeDeviceName(name);\n\n // Split into tokens\n const tokens = normalized.split(/[\\s\\-_:]+/);\n\n // Filter out very short tokens and common generic words\n const genericWords = new Set([\n \"midi\",\n \"input\",\n \"output\",\n \"port\",\n \"device\",\n \"in\",\n \"out\",\n ]);\n\n return tokens.filter((token) => token.length > 1 && !genericWords.has(token));\n}\n\n/**\n * Calculates Levenshtein distance between two strings\n * Used for fuzzy string matching\n */\nfunction levenshteinDistance(str1: string, str2: string): number {\n const len1 = str1.length;\n const len2 = str2.length;\n const matrix: number[][] = [];\n\n // Initialize matrix\n for (let i = 0; i <= len1; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= len2; j++) {\n if (matrix[0]) {\n matrix[0][j] = j;\n }\n }\n\n // Fill matrix\n for (let i = 1; i <= len1; i++) {\n for (let j = 1; j <= len2; j++) {\n const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;\n const prevRow = matrix[i - 1];\n const currRow = matrix[i];\n const prevCell = currRow?.[j - 1];\n const diagCell = prevRow?.[j - 1];\n\n const prevCellInRow = prevRow?.[j];\n if (\n currRow &&\n prevCellInRow !== undefined &&\n prevCell !== undefined &&\n diagCell !== undefined\n ) {\n currRow[j] = Math.min(\n prevCellInRow + 1, // deletion\n prevCell + 1, // insertion\n diagCell + cost, // substitution\n );\n }\n }\n }\n\n const lastRow = matrix[len1];\n return lastRow?.[len2] ?? 0;\n}\n\n/**\n * Calculates similarity score between two device names\n * Returns a score between 0 (no match) and 1 (perfect match)\n *\n * Uses multiple strategies:\n * 1. Exact normalized match (score: 1.0)\n * 2. Token overlap (Jaccard similarity)\n * 3. String similarity (based on Levenshtein distance)\n * 4. Substring matching\n */\nexport function calculateSimilarity(name1: string, name2: string): number {\n const normalized1 = normalizeDeviceName(name1);\n const normalized2 = normalizeDeviceName(name2);\n\n // Exact match after normalization\n if (normalized1 === normalized2) {\n return 1.0;\n }\n\n const tokens1 = new Set(extractCoreTokens(name1));\n const tokens2 = new Set(extractCoreTokens(name2));\n\n // If no tokens extracted, fall back to pure string similarity\n if (tokens1.size === 0 || tokens2.size === 0) {\n const maxLen = Math.max(normalized1.length, normalized2.length);\n if (maxLen === 0) return 0;\n const distance = levenshteinDistance(normalized1, normalized2);\n return 1 - distance / maxLen;\n }\n\n // Calculate Jaccard similarity for tokens\n const intersection = new Set([...tokens1].filter((x) => tokens2.has(x)));\n const union = new Set([...tokens1, ...tokens2]);\n const jaccardScore = intersection.size / union.size;\n\n // Calculate string similarity\n const maxLen = Math.max(normalized1.length, normalized2.length);\n const distance = levenshteinDistance(normalized1, normalized2);\n const stringScore = 1 - distance / maxLen;\n\n // Check for substring matches\n const substringScore =\n normalized1.includes(normalized2) || normalized2.includes(normalized1)\n ? 0.8\n : 0;\n\n // Weighted combination of scores\n // Token overlap is most important, then string similarity, then substring\n const finalScore =\n jaccardScore * 0.5 + stringScore * 0.3 + substringScore * 0.2;\n\n return finalScore;\n}\n\n/**\n * Finds the best matching device name from a list of candidates\n * Returns the best match and its confidence score\n *\n * @param targetName - The name to match against\n * @param candidateNames - List of possible matches\n * @param threshold - Minimum similarity score to consider a match (default: 0.6)\n * @returns Best match and score, or null if no match above threshold\n */\nexport function findBestMatch(\n targetName: string,\n candidateNames: string[],\n threshold = 0.6,\n): { name: string; score: number } | null {\n let bestMatch: { name: string; score: number } | null = null;\n\n for (const candidateName of candidateNames) {\n const score = calculateSimilarity(targetName, candidateName);\n\n if (score >= threshold && (!bestMatch || score > bestMatch.score)) {\n bestMatch = { name: candidateName, score };\n }\n }\n\n return bestMatch;\n}\n","import { Context } from \"@blibliki/utils\";\nimport ComputerKeyboardDevice from \"./ComputerKeyboardDevice\";\nimport MidiDevice from \"./MidiDevice\";\nimport { createMidiAdapter, type IMidiAccess } from \"./adapters\";\nimport { findBestMatch } from \"./deviceMatcher\";\n\ntype ListenerCallback = (device: MidiDevice) => void;\n\nexport default class MidiDeviceManager {\n devices = new Map<string, MidiDevice | ComputerKeyboardDevice>();\n private initialized = false;\n private listeners: ListenerCallback[] = [];\n private context: Readonly<Context>;\n private midiAccess: IMidiAccess | null = null;\n private adapter = createMidiAdapter();\n\n constructor(context: Context) {\n this.context = context;\n this.addComputerKeyboard();\n }\n\n async initialize() {\n await this.initializeDevices();\n\n this.listenChanges();\n this.initialized = true;\n }\n\n find(id: string): MidiDevice | ComputerKeyboardDevice | undefined {\n return this.devices.get(id);\n }\n\n findByName(name: string): MidiDevice | ComputerKeyboardDevice | undefined {\n return Array.from(this.devices.values()).find((d) => d.name === name);\n }\n\n /**\n * Finds a device using fuzzy name matching\n * Useful for matching devices across browser/Node.js environments where names differ\n *\n * @param targetName - The device name to match\n * @param threshold - Minimum similarity score (0-1, default: 0.6)\n * @returns The best matching device and confidence score, or null\n */\n findByFuzzyName(\n targetName: string,\n threshold = 0.6,\n ): { device: MidiDevice | ComputerKeyboardDevice; score: number } | null {\n const deviceEntries = Array.from(this.devices.values());\n const candidateNames = deviceEntries.map((d) => d.name);\n\n const match = findBestMatch(targetName, candidateNames, threshold);\n\n if (!match) return null;\n\n const device = deviceEntries.find((d) => d.name === match.name);\n\n return device ? { device, score: match.score } : null;\n }\n\n addListener(callback: ListenerCallback) {\n this.listeners.push(callback);\n }\n\n private async initializeDevices() {\n if (this.initialized) return;\n\n try {\n if (!this.adapter.isSupported()) {\n console.warn(\"MIDI is not supported on this platform\");\n return;\n }\n\n this.midiAccess = await this.adapter.requestMIDIAccess();\n\n if (!this.midiAccess) {\n console.error(\"Failed to get MIDI access\");\n return;\n }\n\n for (const input of this.midiAccess.inputs()) {\n if (!this.devices.has(input.id)) {\n this.devices.set(input.id, new MidiDevice(input, this.context));\n }\n }\n } catch (err) {\n console.error(\"Error enabling MIDI:\", err);\n }\n }\n\n private addComputerKeyboard() {\n if (typeof document === \"undefined\") return;\n\n const computerKeyboardDevice = new ComputerKeyboardDevice(this.context);\n this.devices.set(computerKeyboardDevice.id, computerKeyboardDevice);\n }\n\n private listenChanges() {\n if (!this.midiAccess) return;\n\n this.midiAccess.addEventListener(\"statechange\", (port) => {\n if (port.state === \"connected\") {\n // Device connected\n if (this.devices.has(port.id)) return;\n\n const device = new MidiDevice(port, this.context);\n this.devices.set(device.id, device);\n\n this.listeners.forEach((listener) => {\n listener(device);\n });\n } else {\n // Device disconnected\n const device = this.devices.get(port.id);\n if (!device) return;\n if (device instanceof ComputerKeyboardDevice) return;\n\n device.disconnect();\n this.devices.delete(device.id);\n\n this.listeners.forEach((listener) => {\n listener(device);\n });\n }\n });\n }\n}\n","import { assertNever } from \"@blibliki/utils\";\nimport { IModule, Module } from \"@/core\";\nimport { IPolyModuleConstructor } from \"@/core/module/PolyModule\";\nimport VoiceScheduler, {\n IVoiceSchedulerProps,\n voiceSchedulerPropSchema,\n} from \"@/core/module/VoiceScheduler\";\nimport Constant, { constantPropSchema, IConstantProps } from \"./Constant\";\nimport Envelope, { envelopePropSchema, IEnvelopeProps } from \"./Envelope\";\nimport Filter, { filterPropSchema, IFilterProps } from \"./Filter\";\nimport Gain, { gainPropSchema, IGainProps } from \"./Gain\";\nimport Inspector, { IInspectorProps, inspectorPropSchema } from \"./Inspector\";\nimport Master, { IMasterProps, masterPropSchema } from \"./Master\";\nimport MidiMapper, {\n IMidiMapperProps,\n midiMapperPropSchema,\n} from \"./MidiMapper\";\nimport MidiSelector, {\n IMidiSelectorProps,\n midiSelectorPropSchema,\n} from \"./MidiSelector\";\nimport Oscillator, {\n IOscillatorProps,\n oscillatorPropSchema,\n} from \"./Oscillator\";\nimport Scale, { IScaleProps, scalePropSchema } from \"./Scale\";\nimport StepSequencer, {\n IStepSequencerProps,\n stepSequencerPropSchema,\n} from \"./StepSequencer\";\nimport StereoPanner, {\n IStereoPannerProps,\n stereoPannerPropSchema,\n} from \"./StereoPanner\";\nimport VirtualMidi, {\n IVirtualMidiProps,\n virtualMidiPropSchema,\n} from \"./VirtualMidi\";\n\nexport enum ModuleType {\n Master = \"Master\",\n Oscillator = \"Oscillator\",\n Gain = \"Gain\",\n MidiSelector = \"MidiSelector\",\n Envelope = \"Envelope\",\n Filter = \"Filter\",\n Scale = \"Scale\",\n StereoPanner = \"StereoPanner\",\n Inspector = \"Inspector\",\n Constant = \"Constant\",\n MidiMapper = \"MidiMapper\",\n VirtualMidi = \"VirtualMidi\",\n StepSequencer = \"StepSequencer\",\n VoiceScheduler = \"VoiceScheduler\",\n}\n\nexport type ModuleTypeToPropsMapping = {\n [ModuleType.Oscillator]: IOscillatorProps;\n [ModuleType.Gain]: IGainProps;\n [ModuleType.Master]: IMasterProps;\n [ModuleType.MidiSelector]: IMidiSelectorProps;\n [ModuleType.Envelope]: IEnvelopeProps;\n [ModuleType.Filter]: IFilterProps;\n [ModuleType.Scale]: IScaleProps;\n [ModuleType.StereoPanner]: IStereoPannerProps;\n [ModuleType.Inspector]: IInspectorProps;\n [ModuleType.Constant]: IConstantProps;\n [ModuleType.MidiMapper]: IMidiMapperProps;\n [ModuleType.VirtualMidi]: IVirtualMidiProps;\n [ModuleType.StepSequencer]: IStepSequencerProps;\n [ModuleType.VoiceScheduler]: IVoiceSchedulerProps;\n};\n\nexport type ModuleTypeToModuleMapping = {\n [ModuleType.Oscillator]: Oscillator;\n [ModuleType.Gain]: Gain;\n [ModuleType.Master]: Master;\n [ModuleType.MidiSelector]: MidiSelector;\n [ModuleType.Envelope]: Envelope;\n [ModuleType.Filter]: Filter;\n [ModuleType.Scale]: Scale;\n [ModuleType.StereoPanner]: StereoPanner;\n [ModuleType.Inspector]: Inspector;\n [ModuleType.Constant]: Constant;\n [ModuleType.MidiMapper]: MidiMapper;\n [ModuleType.VirtualMidi]: VirtualMidi;\n [ModuleType.StepSequencer]: StepSequencer;\n [ModuleType.VoiceScheduler]: VoiceScheduler;\n};\n\nexport const moduleSchemas = {\n [ModuleType.Oscillator]: oscillatorPropSchema,\n [ModuleType.Gain]: gainPropSchema,\n [ModuleType.Master]: masterPropSchema,\n [ModuleType.MidiSelector]: midiSelectorPropSchema,\n [ModuleType.Envelope]: envelopePropSchema,\n [ModuleType.Filter]: filterPropSchema,\n [ModuleType.Scale]: scalePropSchema,\n [ModuleType.StereoPanner]: stereoPannerPropSchema,\n [ModuleType.Inspector]: inspectorPropSchema,\n [ModuleType.Constant]: constantPropSchema,\n [ModuleType.MidiMapper]: midiMapperPropSchema,\n [ModuleType.VirtualMidi]: virtualMidiPropSchema,\n [ModuleType.StepSequencer]: stepSequencerPropSchema,\n [ModuleType.VoiceScheduler]: voiceSchedulerPropSchema,\n};\n\nexport type { IOscillator } from \"./Oscillator\";\nexport { OscillatorWave } from \"./Oscillator\";\nexport type { IGain } from \"./Gain\";\nexport type { IMaster } from \"./Master\";\nexport type { IMidiSelector } from \"./MidiSelector\";\nexport type { IStereoPanner } from \"./StereoPanner\";\nexport type {\n IStepSequencer,\n IStepSequencerProps,\n ISequence,\n} from \"./StepSequencer\";\nexport type { IMidiMapper, IMidiMapperProps, MidiMapping } from \"./MidiMapper\";\nexport { MidiMappingMode } from \"./MidiMapper\";\n\nexport type AnyModule = Module<ModuleType>;\nexport type IAnyModule = IModule<ModuleType>;\n\nexport type ICreateModule<T extends ModuleType> = {\n id?: string;\n name: string;\n moduleType: T;\n props: Partial<ModuleTypeToPropsMapping[T]>;\n};\n\nexport type ModuleParams = {\n [K in ModuleType]: K extends\n | ModuleType.Oscillator\n | ModuleType.Gain\n | ModuleType.Envelope\n | ModuleType.Filter\n | ModuleType.StereoPanner\n | ModuleType.VoiceScheduler\n ? IPolyModuleConstructor<K>\n : ICreateModule<K>;\n}[ModuleType];\n\nexport function createModule(\n engineId: string,\n params: ModuleParams,\n): ModuleTypeToModuleMapping[keyof ModuleTypeToModuleMapping] {\n switch (params.moduleType) {\n case ModuleType.Oscillator:\n return new Oscillator(engineId, params);\n case ModuleType.Gain:\n return new Gain(engineId, params);\n case ModuleType.Master:\n return new Master(engineId, params);\n case ModuleType.MidiSelector:\n return new MidiSelector(engineId, params);\n case ModuleType.Envelope:\n return new Envelope(engineId, params);\n case ModuleType.Filter:\n return new Filter(engineId, params);\n case ModuleType.Scale:\n return new Scale(engineId, params);\n case ModuleType.StereoPanner:\n return new StereoPanner(engineId, params);\n case ModuleType.Inspector:\n return new Inspector(engineId, params);\n case ModuleType.Constant:\n return new Constant(engineId, params);\n case ModuleType.MidiMapper:\n return new MidiMapper(engineId, params);\n case ModuleType.VirtualMidi:\n return new VirtualMidi(engineId, params);\n case ModuleType.StepSequencer:\n return new StepSequencer(engineId, params);\n case ModuleType.VoiceScheduler:\n return new VoiceScheduler(engineId, params);\n default:\n assertNever(params);\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { EmptyObject } from \"@blibliki/utils\";\nimport { ICreateModule, ModuleType } from \"@/modules\";\nimport { MidiOutput } from \"../IO\";\nimport MidiEvent, { MidiEventType } from \"../midi/MidiEvent\";\nimport { ModulePropSchema } from \"../schema\";\nimport { IModuleConstructor, Module } from \"./Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"./PolyModule\";\n\nexport type IVoiceSchedulerProps = EmptyObject;\nexport const voiceSchedulerPropSchema: ModulePropSchema<IVoiceSchedulerProps> =\n {};\nconst DEFAULT_PROPS = {};\n\nclass Voice extends Module<ModuleType.VoiceScheduler> {\n declare audioNode: undefined;\n activeNote: string | null = null;\n triggeredAt: ContextTime = 0;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.VoiceScheduler>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n }\n\n midiTriggered = (midiEvent: MidiEvent) => {\n const { triggeredAt, note, type } = midiEvent;\n\n if (!note) return;\n const noteName = note.fullName;\n\n switch (type) {\n case MidiEventType.noteOn:\n this.activeNote = noteName;\n this.triggeredAt = triggeredAt;\n\n break;\n case MidiEventType.noteOff:\n this.activeNote = null;\n break;\n default:\n throw Error(\"This type is not a note\");\n }\n };\n}\n\nexport default class VoiceScheduler extends PolyModule<ModuleType.VoiceScheduler> {\n declare audioModules: Voice[];\n midiOutput!: MidiOutput;\n\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.VoiceScheduler>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.VoiceScheduler>,\n ) => new Voice(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerInputs();\n this.registerOutputs();\n }\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n let voice: Voice | undefined;\n\n switch (midiEvent.type) {\n case MidiEventType.noteOn:\n voice = this.findFreeVoice();\n\n break;\n case MidiEventType.noteOff:\n voice = this.audioModules.find(\n (v) => v.activeNote === midiEvent.note!.fullName,\n );\n break;\n default:\n throw Error(\"This type is not a note\");\n }\n\n if (!voice) return;\n\n voice.midiTriggered(midiEvent);\n midiEvent.voiceNo = voice.voiceNo;\n this.midiOutput.onMidiEvent(midiEvent);\n };\n\n private findFreeVoice(): Voice {\n let voice = this.audioModules.find((v) => !v.activeNote);\n\n // If no available voice, get the one with the lowest triggeredAt\n if (!voice) {\n const sorted = this.audioModules.sort((a, b) => {\n return a.triggeredAt - b.triggeredAt;\n });\n voice = sorted[0];\n if (!voice) {\n throw new Error(\"No voices available in voice scheduler\");\n }\n }\n\n return voice;\n }\n\n private registerInputs() {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n }\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi out\" });\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { ConstantSourceNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, ModulePropSchema, SetterHooks } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IConstant = IModule<ModuleType.Constant>;\nexport type IConstantProps = {\n value: number;\n};\n\nexport const constantPropSchema: ModulePropSchema<IConstantProps> = {\n value: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Value\",\n },\n};\n\nconst DEFAULT_PROPS: IConstantProps = { value: 1 };\n\nexport default class Constant\n extends Module<ModuleType.Constant>\n implements Pick<SetterHooks<IConstantProps>, \"onAfterSetValue\">\n{\n declare audioNode: ConstantSourceNode;\n isStated = false;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Constant>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new ConstantSourceNode(context.audioContext);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs(\"out\");\n }\n\n onAfterSetValue: SetterHooks<IConstantProps>[\"onAfterSetValue\"] = (value) => {\n this.audioNode.offset.value = value;\n };\n\n start(time: ContextTime) {\n if (this.isStated) return;\n\n this.isStated = true;\n this.audioNode.start(time);\n }\n\n stop(time: ContextTime) {\n if (!this.isStated) return;\n\n this.audioNode.stop(time);\n this.rePlugAll(() => {\n this.audioNode = new ConstantSourceNode(this.context.audioContext, {\n offset: this.props.value,\n });\n });\n\n this.isStated = false;\n }\n\n triggerAttack = (note: Note, triggeredAt: ContextTime) => {\n this.audioNode.offset.setValueAtTime(note.frequency, triggeredAt);\n this.start(triggeredAt);\n };\n\n triggerRelease = () => {\n // Do nothing\n };\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context, cancelAndHoldAtTime } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { Module } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { IModuleConstructor } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IEnvelopeProps = {\n attack: number;\n decay: number;\n sustain: number;\n release: number;\n};\n\nconst DEFAULT_PROPS: IEnvelopeProps = {\n attack: 0.01,\n decay: 0,\n sustain: 1,\n release: 0,\n};\n\nexport const envelopePropSchema: ModulePropSchema<IEnvelopeProps> = {\n attack: {\n kind: \"number\",\n min: 0.0001,\n max: 20,\n step: 0.01,\n exp: 3,\n label: \"Attack\",\n },\n decay: {\n kind: \"number\",\n min: 0,\n max: 20,\n step: 0.01,\n exp: 3,\n label: \"Decay\",\n },\n sustain: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Sustain\",\n },\n release: {\n kind: \"number\",\n min: 0,\n max: 20,\n step: 0.01,\n exp: 3,\n label: \"Release\",\n },\n};\n\nclass MonoEnvelope extends Module<ModuleType.Envelope> {\n declare audioNode: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Envelope>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) => {\n const audioNode = new GainNode(context.audioContext);\n audioNode.gain.value = 0;\n return audioNode;\n };\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs();\n }\n\n triggerAttack(note: Note, triggeredAt: ContextTime) {\n super.triggerAttack(note, triggeredAt);\n\n const attack = this.props.attack;\n const decay = this.props.decay;\n const sustain = this.props.sustain;\n\n cancelAndHoldAtTime(this.audioNode.gain, triggeredAt);\n\n // Always start from a tiny value, can't ramp from 0\n if (this.audioNode.gain.value === 0) {\n this.audioNode.gain.setValueAtTime(0.001, triggeredAt);\n }\n\n // Attack\n this.audioNode.gain.exponentialRampToValueAtTime(1.0, triggeredAt + attack);\n\n // Decay\n if (sustain > 0) {\n this.audioNode.gain.exponentialRampToValueAtTime(\n sustain,\n triggeredAt + attack + decay,\n );\n // Do not set to zero or anything else!\n } else {\n this.audioNode.gain.exponentialRampToValueAtTime(\n 0.001,\n triggeredAt + attack + decay,\n );\n }\n }\n\n triggerRelease(note: Note, triggeredAt: ContextTime) {\n super.triggerRelease(note, triggeredAt);\n if (this.activeNotes.length > 0) return;\n\n const release = this.props.release;\n\n // Cancel scheduled automations and set gain to the ACTUAL value at this moment\n const currentGainValue = cancelAndHoldAtTime(\n this.audioNode.gain,\n triggeredAt,\n );\n\n if (currentGainValue >= 0.0001) {\n // Always set the value at the release time to ensure a smooth ramp from here\n this.audioNode.gain.setValueAtTime(currentGainValue, triggeredAt);\n // Exponential ramp to a tiny value\n this.audioNode.gain.exponentialRampToValueAtTime(\n 0.0001,\n triggeredAt + release - 0.0001,\n );\n }\n\n // Set to zero at the very end\n this.audioNode.gain.setValueAtTime(0, triggeredAt + release);\n }\n}\n\nexport default class Envelope extends PolyModule<ModuleType.Envelope> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Envelope>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Envelope>,\n ) => new MonoEnvelope(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerDefaultIOs();\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { BiquadFilterNode } from \"@blibliki/utils/web-audio-api\";\nimport { EnumProp, ModulePropSchema } from \"@/core\";\nimport { IModuleConstructor, Module, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { createModule, ICreateModule, ModuleType } from \".\";\nimport { MonoGain } from \"./Gain\";\nimport Scale from \"./Scale\";\n\nexport type IFilterProps = {\n cutoff: number;\n envelopeAmount: number;\n type: BiquadFilterType;\n Q: number;\n};\n\nconst MIN_FREQ = 20;\nconst MAX_FREQ = 20000;\n\nconst DEFAULT_PROPS: IFilterProps = {\n cutoff: MAX_FREQ,\n envelopeAmount: 0,\n type: \"lowpass\",\n Q: 1,\n};\n\nexport const filterPropSchema: ModulePropSchema<\n IFilterProps,\n {\n type: EnumProp<BiquadFilterType>;\n }\n> = {\n cutoff: {\n kind: \"number\",\n min: MIN_FREQ,\n max: MAX_FREQ,\n step: 1,\n exp: 5,\n label: \"Cutoff\",\n },\n envelopeAmount: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Envelope Amount\",\n },\n type: {\n kind: \"enum\",\n options: [\"lowpass\", \"highpass\", \"bandpass\"] satisfies BiquadFilterType[],\n label: \"Type\",\n },\n Q: {\n kind: \"number\",\n min: 0.0001,\n max: 1000,\n step: 0.1,\n exp: 5,\n label: \"Q\",\n },\n};\n\nclass MonoFilter\n extends Module<ModuleType.Filter>\n implements\n Pick<\n SetterHooks<IFilterProps>,\n | \"onAfterSetType\"\n | \"onAfterSetCutoff\"\n | \"onAfterSetQ\"\n | \"onAfterSetEnvelopeAmount\"\n >\n{\n declare audioNode: BiquadFilterNode;\n private scale: Scale;\n private amount: MonoGain;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Filter>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n const audioNodeConstructor = (context: Context) =>\n new BiquadFilterNode(context.audioContext, {\n type: props.type,\n frequency: 0,\n Q: props.Q,\n });\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.amount = new MonoGain(engineId, {\n name: \"amount\",\n moduleType: ModuleType.Gain,\n props: { gain: props.envelopeAmount },\n });\n\n this.scale = createModule(engineId, {\n name: \"scale\",\n moduleType: ModuleType.Scale,\n props: { min: MIN_FREQ, max: MAX_FREQ, current: this.props.cutoff },\n }) as Scale;\n\n this.amount.plug({ audioModule: this.scale, from: \"out\", to: \"in\" });\n this.scale.audioNode.connect(this.audioNode.frequency);\n\n this.registerDefaultIOs();\n this.registerInputs();\n }\n\n onAfterSetType: SetterHooks<IFilterProps>[\"onAfterSetType\"] = (value) => {\n this.audioNode.type = value;\n };\n\n onAfterSetCutoff: SetterHooks<IFilterProps>[\"onAfterSetCutoff\"] = (value) => {\n this.scale.props = { current: value };\n };\n\n onAfterSetQ: SetterHooks<IFilterProps>[\"onAfterSetQ\"] = (value) => {\n this.audioNode.Q.value = value;\n };\n\n onAfterSetEnvelopeAmount: SetterHooks<IFilterProps>[\"onAfterSetEnvelopeAmount\"] =\n (value) => {\n this.amount.props = { gain: value };\n };\n\n private registerInputs() {\n this.registerAudioInput({\n name: \"cutoff\",\n getAudioNode: () => this.audioNode.frequency,\n });\n\n this.registerAudioInput({\n name: \"cutoffMod\",\n getAudioNode: () => this.amount.audioNode,\n });\n\n this.registerAudioInput({\n name: \"Q\",\n getAudioNode: () => this.audioNode.Q,\n });\n }\n}\n\nexport default class Filter extends PolyModule<ModuleType.Filter> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Filter>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Filter>,\n ) => new MonoFilter(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerInputs();\n this.registerDefaultIOs();\n }\n\n private registerInputs() {\n this.registerAudioInput({ name: \"cutoff\" });\n this.registerAudioInput({ name: \"cutoffMod\" });\n this.registerAudioInput({ name: \"Q\" });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, ModulePropSchema, SetterHooks } from \"@/core\";\nimport { IModuleConstructor } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IGain = IModule<ModuleType.Gain>;\nexport type IGainProps = {\n gain: number;\n};\n\nexport const gainPropSchema: ModulePropSchema<IGainProps> = {\n gain: {\n kind: \"number\",\n min: 0,\n max: Infinity,\n step: 0.01,\n label: \"Gain\",\n },\n};\n\nconst DEFAULT_PROPS: IGainProps = { gain: 1 };\n\nexport class MonoGain\n extends Module<ModuleType.Gain>\n implements Pick<SetterHooks<IGainProps>, \"onAfterSetGain\">\n{\n declare audioNode: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Gain>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new GainNode(context.audioContext);\n\n super(engineId, {\n ...params,\n audioNodeConstructor,\n props,\n });\n\n this.registerDefaultIOs();\n this.registerAdditionalInputs();\n }\n\n onAfterSetGain: SetterHooks<IGainProps>[\"onAfterSetGain\"] = (value) => {\n this.audioNode.gain.value = value;\n };\n\n private registerAdditionalInputs() {\n this.registerAudioInput({\n name: \"gain\",\n getAudioNode: () => this.audioNode.gain,\n });\n }\n}\n\nexport default class Gain extends PolyModule<ModuleType.Gain> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Gain>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Gain>,\n ) => new MonoGain(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerAdditionalInputs();\n this.registerDefaultIOs();\n }\n\n private registerAdditionalInputs() {\n this.registerAudioInput({ name: \"gain\" });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { AnalyserNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, SetterHooks } from \"@/core\";\nimport { EnumProp, ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IInspector = IModule<ModuleType.Inspector>;\nexport type IInspectorProps = {\n fftSize: number;\n};\n\nexport const inspectorPropSchema: ModulePropSchema<\n IInspectorProps,\n {\n fftSize: EnumProp<number>;\n }\n> = {\n fftSize: {\n kind: \"enum\",\n options: [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768],\n label: \"FFT size\",\n },\n};\n\nconst DEFAULT_PROPS: IInspectorProps = { fftSize: 512 };\n\nexport default class Inspector\n extends Module<ModuleType.Inspector>\n implements Pick<SetterHooks<IInspectorProps>, \"onAfterSetFftSize\">\n{\n declare audioNode: AnalyserNode;\n private _buffer?: Float32Array<ArrayBuffer>;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Inspector>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new AnalyserNode(context.audioContext);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs(\"in\");\n }\n\n onAfterSetFftSize: SetterHooks<IInspectorProps>[\"onAfterSetFftSize\"] = (\n value,\n ) => {\n this._buffer = new Float32Array(value);\n };\n\n get buffer() {\n if (this._buffer) return this._buffer;\n\n this._buffer = new Float32Array(this.props.fftSize);\n\n return this._buffer;\n }\n\n getValue(): number {\n const value = this.getValues()[0];\n return value ?? 0;\n }\n\n getValues(): Float32Array {\n this.audioNode.getFloatTimeDomainData(this.buffer);\n\n return this.buffer;\n }\n}\n","import { Context, EmptyObject } from \"@blibliki/utils\";\nimport { IModule, Module, ModulePropSchema } from \"@/core\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IMaster = IModule<ModuleType.Master>;\nexport type IMasterProps = EmptyObject;\n\nconst DEFAULT_PROPS: IMasterProps = {};\n\nexport const masterPropSchema: ModulePropSchema<IMasterProps> = {};\n\nexport default class Master extends Module<ModuleType.Master> {\n declare audioNode: AudioDestinationNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Master>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) => context.destination;\n\n super(engineId, { ...params, audioNodeConstructor, props });\n\n this.registerDefaultIOs(\"in\");\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { IModule, MidiEvent, Module, SetterHooks } from \"@/core\";\nimport { ModulePropSchema, NumberProp, PropSchema } from \"@/core/schema\";\nimport { ICreateModule, moduleSchemas, ModuleType } from \".\";\n\nexport type IMidiMapper = IModule<ModuleType.MidiMapper>;\nexport type IMidiMapperProps = {\n pages: MidiMappingPage[];\n activePage: number;\n globalMappings: MidiMapping<ModuleType>[];\n};\n\nexport type MidiMappingPage = {\n name?: string;\n mappings: MidiMapping<ModuleType>[];\n};\n\nexport enum MidiMappingMode {\n direct = \"direct\",\n directRev = \"directRev\",\n toggleInc = \"toggleInc\",\n toggleDec = \"toggleDec\",\n incDec = \"incDec\",\n incDecRev = \"incDecRev\",\n}\n\nexport type MidiMapping<T extends ModuleType> = {\n cc?: number;\n moduleId?: string;\n moduleType?: T;\n propName?: string;\n autoAssign?: boolean;\n mode?: MidiMappingMode;\n threshold?: number; // For incDec mode (default: 64)\n step?: number;\n};\n\nexport const midiMapperPropSchema: ModulePropSchema<IMidiMapperProps> = {\n pages: {\n kind: \"array\",\n label: \"Midi mapping pages\",\n },\n activePage: {\n kind: \"number\",\n label: \"Active page\",\n min: 0,\n max: 100,\n step: 1,\n },\n globalMappings: {\n kind: \"array\",\n label: \"Global midi mappings\",\n },\n};\n\nconst DEFAULT_PROPS: IMidiMapperProps = {\n pages: [{ name: \"Page 1\", mappings: [{}] }],\n activePage: 0,\n globalMappings: [{}],\n};\n\nfunction getMidiFromMappedValue({\n value,\n midiValue,\n propSchema,\n mapping,\n}: {\n value: number;\n propSchema: NumberProp;\n midiValue: number;\n mapping: MidiMapping<ModuleType>;\n}): number {\n const min = propSchema.min ?? 0;\n const max = propSchema.max ?? 1;\n const exp = propSchema.exp ?? 1;\n\n const { threshold = 64, mode } = mapping;\n\n // Reverse the range mapping: get curvedValue\n const curvedValue = (value - min) / (max - min);\n\n // Reverse the exponential curve: get normalizedMidi\n const normalizedMidi = Math.pow(curvedValue, 1 / exp);\n\n // Reverse the MIDI normalization: get midiValue\n let newMidiValue = normalizedMidi * 127;\n newMidiValue =\n (midiValue >= threshold && mode === MidiMappingMode.incDec) ||\n (midiValue <= threshold && mode === MidiMappingMode.incDecRev)\n ? newMidiValue + 1\n : newMidiValue - 1;\n return Math.round(Math.max(0, Math.min(127, newMidiValue))); // Valid MIDI range\n}\n\ntype MidiMapperSetterHooks = Pick<\n SetterHooks<IMidiMapperProps>,\n \"onSetActivePage\"\n>;\n\nexport default class MidiMapper\n extends Module<ModuleType.MidiMapper>\n implements MidiMapperSetterHooks\n{\n declare audioNode: undefined;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.MidiMapper>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n }\n\n onSetActivePage: MidiMapperSetterHooks[\"onSetActivePage\"] = (value) => {\n return Math.max(Math.min(value, this.props.pages.length - 1), 0);\n };\n\n handleCC = (event: MidiEvent, triggeredAt: ContextTime) => {\n this.checkAutoAssign(event);\n\n const activePage = this.props.pages[this.props.activePage];\n if (!activePage) return;\n\n [\n ...this.props.globalMappings.filter((m) => m.cc === event.cc),\n ...activePage.mappings.filter((m) => m.cc === event.cc),\n ].forEach((mapping) => {\n this.forwardMapping(event, mapping, triggeredAt);\n });\n };\n\n forwardMapping = (\n event: MidiEvent,\n mapping: MidiMapping<ModuleType>,\n _triggeredAt: ContextTime,\n ) => {\n if (\n mapping.moduleId === undefined ||\n mapping.moduleType === undefined ||\n mapping.propName === undefined\n )\n return;\n\n const propName = mapping.propName;\n let midiValue = event.ccValue;\n if (midiValue === undefined) return;\n\n const mode = mapping.mode ?? \"direct\";\n\n // Toggle mode: only respond to 127 (button press), ignore 0\n if (\n (mode === MidiMappingMode.toggleInc ||\n mode === MidiMappingMode.toggleDec) &&\n midiValue !== 127\n ) {\n return;\n }\n\n const mappedModule = this.engine.findModule(mapping.moduleId);\n // @ts-expect-error TS7053 ignore this error\n const propSchema = moduleSchemas[mappedModule.moduleType][\n propName\n ] as PropSchema;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let mappedValue: any;\n\n // Direct mode (default) or Toggle mode: map value directly\n switch (propSchema.kind) {\n case \"number\": {\n // @ts-expect-error TS7053 ignore this error\n const currentValue = mappedModule.props[propName] as number;\n\n if (\n mode === MidiMappingMode.incDec ||\n mode === MidiMappingMode.incDecRev\n ) {\n midiValue = getMidiFromMappedValue({\n value: currentValue,\n propSchema,\n mapping,\n midiValue,\n });\n } else if (mode === MidiMappingMode.directRev) {\n midiValue = 127 - midiValue;\n }\n\n if (mode === MidiMappingMode.toggleInc) {\n mappedValue = currentValue + (propSchema.step ?? 1);\n } else if (mode === MidiMappingMode.toggleDec) {\n mappedValue = currentValue - (propSchema.step ?? 1);\n } else {\n const min = propSchema.min ?? 0;\n const max = propSchema.max ?? 1;\n const normalizedMidi = midiValue / 127;\n const curvedValue = Math.pow(normalizedMidi, propSchema.exp ?? 1);\n mappedValue = min + curvedValue * (max - min);\n\n // Round to step if defined\n if (\n propSchema.step !== undefined &&\n (!propSchema.exp || propSchema.exp === 1)\n ) {\n const steps = Math.round((mappedValue - min) / propSchema.step);\n mappedValue = min + steps * propSchema.step;\n }\n }\n\n break;\n }\n case \"enum\": {\n const optionIndex = Math.floor(\n (midiValue / 127) * propSchema.options.length,\n );\n const clampedIndex = Math.min(\n optionIndex,\n propSchema.options.length - 1,\n );\n mappedValue = propSchema.options[clampedIndex];\n break;\n }\n case \"boolean\":\n mappedValue = midiValue >= 64;\n break;\n case \"string\":\n throw Error(\"MidiMapper not support string type of values\");\n case \"array\":\n throw Error(\"MidiMapper not support array type of values\");\n\n default:\n throw Error(\"MidiMapper unknown type\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n mappedModule.props = { [propName]: mappedValue };\n mappedModule.triggerPropsUpdate();\n };\n\n private checkAutoAssign(event: MidiEvent) {\n if (event.cc === undefined) return;\n\n const activePage = this.props.pages[this.props.activePage];\n if (!activePage) return;\n\n const hasGlobalAutoAssign = this.props.globalMappings.some(\n ({ autoAssign }) => autoAssign,\n );\n const hasPageAutoAssign = activePage.mappings.some(\n ({ autoAssign }) => autoAssign,\n );\n\n if (!hasGlobalAutoAssign && !hasPageAutoAssign) return;\n\n // Update global mappings if needed\n const updatedGlobalMappings = hasGlobalAutoAssign\n ? this.props.globalMappings.map((mapping) => {\n if (!mapping.autoAssign) return mapping;\n\n return {\n ...mapping,\n cc: event.cc,\n autoAssign: false,\n };\n })\n : this.props.globalMappings;\n\n // Update page mappings if needed\n const updatedPageMappings = hasPageAutoAssign\n ? activePage.mappings.map((mapping) => {\n if (!mapping.autoAssign) return mapping;\n\n return {\n ...mapping,\n cc: event.cc,\n autoAssign: false,\n };\n })\n : activePage.mappings;\n\n const updatedPages = this.props.pages.map((page, index) =>\n index === this.props.activePage\n ? { ...page, mappings: updatedPageMappings }\n : page,\n );\n\n this.props = { pages: updatedPages, globalMappings: updatedGlobalMappings };\n this.triggerPropsUpdate();\n }\n}\n","import { IModule, Module, MidiOutput, SetterHooks, MidiDevice } from \"@/core\";\nimport ComputerKeyboardInput from \"@/core/midi/ComputerKeyboardDevice\";\nimport MidiEvent from \"@/core/midi/MidiEvent\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IMidiSelector = IModule<ModuleType.MidiSelector>;\nexport type IMidiSelectorProps = {\n selectedId: string | undefined | null;\n selectedName: string | undefined | null;\n};\n\nexport const midiSelectorPropSchema: ModulePropSchema<IMidiSelectorProps> = {\n selectedId: {\n kind: \"string\",\n label: \"Midi device ID\",\n },\n selectedName: {\n kind: \"string\",\n label: \"Midi device name\",\n },\n};\n\nconst DEFAULT_PROPS: IMidiSelectorProps = {\n selectedId: undefined,\n selectedName: undefined,\n};\n\nexport default class MidiSelector\n extends Module<ModuleType.MidiSelector>\n implements Pick<SetterHooks<IMidiSelectorProps>, \"onSetSelectedId\">\n{\n declare audioNode: undefined;\n midiOutput!: MidiOutput;\n _forwardMidiEvent?: (midiEvent: MidiEvent) => void;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.MidiSelector>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n // Try to find device in order of preference:\n // 1. By exact ID match\n // 2. By exact name match\n // 3. By fuzzy name match (for cross-platform compatibility)\n let midiDevice =\n this.props.selectedId &&\n this.engine.findMidiDevice(this.props.selectedId);\n\n if (!midiDevice && this.props.selectedName) {\n midiDevice = this.engine.findMidiDeviceByName(this.props.selectedName);\n\n // If exact name match fails, try fuzzy matching\n if (!midiDevice) {\n const fuzzyMatch = this.engine.findMidiDeviceByFuzzyName(\n this.props.selectedName,\n 0.6, // 60% similarity threshold\n );\n\n if (fuzzyMatch) {\n midiDevice = fuzzyMatch.device;\n console.log(\n `MIDI device fuzzy matched: \"${this.props.selectedName}\" -> \"${midiDevice.name}\" (confidence: ${Math.round(fuzzyMatch.score * 100)}%)`,\n );\n }\n }\n }\n\n if (midiDevice) {\n this.addEventListener(midiDevice);\n }\n\n this.registerOutputs();\n }\n\n onSetSelectedId: SetterHooks<IMidiSelectorProps>[\"onSetSelectedId\"] = (\n value,\n ) => {\n this.removeEventListener();\n if (!value) return value;\n\n const midiDevice = this.engine.findMidiDevice(value);\n if (!midiDevice) return value;\n\n if (this.props.selectedName !== midiDevice.name) {\n this.props = { selectedName: midiDevice.name };\n this.triggerPropsUpdate();\n }\n this.addEventListener(midiDevice);\n\n return value;\n };\n\n private get forwardMidiEvent() {\n if (this._forwardMidiEvent) return this._forwardMidiEvent;\n\n this._forwardMidiEvent = (midiEvent: MidiEvent) => {\n this.midiOutput.onMidiEvent(midiEvent);\n };\n\n return this._forwardMidiEvent;\n }\n\n private addEventListener(midiDevice: MidiDevice | ComputerKeyboardInput) {\n midiDevice.addEventListener(this.forwardMidiEvent);\n }\n\n private removeEventListener() {\n if (!this.props.selectedId) return;\n\n const midiDevice = this.engine.findMidiDevice(this.props.selectedId);\n midiDevice?.removeEventListener(this.forwardMidiEvent);\n }\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi out\" });\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context, dbToGain } from \"@blibliki/utils\";\nimport { GainNode, OscillatorNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { IModuleConstructor, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { EnumProp, ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nconst LOW_GAIN = -18;\n\nexport type IOscillator = IModule<ModuleType.Oscillator>;\n\nexport enum OscillatorWave {\n sine = \"sine\",\n triangle = \"triangle\",\n square = \"square\",\n sawtooth = \"sawtooth\",\n}\n\n/**\n * Props for the Oscillator module.\n *\n * @property wave - Waveform shape of the oscillator.\n * One of: \"sine\", \"square\", \"sawtooth\", \"triangle\", or \"custom\".\n * @property frequency - Base frequency in Hz (e.g. 440 for A4).\n * @property fine - Fine tuning factor in the range [-1, 1], where ±1 represents ±1 semitone.\n * @property coarse - Coarse tuning factor in the range [-1, 1], scaled to ±12 semitones.\n * @property octave - Octave transposition value (e.g. +1 for one octave up, -2 for two octaves down).\n * @property lowGain - Whether to gain reduction (-18dB). When false, oscillator runs at full gain.\n */\nexport type IOscillatorProps = {\n wave: OscillatorWave;\n frequency: number;\n fine: number;\n coarse: number;\n octave: number;\n lowGain: boolean;\n};\n\nexport const oscillatorPropSchema: ModulePropSchema<\n IOscillatorProps,\n {\n wave: EnumProp<OscillatorWave>;\n }\n> = {\n wave: {\n kind: \"enum\",\n options: Object.values(OscillatorWave),\n label: \"Waveform\",\n },\n frequency: {\n kind: \"number\",\n min: 0,\n max: 25000,\n step: 1,\n label: \"Frequency\",\n },\n fine: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Fine\",\n },\n coarse: {\n kind: \"number\",\n min: -12,\n max: 12,\n step: 1,\n label: \"Coarse\",\n },\n octave: {\n kind: \"number\",\n min: -1,\n max: 2,\n step: 1,\n label: \"Octave\",\n },\n lowGain: {\n kind: \"boolean\",\n label: `Use ${LOW_GAIN}db Gain`,\n },\n};\n\nconst DEFAULT_PROPS: IOscillatorProps = {\n wave: OscillatorWave.sine,\n frequency: 440,\n fine: 0,\n coarse: 0,\n octave: 0,\n lowGain: false,\n};\n\ntype OscillatorSetterHooks = Pick<\n SetterHooks<IOscillatorProps>,\n | \"onAfterSetWave\"\n | \"onAfterSetFrequency\"\n | \"onAfterSetFine\"\n | \"onAfterSetCoarse\"\n | \"onAfterSetOctave\"\n | \"onAfterSetLowGain\"\n>;\n\nexport class MonoOscillator\n extends Module<ModuleType.Oscillator>\n implements OscillatorSetterHooks\n{\n declare audioNode: OscillatorNode;\n isStated = false;\n outputGain: GainNode;\n detuneGain!: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Oscillator>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new OscillatorNode(context.audioContext);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.outputGain = new GainNode(this.context.audioContext, {\n gain: dbToGain(LOW_GAIN),\n });\n\n this.applyOutputGain();\n this.initializeGainDetune();\n this.registerInputs();\n this.registerOutputs();\n }\n\n onAfterSetWave: OscillatorSetterHooks[\"onAfterSetWave\"] = (value) => {\n this.audioNode.type = value;\n };\n\n onAfterSetFrequency: OscillatorSetterHooks[\"onAfterSetFrequency\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetFine: OscillatorSetterHooks[\"onAfterSetFine\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetCoarse: OscillatorSetterHooks[\"onAfterSetCoarse\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetOctave: OscillatorSetterHooks[\"onAfterSetOctave\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetLowGain: OscillatorSetterHooks[\"onAfterSetLowGain\"] = (lowGain) => {\n this.outputGain.gain.value = lowGain ? dbToGain(LOW_GAIN) : 1;\n };\n\n start(time: ContextTime) {\n if (this.isStated) return;\n\n this.isStated = true;\n this.audioNode.start(time);\n }\n\n stop(time: ContextTime) {\n if (!this.isStated) return;\n\n this.audioNode.stop(time);\n this.rePlugAll(() => {\n this.audioNode = new OscillatorNode(this.context.audioContext, {\n type: this.props.wave,\n frequency: this.finalFrequency,\n });\n this.applyOutputGain();\n this.detuneGain.connect(this.audioNode.detune);\n });\n\n this.isStated = false;\n }\n\n triggerAttack = (note: Note, triggeredAt: ContextTime) => {\n super.triggerAttack(note, triggeredAt);\n\n this.props = { frequency: note.frequency };\n this.updateFrequency(triggeredAt);\n this.start(triggeredAt);\n };\n\n triggerRelease(note: Note, triggeredAt: ContextTime) {\n super.triggerRelease(note, triggeredAt);\n\n const lastNote = this.activeNotes.length\n ? this.activeNotes[this.activeNotes.length - 1]\n : null;\n if (!lastNote) return;\n\n this.props = { frequency: lastNote.frequency };\n this.updateFrequency(triggeredAt);\n }\n\n private get finalFrequency(): number | undefined {\n const { frequency, coarse, octave, fine } = this.props;\n\n const transposed =\n frequency * Math.pow(2, coarse / 12 + octave + fine / 12);\n return transposed;\n }\n\n private updateFrequency(actionAt?: ContextTime) {\n if (this.finalFrequency === undefined) return;\n\n if (actionAt) {\n this.audioNode.frequency.setValueAtTime(this.finalFrequency, actionAt);\n } else {\n this.audioNode.frequency.value = this.finalFrequency;\n }\n }\n\n private applyOutputGain() {\n this.audioNode.connect(this.outputGain);\n }\n\n private initializeGainDetune() {\n this.detuneGain = new GainNode(this.context.audioContext, { gain: 100 });\n this.detuneGain.connect(this.audioNode.detune);\n }\n\n private registerInputs() {\n this.registerAudioInput({\n name: \"detune\",\n getAudioNode: () => this.detuneGain,\n });\n }\n\n private registerOutputs() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.outputGain,\n });\n }\n}\n\nexport default class Oscillator extends PolyModule<ModuleType.Oscillator> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Oscillator>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Oscillator>,\n ) => new MonoOscillator(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerInputs();\n this.registerDefaultIOs(\"out\");\n }\n\n start(time: ContextTime) {\n this.audioModules.forEach((audioModule) => {\n audioModule.start(time);\n });\n }\n\n stop(time: ContextTime) {\n this.audioModules.forEach((audioModule) => {\n audioModule.stop(time);\n });\n }\n\n private registerInputs() {\n this.registerAudioInput({ name: \"detune\" });\n }\n}\n","import { assertNever, Context } from \"@blibliki/utils\";\nimport { filterProcessorURL } from \"./filter-processor\";\nimport { scaleProcessorURL } from \"./scale-processor\";\n\nexport enum CustomWorklet {\n ScaleProcessor = \"ScaleProcessor\",\n FilterProcessor = \"FilterProcessor\",\n}\n\nexport async function loadProcessors(context: Context) {\n await context.addModule(scaleProcessorURL);\n await context.addModule(filterProcessorURL);\n}\n\nexport function newAudioWorklet(context: Context, worklet: CustomWorklet) {\n switch (worklet) {\n case CustomWorklet.ScaleProcessor:\n return context.newAudioWorklet(\"scale-processor\");\n case CustomWorklet.FilterProcessor:\n return context.newAudioWorklet(\"filter-processor\");\n default:\n assertNever(worklet);\n }\n}\n","export const filterProcessorURL = URL.createObjectURL(\n new Blob(\n [\n \"(\",\n (() => {\n class FilterProcessor extends AudioWorkletProcessor {\n s0: number;\n s1: number;\n\n constructor() {\n super();\n this.s0 = 0;\n this.s1 = 0;\n }\n\n static get parameterDescriptors() {\n return [\n {\n name: \"cutoff\",\n defaultValue: 1000,\n minValue: 20,\n maxValue: 20000,\n },\n {\n name: \"resonance\",\n defaultValue: 0.0,\n minValue: 0.0,\n maxValue: 4.0,\n },\n ];\n }\n\n process(\n inputs: Float32Array[][],\n outputs: Float32Array[][],\n parameters: Record<string, Float32Array>,\n ): boolean {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n\n const cutoff = parameters.cutoff;\n const resonance = parameters.resonance;\n if (!cutoff || !resonance) return true;\n\n for (let channelNum = 0; channelNum < input.length; channelNum++) {\n const inputChannel = input[channelNum];\n const outputChannel = output[channelNum];\n if (!inputChannel || !outputChannel) continue;\n\n for (let i = 0; i < inputChannel.length; i++) {\n const s = inputChannel[i];\n if (s === undefined) continue;\n\n // Convert Hz to normalized frequency using logarithmic scale\n // This better matches human hearing perception\n const cutoffHz =\n cutoff.length > 1 ? (cutoff[i] ?? cutoff[0]) : cutoff[0];\n if (cutoffHz === undefined) continue;\n\n const clampedHz = Math.max(20, Math.min(20000, cutoffHz));\n const normalizedCutoff =\n Math.log(clampedHz / 20) / Math.log(20000 / 20);\n const c = Math.pow(0.5, (1 - normalizedCutoff) / 0.125);\n\n const resonanceValue =\n resonance.length > 1\n ? (resonance[i] ?? resonance[0])\n : resonance[0];\n if (resonanceValue === undefined) continue;\n\n const r = Math.pow(0.5, (resonanceValue + 0.125) / 0.125);\n const mrc = 1 - r * c;\n\n this.s0 = mrc * this.s0 - c * this.s1 + c * s;\n this.s1 = mrc * this.s1 + c * this.s0;\n\n outputChannel[i] = this.s1;\n }\n }\n\n return true;\n }\n }\n\n registerProcessor(\"filter-processor\", FilterProcessor);\n }).toString(),\n \")()\",\n ],\n { type: \"application/javascript\" },\n ),\n);\n","export const scaleProcessorURL = URL.createObjectURL(\n new Blob(\n [\n \"(\",\n (() => {\n class ScaleProcessor extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"min\",\n defaultValue: 1e-10,\n },\n {\n name: \"max\",\n defaultValue: 1,\n },\n {\n name: \"current\",\n defaultValue: 0.5,\n },\n ];\n }\n\n process(\n inputs: Float32Array[][],\n outputs: Float32Array[][],\n parameters: Record<string, Float32Array>,\n ) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n\n const minValues = parameters.min;\n const maxValues = parameters.max;\n const currentValues = parameters.current;\n if (!minValues || !maxValues || !currentValues) return true;\n\n const firstInput = input[0];\n if (!firstInput || firstInput.length === 0) {\n for (const outputChannel of output) {\n const current =\n currentValues.length > 1\n ? (currentValues[0] ?? 0.5)\n : (currentValues[0] ?? 0.5);\n\n outputChannel.fill(current);\n }\n\n return true;\n }\n\n for (let channel = 0; channel < input.length; channel++) {\n const inputChannel = input[channel];\n const outputChannel = output[channel];\n if (!inputChannel || !outputChannel) continue;\n\n for (let i = 0; i < inputChannel.length; i++) {\n const x = inputChannel[i];\n if (x === undefined) continue;\n\n const min =\n minValues.length > 1\n ? (minValues[i] ?? minValues[0])\n : minValues[0];\n const max =\n maxValues.length > 1\n ? (maxValues[i] ?? maxValues[0])\n : maxValues[0];\n const current =\n currentValues.length > 1\n ? (currentValues[i] ?? currentValues[0])\n : currentValues[0];\n\n if (\n min === undefined ||\n max === undefined ||\n current === undefined\n )\n continue;\n\n if (x < 0) {\n outputChannel[i] = current * Math.pow(min / current, -x);\n } else {\n outputChannel[i] = current * Math.pow(max / current, x);\n }\n }\n }\n\n return true;\n }\n }\n\n registerProcessor(\"scale-processor\", ScaleProcessor);\n }).toString(),\n \")()\",\n ],\n { type: \"application/javascript\" },\n ),\n);\n","import { Context } from \"@blibliki/utils\";\nimport { IModule, Module, SetterHooks } from \"@/core\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { CustomWorklet, newAudioWorklet } from \"@/processors\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IScale = IModule<ModuleType.Scale>;\nexport type IScaleProps = {\n min: number;\n max: number;\n current: number;\n};\n\nexport const scalePropSchema: ModulePropSchema<IScaleProps> = {\n min: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Min\",\n },\n max: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Max\",\n },\n current: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Current\",\n },\n};\n\nconst DEFAULT_PROPS: IScaleProps = { min: 0, max: 1, current: 0.5 };\n\nexport default class Scale\n extends Module<ModuleType.Scale>\n implements\n Pick<\n SetterHooks<IScaleProps>,\n \"onAfterSetMin\" | \"onAfterSetMax\" | \"onAfterSetCurrent\"\n >\n{\n declare audioNode: AudioWorkletNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Scale>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n newAudioWorklet(context, CustomWorklet.ScaleProcessor);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs();\n }\n\n get current() {\n return this.audioNode.parameters.get(\"current\")!;\n }\n\n get min() {\n return this.audioNode.parameters.get(\"min\")!;\n }\n\n get max() {\n return this.audioNode.parameters.get(\"max\")!;\n }\n\n onAfterSetMin: SetterHooks<IScaleProps>[\"onAfterSetMin\"] = (value) => {\n this.min.value = value;\n };\n\n onAfterSetMax: SetterHooks<IScaleProps>[\"onAfterSetMax\"] = (value) => {\n this.max.value = value;\n };\n\n onAfterSetCurrent: SetterHooks<IScaleProps>[\"onAfterSetCurrent\"] = (\n value,\n ) => {\n this.current.value = value;\n };\n}\n","import { INote, Module, IModule, MidiOutput, ModulePropSchema } from \"@/core\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IStepSequencer = IModule<ModuleType.StepSequencer>;\n\nexport type ISequence = {\n active: boolean;\n // time: BarsBeatsSixteenths;\n duration: string;\n notes: INote[];\n};\n\nexport type IStepSequencerProps = {\n bars: number;\n steps: number;\n sequences: ISequence[][];\n};\n\nexport const stepSequencerPropSchema: ModulePropSchema<\n Omit<IStepSequencerProps, \"sequences\">\n> = {\n steps: {\n kind: \"number\",\n min: 1,\n max: 16,\n step: 1,\n label: \"Steps\",\n },\n bars: {\n kind: \"number\",\n min: 1,\n max: 16,\n step: 1,\n label: \"Steps\",\n },\n};\n\nconst DEFAULT_PROPS: IStepSequencerProps = {\n sequences: [],\n steps: 16,\n bars: 1,\n};\n\n// Not implemented yet, just the data modeling\nexport default class StepSequencer extends Module<ModuleType.StepSequencer> {\n declare audioNode: undefined;\n midiOutput!: MidiOutput;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.StepSequencer>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { StereoPannerNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, ModulePropSchema } from \"@/core\";\nimport { IModuleConstructor, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IStereoPanner = IModule<ModuleType.StereoPanner>;\nexport type IStereoPannerProps = {\n pan: number;\n};\n\nexport const stereoPannerPropSchema: ModulePropSchema<IStereoPannerProps> = {\n pan: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Pan\",\n },\n};\n\nconst DEFAULT_PROPS: IStereoPannerProps = {\n pan: 0,\n};\n\nexport class MonoStereoPanner\n extends Module<ModuleType.StereoPanner>\n implements Pick<SetterHooks<IStereoPannerProps>, \"onAfterSetPan\">\n{\n declare audioNode: StereoPannerNode;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.StereoPanner>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new StereoPannerNode(context.audioContext);\n\n super(engineId, {\n ...params,\n audioNodeConstructor,\n props,\n });\n\n this.registerDefaultIOs();\n this.registerAdditionalInputs();\n }\n\n onAfterSetPan: SetterHooks<IStereoPannerProps>[\"onAfterSetPan\"] = (value) => {\n this.audioNode.pan.value = value;\n };\n\n private registerAdditionalInputs() {\n this.registerAudioInput({\n name: \"pan\",\n getAudioNode: () => this.audioNode.pan,\n });\n }\n}\n\nexport default class StereoPanner extends PolyModule<ModuleType.StereoPanner> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.StereoPanner>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.StereoPanner>,\n ) => new MonoStereoPanner(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerAdditionalInputs();\n this.registerDefaultIOs();\n }\n\n private registerAdditionalInputs() {\n this.registerAudioInput({ name: \"pan\" });\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Module, IModule, MidiOutput, Note, ModulePropSchema } from \"@/core\";\nimport MidiEvent from \"@/core/midi/MidiEvent\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IVirtualMidi = IModule<ModuleType.VirtualMidi>;\nexport type IVirtualMidiProps = {\n activeNotes: string[];\n};\n\nexport const virtualMidiPropSchema: ModulePropSchema<IVirtualMidiProps> = {\n activeNotes: {\n kind: \"array\",\n label: \"Active notes\",\n },\n};\n\nconst DEFAULT_PROPS: IVirtualMidiProps = { activeNotes: [] };\n\nexport default class VirtualMidi extends Module<ModuleType.VirtualMidi> {\n declare audioNode: undefined;\n midiOutput!: MidiOutput;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.VirtualMidi>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n this.registerInputs();\n this.registerOutputs();\n }\n\n sendMidi(midiEvent: MidiEvent) {\n this.midiOutput.onMidiEvent(midiEvent);\n }\n\n triggerAttack = (note: Note, triggerAttack: ContextTime) => {\n this.props = { activeNotes: [...this.props.activeNotes, note.fullName] };\n this.triggerPropsUpdate();\n this.sendMidi(MidiEvent.fromNote(note, true, triggerAttack));\n };\n\n triggerRelease = (note: Note, triggerAttack: ContextTime) => {\n this.props = {\n activeNotes: this.props.activeNotes.filter(\n (name) => name !== note.fullName,\n ),\n };\n this.triggerPropsUpdate();\n this.sendMidi(MidiEvent.fromNote(note, false, triggerAttack));\n };\n\n private registerInputs() {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n }\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi out\" });\n }\n}\n","export { Engine } from \"./Engine\";\nexport type { ICreateRoute, IUpdateModule, IEngineSerialize } from \"./Engine\";\n\nexport type {\n IRoute,\n IIOSerialize,\n IModule,\n IModuleSerialize,\n IPolyModuleSerialize,\n IAnyModuleSerialize,\n IMidiDevice,\n ModulePropSchema,\n PropSchema,\n StringProp,\n NumberProp,\n EnumProp,\n BooleanProp,\n ArrayProp,\n INote,\n} from \"./core\";\nexport { MidiDevice, MidiPortState, Note } from \"./core\";\n\nexport { TransportState } from \"@blibliki/transport\";\nexport type { TimeSignature, Position } from \"@blibliki/transport\";\n\nexport { Context } from \"@blibliki/utils\";\n\nexport {\n ModuleType,\n moduleSchemas,\n OscillatorWave,\n MidiMappingMode,\n} from \"./modules\";\nexport type {\n IOscillator,\n IGain,\n IMaster,\n ISequence,\n IStepSequencerProps,\n IStepSequencer,\n ModuleTypeToPropsMapping,\n ICreateModule,\n ModuleParams,\n IMidiMapper,\n IMidiMapperProps,\n MidiMapping,\n} from \"./modules\";\n"],"mappings":"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","names":["Transport","assertDefined","Context","pick","uuidv4","upperFirst","uuidv4","requestAnimationFrame","assertNever","sortBy","deterministicId","uuidv4","requestAnimationFrame","PolyModule","engineId","params","id","name","moduleType","voices","monoModuleConstructor","props","uuidv4","InputCollection","OutputCollection","value","m","time","audioModule","from","to","output","input","callback","midiEvent","voiceNo","requestAnimationFrame","moduleByVoice","deterministicId","Engine","AudioParam","deterministicId","Base","module","props","deterministicId","io","plugOther","currentIO","callback","connections","otherIO","IO","PolyAudioInput","IO","io","plugOther","PolyAudioOutput","plugOrUnplug","voice","thisIO","otherIO","isPlug","maxVoices","thisMonoIO","otherMonoIO","AudioInput","IO","module","props","AudioOutput","io","plugOther","PolyAudioInput","input","AudioParam","MidiInput","IO","module","props","MidiOutput","event","input","IOCollection","collectionType","module","props","io","PolyModule","AudioInput","AudioOutput","Module","PolyAudioInput","PolyAudioOutput","MidiInput","MidiOutput","assertNever","callback","id","name","sortBy","InputCollection","OutputCollection","frequencyTable","frequencyTable_default","Notes","MIDI_OCTAVE_SYTSTEM","Note","_Note","frequency","noteName","note","freq","frequencyTable_default","message","dataByte","name","octave","noteOn","statusByte","string","matches","props","Message","data","statusByte","MidiEvent","_MidiEvent","noteName","noteOn","triggeredAt","note","Note","Message","cc","value","message","voiceNo","newEvent","Module","engineId","params","id","name","moduleType","voiceNo","audioNodeConstructor","props","uuidv4","InputCollection","OutputCollection","value","updatedValue","key","propValue","result","hookType","hookName","upperFirst","hook","audioModule","from","to","output","input","callback","_time","note","_triggeredAt","n","_event","midiEvent","triggeredAt","requestAnimationFrame","Engine","uuidv4","Routes","engine","props","id","route","_","sourceIO","destinationIO","source","destination","MidiPortState","MidiDevice","input","context","id","name","state","callback","c","event","message","Message","midiEvent","MidiEvent","MAP_KEYS","Note","computerKeyboardData","ComputerKeyboardInput","context","id","name","state","callback","c","noteOn","event","note","midiEvent","MidiEvent","isNode","isNode","NodeMidiInputPort","portIndex","name","input","state","callback","_deltaTime","message","event","cb","err","NodeMidiAccess","MidiModule","portCount","i","portName","id","portInput","port","_event","_callback","NodeMidiAdapter","midi","midiModule","WebMidiInputPort","input","callback","e","event","cb","WebMidiAccess","midiAccess","port","WebMidiAdapter","err","createMidiAdapter","isNode","NodeMidiAdapter","WebMidiAdapter","normalizeDeviceName","name","normalized","parts","longest","current","extractCoreTokens","tokens","genericWords","token","levenshteinDistance","str1","str2","len1","len2","matrix","i","j","cost","prevRow","currRow","prevCell","diagCell","prevCellInRow","calculateSimilarity","name1","name2","normalized1","normalized2","tokens1","tokens2","maxLen","intersection","x","union","jaccardScore","stringScore","substringScore","findBestMatch","targetName","candidateNames","threshold","bestMatch","candidateName","score","MidiDeviceManager","createMidiAdapter","context","id","name","d","targetName","threshold","deviceEntries","candidateNames","match","findBestMatch","device","callback","input","MidiDevice","err","computerKeyboardDevice","ComputerKeyboardInput","port","listener","assertNever","voiceSchedulerPropSchema","DEFAULT_PROPS","Voice","Module","engineId","params","props","midiEvent","triggeredAt","note","type","noteName","VoiceScheduler","PolyModule","monoModuleConstructor","voice","v","a","b","ConstantSourceNode","constantPropSchema","DEFAULT_PROPS","Constant","Module","engineId","params","props","audioNodeConstructor","context","ConstantSourceNode","value","time","note","triggeredAt","cancelAndHoldAtTime","GainNode","DEFAULT_PROPS","envelopePropSchema","MonoEnvelope","Module","engineId","params","props","audioNodeConstructor","context","audioNode","GainNode","note","triggeredAt","attack","decay","sustain","cancelAndHoldAtTime","release","currentGainValue","Envelope","PolyModule","monoModuleConstructor","BiquadFilterNode","GainNode","gainPropSchema","DEFAULT_PROPS","MonoGain","Module","engineId","params","props","audioNodeConstructor","context","GainNode","value","Gain","PolyModule","monoModuleConstructor","MIN_FREQ","MAX_FREQ","DEFAULT_PROPS","filterPropSchema","MonoFilter","Module","engineId","params","props","audioNodeConstructor","context","BiquadFilterNode","MonoGain","createModule","value","Filter","PolyModule","monoModuleConstructor","AnalyserNode","inspectorPropSchema","DEFAULT_PROPS","Inspector","Module","engineId","params","props","audioNodeConstructor","context","AnalyserNode","value","DEFAULT_PROPS","masterPropSchema","Master","Module","engineId","params","props","audioNodeConstructor","context","MidiMappingMode","midiMapperPropSchema","DEFAULT_PROPS","getMidiFromMappedValue","value","midiValue","propSchema","mapping","min","max","exp","threshold","mode","curvedValue","newMidiValue","MidiMapper","Module","engineId","params","props","event","triggeredAt","activePage","m","_triggeredAt","propName","mappedModule","moduleSchemas","mappedValue","currentValue","normalizedMidi","steps","optionIndex","clampedIndex","hasGlobalAutoAssign","autoAssign","hasPageAutoAssign","updatedGlobalMappings","updatedPageMappings","updatedPages","page","index","midiSelectorPropSchema","DEFAULT_PROPS","MidiSelector","Module","engineId","params","props","midiDevice","fuzzyMatch","value","midiEvent","dbToGain","GainNode","OscillatorNode","LOW_GAIN","OscillatorWave","oscillatorPropSchema","DEFAULT_PROPS","MonoOscillator","Module","engineId","params","props","audioNodeConstructor","context","OscillatorNode","GainNode","dbToGain","value","lowGain","time","note","triggeredAt","lastNote","frequency","coarse","octave","fine","actionAt","Oscillator","PolyModule","monoModuleConstructor","audioModule","assertNever","filterProcessorURL","FilterProcessor","inputs","outputs","parameters","input","output","cutoff","resonance","channelNum","inputChannel","outputChannel","i","s","cutoffHz","clampedHz","normalizedCutoff","c","resonanceValue","mrc","scaleProcessorURL","ScaleProcessor","inputs","outputs","parameters","input","output","minValues","maxValues","currentValues","firstInput","outputChannel","current","channel","inputChannel","i","x","min","max","loadProcessors","context","scaleProcessorURL","filterProcessorURL","newAudioWorklet","worklet","assertNever","scalePropSchema","DEFAULT_PROPS","Scale","Module","engineId","params","props","audioNodeConstructor","context","newAudioWorklet","value","stepSequencerPropSchema","DEFAULT_PROPS","StepSequencer","Module","engineId","params","props","StereoPannerNode","stereoPannerPropSchema","DEFAULT_PROPS","MonoStereoPanner","Module","engineId","params","props","audioNodeConstructor","context","StereoPannerNode","value","StereoPanner","PolyModule","monoModuleConstructor","virtualMidiPropSchema","DEFAULT_PROPS","VirtualMidi","Module","engineId","params","props","midiEvent","note","triggerAttack","MidiEvent","name","ModuleType","moduleSchemas","oscillatorPropSchema","gainPropSchema","masterPropSchema","midiSelectorPropSchema","envelopePropSchema","filterPropSchema","scalePropSchema","stereoPannerPropSchema","inspectorPropSchema","constantPropSchema","midiMapperPropSchema","virtualMidiPropSchema","stepSequencerPropSchema","voiceSchedulerPropSchema","createModule","engineId","params","Oscillator","Gain","Master","MidiSelector","Envelope","Filter","Scale","StereoPanner","Inspector","Constant","MidiMapper","VirtualMidi","StepSequencer","VoiceScheduler","assertNever","Engine","_Engine","id","engine","assertDefined","data","bpm","timeSignature","modules","routes","context","Context","m","r","uuidv4","Transport","_start","_end","_event","_ticks","_actionAt","Routes","MidiDeviceManager","loadProcessors","params","module","createModule","updates","pick","PolyModule","props","source","destination","output","input","value","moduleId","ioName","type","name","threshold","callback","noteName","virtualMidi","MidiEvent","actionAt","TransportState","Context"]}
|
|
1
|
+
{"version":3,"sources":["../src/Engine.ts","../src/core/module/Module.ts","../src/core/IO/Collection.ts","../src/core/module/PolyModule.ts","../src/core/IO/AudioIO.ts","../src/core/IO/Base.ts","../src/core/IO/PolyAudioIO.ts","../src/core/IO/MidiIO.ts","../src/core/Note/frequencyTable.ts","../src/core/Note/index.ts","../src/core/midi/Message.ts","../src/core/midi/MidiEvent.ts","../src/core/Route.ts","../src/core/midi/BaseMidiDevice.ts","../src/core/midi/ComputerKeyboardDevice.ts","../src/core/midi/MidiInputDevice.ts","../src/core/midi/MidiOutputDevice.ts","../src/core/midi/adapters/index.ts","../src/core/midi/adapters/NodeMidiAdapter.ts","../src/core/midi/adapters/WebMidiAdapter.ts","../src/core/midi/deviceMatcher.ts","../src/core/midi/MidiDeviceManager.ts","../src/modules/index.ts","../src/core/module/VoiceScheduler.ts","../src/modules/Chorus.ts","../src/utils/WetDryMixer.ts","../src/modules/Constant.ts","../src/modules/Delay.ts","../src/modules/Distortion.ts","../src/modules/Envelope.ts","../src/processors/index.ts","../src/processors/custom-envelope-processor.ts","../src/processors/filter-processor.ts","../src/processors/lfo-processor.ts","../src/processors/scale-processor.ts","../src/modules/Filter.ts","../src/modules/Gain.ts","../src/modules/Scale.ts","../src/modules/Inspector.ts","../src/modules/LFO.ts","../src/modules/LegacyEnvelope.ts","../src/modules/Master.ts","../src/modules/MidiInput.ts","../src/modules/MidiMapper.ts","../src/modules/MidiOutput.ts","../src/modules/Noise.ts","../src/modules/Oscillator.ts","../src/modules/Reverb.ts","../src/modules/StepSequencer.ts","../src/modules/StereoPanner.ts","../src/modules/VirtualMidi.ts","../src/index.ts"],"sourcesContent":["import { BPM, Ticks, TimeSignature, Transport } from \"@blibliki/transport\";\nimport {\n assertDefined,\n Context,\n Optional,\n pick,\n uuidv4,\n} from \"@blibliki/utils\";\nimport {\n IRoute,\n Routes,\n MidiDeviceManager,\n IModule,\n MidiEvent,\n IModuleSerialize,\n} from \"@/core\";\nimport {\n ICreateModule,\n ModuleParams,\n ModuleType,\n ModuleTypeToModuleMapping,\n ModuleTypeToStateMapping,\n createModule,\n} from \"@/modules\";\nimport {\n IPolyModule,\n IPolyModuleSerialize,\n PolyModule,\n} from \"./core/module/PolyModule\";\nimport { loadProcessors } from \"./processors\";\n\nexport type IUpdateModule<T extends ModuleType> = {\n id: string;\n moduleType: T;\n changes: Partial<Omit<ICreateModule<T>, \"id\" | \"moduleType\" | \"voice\">> & {\n voices?: number;\n };\n};\n\nexport type ICreateRoute = Optional<IRoute, \"id\">;\n\nexport interface IEngineSerialize {\n bpm: BPM;\n timeSignature: TimeSignature;\n modules: (IModuleSerialize<ModuleType> | IPolyModuleSerialize<ModuleType>)[];\n routes: IRoute[];\n}\n\nexport class Engine {\n private static _engines = new Map<string, Engine>();\n private static _currentId: string | undefined;\n private propsUpdateCallbacks: (<T extends ModuleType>(\n params: IModule<T> | IPolyModule<T>,\n ) => void)[] = [];\n\n readonly id: string;\n context: Context;\n isInitialized = false;\n routes: Routes;\n transport: Transport;\n modules: Map<\n string,\n ModuleTypeToModuleMapping[keyof ModuleTypeToModuleMapping]\n >;\n\n midiDeviceManager: MidiDeviceManager;\n\n static getById(id: string): Engine {\n const engine = Engine._engines.get(id);\n assertDefined(engine);\n\n return engine;\n }\n\n static get current(): Engine {\n assertDefined(this._currentId);\n\n return this.getById(this._currentId);\n }\n\n static async load(data: IEngineSerialize): Promise<Engine> {\n const { bpm, timeSignature, modules, routes } = data;\n const context = new Context();\n const engine = new Engine(context);\n await engine.initialize();\n\n engine.timeSignature = timeSignature;\n engine.bpm = bpm;\n modules.forEach((m) => {\n engine.addModule(m);\n });\n routes.forEach((r) => {\n engine.addRoute(r);\n });\n\n return engine;\n }\n\n constructor(context: Context) {\n this.id = uuidv4();\n\n this.context = context;\n this.transport = new Transport(this.context, {\n onStart: this.onStart,\n onStop: this.onStop,\n });\n this.routes = new Routes(this);\n this.modules = new Map();\n this.midiDeviceManager = new MidiDeviceManager(this.context);\n\n Engine._engines.set(this.id, this);\n Engine._currentId = this.id;\n }\n\n get state() {\n return this.transport.state;\n }\n\n async initialize() {\n if (this.isInitialized) return;\n\n await loadProcessors(this.context);\n await this.midiDeviceManager.initialize();\n this.isInitialized = true;\n }\n\n addModule<T extends ModuleType>(params: ICreateModule<T>) {\n const module = createModule(this.id, params as ModuleParams);\n this.modules.set(module.id, module);\n\n return module.serialize();\n }\n\n updateModule<T extends ModuleType>(params: IUpdateModule<T>) {\n const module = this.findModule(params.id);\n if (module.moduleType !== params.moduleType) {\n throw Error(\n `The module id ${params.id} isn't moduleType ${params.moduleType}`,\n );\n }\n\n const updates = pick(params.changes, [\"name\", \"props\"]);\n Object.assign(module, updates);\n\n if (module instanceof PolyModule && params.changes.voices !== undefined) {\n module.voices = params.changes.voices;\n }\n\n return module.serialize();\n }\n\n removeModule(id: string) {\n this.modules.delete(id);\n }\n\n addRoute(props: ICreateRoute): IRoute {\n return this.routes.addRoute(props);\n }\n\n removeRoute(id: string) {\n this.routes.removeRoute(id);\n }\n\n validRoute(props: Optional<IRoute, \"id\">): boolean {\n const { source, destination } = props;\n\n const output = this.findIO(source.moduleId, source.ioName, \"output\");\n const input = this.findIO(\n destination.moduleId,\n destination.ioName,\n \"input\",\n );\n\n return (\n (output.isMidi() && input.isMidi()) ||\n (output.isAudio() && input.isAudio())\n );\n }\n\n async start() {\n await this.resume();\n const actionAt = this.context.currentTime;\n this.transport.start(actionAt);\n }\n\n stop() {\n const actionAt = this.context.currentTime;\n this.transport.stop(actionAt);\n this.transport.reset(actionAt);\n }\n\n pause() {\n const actionAt = this.context.currentTime;\n this.transport.stop(actionAt);\n }\n\n get bpm() {\n return this.transport.bpm;\n }\n\n set bpm(value: number) {\n this.transport.bpm = value;\n }\n\n get timeSignature() {\n return this.transport.timeSignature;\n }\n\n set timeSignature(value: TimeSignature) {\n this.transport.timeSignature = value;\n }\n\n async resume() {\n await this.context.resume();\n }\n\n dispose() {\n this.stop();\n this.routes.clear();\n this.modules.forEach((module) => {\n module.dispose();\n });\n this.modules.clear();\n }\n\n serialize(): IEngineSerialize {\n return {\n bpm: this.bpm,\n timeSignature: this.timeSignature,\n modules: Array.from(this.modules.values()).map((m) => m.serialize()),\n routes: this.routes.serialize(),\n };\n }\n\n findModule(\n id: string,\n ): ModuleTypeToModuleMapping[keyof ModuleTypeToModuleMapping] {\n const module = this.modules.get(id);\n if (!module) throw Error(`The module with id ${id} is not exists`);\n\n return module;\n }\n\n findIO(moduleId: string, ioName: string, type: \"input\" | \"output\") {\n const module = this.findModule(moduleId);\n return module[`${type}s`].findByName(ioName);\n }\n\n findMidiDevice(id: string) {\n return this.midiDeviceManager.find(id);\n }\n\n findMidiDeviceByName(name: string) {\n return this.midiDeviceManager.findByName(name);\n }\n\n findMidiDeviceByFuzzyName(name: string, threshold?: number) {\n return this.midiDeviceManager.findByFuzzyName(name, threshold);\n }\n\n findMidiInputDevice(id: string) {\n return this.midiDeviceManager.findInput(id);\n }\n\n findMidiInputDeviceByName(name: string) {\n return this.midiDeviceManager.findInputByName(name);\n }\n\n findMidiInputDeviceByFuzzyName(name: string, threshold?: number) {\n return this.midiDeviceManager.findInputByFuzzyName(name, threshold);\n }\n\n findMidiOutputDevice(id: string) {\n return this.midiDeviceManager.findOutput(id);\n }\n\n findMidiOutputDeviceByName(name: string) {\n return this.midiDeviceManager.findOutputByName(name);\n }\n\n findMidiOutputDeviceByFuzzyName(name: string, threshold?: number) {\n return this.midiDeviceManager.findOutputByFuzzyName(name, threshold);\n }\n\n onPropsUpdate(\n callback: <T extends ModuleType>(\n params: (IModule<T> | IPolyModule<T>) & {\n state?: ModuleTypeToStateMapping[T];\n },\n ) => void,\n ) {\n this.propsUpdateCallbacks.push(callback);\n }\n\n _triggerPropsUpdate<T extends ModuleType>(\n params: (IModule<T> | IPolyModule<T>) & {\n state?: ModuleTypeToStateMapping[T];\n },\n ) {\n this.propsUpdateCallbacks.forEach((callback) => {\n callback(params);\n });\n }\n\n // TODO: Find better way to support this\n triggerVirtualMidi(id: string, noteName: string, type: \"noteOn\" | \"noteOff\") {\n const virtualMidi = this.findModule(id);\n if (virtualMidi.moduleType !== ModuleType.VirtualMidi)\n throw Error(\"This is not a virtual mid\");\n\n virtualMidi.sendMidi(\n MidiEvent.fromNote(noteName, type === \"noteOn\", this.context.currentTime),\n );\n }\n\n // actionAt is context time\n private onStart = (ticks: Ticks) => {\n const actionAt = this.transport.getContextTimeAtTicks(ticks);\n\n this.modules.forEach((module) => {\n module.start(actionAt);\n });\n };\n\n // actionAt is context time\n private onStop = (ticks: Ticks) => {\n const actionAt = this.transport.getContextTimeAtTicks(ticks);\n\n this.modules.forEach((module) => {\n module.stop(actionAt);\n });\n };\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport {\n Context,\n Optional,\n upperFirst,\n uuidv4,\n requestAnimationFrame,\n} from \"@blibliki/utils\";\nimport { Engine } from \"@/Engine\";\nimport {\n AnyModule,\n ICreateModule,\n ModuleType,\n ModuleTypeToPropsMapping,\n ModuleTypeToStateMapping,\n} from \"@/modules\";\nimport {\n AudioInputProps,\n AudioOutputProps,\n IIOSerialize,\n IOType,\n InputCollection,\n OutputCollection,\n MidiInputProps,\n MidiOutputProps,\n} from \"../IO\";\nimport Note from \"../Note\";\nimport MidiEvent, { MidiEventType } from \"../midi/MidiEvent\";\n\nexport type IModule<T extends ModuleType> = {\n id: string;\n name: string;\n voiceNo: number;\n moduleType: T;\n props: ModuleTypeToPropsMapping[T];\n};\n\nexport type IModuleSerialize<T extends ModuleType> = IModule<T> & {\n inputs: IIOSerialize[];\n outputs: IIOSerialize[];\n};\n\nexport type IModuleConstructor<T extends ModuleType> = Optional<\n IModule<T>,\n \"id\" | \"voiceNo\"\n> & {\n audioNodeConstructor?: (context: Context) => AudioNode;\n};\n\nexport type SetterHooks<P> = {\n [K in keyof P as `onSet${Capitalize<string & K>}`]: (value: P[K]) => P[K];\n} & {\n [K in keyof P as `onAfterSet${Capitalize<string & K>}`]: (\n value: P[K],\n ) => void;\n};\n\nexport type StateSetterHooks<S> = {\n [K in keyof S as `onSetState${Capitalize<string & K>}`]: (\n value: S[K],\n ) => S[K];\n} & {\n [K in keyof S as `onAfterSetState${Capitalize<string & K>}`]: (\n value: S[K],\n ) => void;\n};\n\nexport abstract class Module<T extends ModuleType> implements IModule<T> {\n id: string;\n engineId: string;\n name: string;\n moduleType: T;\n voiceNo: number;\n audioNode: AudioNode | undefined;\n inputs: InputCollection;\n outputs: OutputCollection;\n protected _props!: ModuleTypeToPropsMapping[T];\n protected _state!: ModuleTypeToStateMapping[T];\n protected activeNotes: Note[];\n protected _propsInitialized = false;\n private pendingUIUpdates = false;\n\n /**\n * Factory method for creating modules with proper initialization timing.\n *\n * This method ensures hooks are called AFTER the child class constructor completes,\n * solving the ES6 class field initialization problem where function properties like hooks\n * aren't available during super() call.\n *\n * @example\n * const gain = Module.create(MonoGain, engineId, {\n * name: \"gain\",\n * moduleType: ModuleType.Gain,\n * props: { gain: 0.5 }\n * });\n */\n static create<T extends ModuleType, M extends Module<T>>(\n ModuleClass: new (engineId: string, params: ICreateModule<T>) => M,\n engineId: string,\n params: Omit<IModuleConstructor<T>, \"props\"> & {\n props: Partial<IModule<T>[\"props\"]>;\n },\n ): M {\n // Create instance with deferred prop initialization\n const instance = new ModuleClass(engineId, {\n ...params,\n });\n\n // Now trigger prop setters after child constructor has completed\n // At this point, all child class properties (including arrow functions) exist\n // TODO: We have to refactor all modules the remove the props assignment from constructor\n instance.props = { ...instance.props };\n instance._propsInitialized = true;\n\n return instance;\n }\n\n constructor(engineId: string, params: IModuleConstructor<T>) {\n const { id, name, moduleType, voiceNo, audioNodeConstructor, props } =\n params;\n\n this.id = id ?? uuidv4();\n this.engineId = engineId;\n this.name = name;\n this.moduleType = moduleType;\n this.voiceNo = voiceNo ?? 0;\n this.activeNotes = [];\n this.audioNode = audioNodeConstructor?.(this.context);\n this._props = props;\n\n this.inputs = new InputCollection(this);\n this.outputs = new OutputCollection(this);\n }\n\n get props(): ModuleTypeToPropsMapping[T] {\n return this._props;\n }\n\n set props(value: Partial<ModuleTypeToPropsMapping[T]>) {\n const updatedValue: Partial<ModuleTypeToPropsMapping[T]> = {};\n const isFirstSet = !this._propsInitialized;\n\n (Object.keys(value) as (keyof ModuleTypeToPropsMapping[T])[]).forEach(\n (key) => {\n const propValue = value[key];\n // On first set, always include the value. On subsequent sets, only if it changed.\n if (\n propValue !== undefined &&\n (isFirstSet || this._props[key] !== propValue)\n ) {\n const result = this.callPropHook(\"onSet\", key, propValue);\n updatedValue[key] = result ?? propValue;\n }\n },\n );\n\n if (Object.keys(updatedValue).length === 0) return;\n\n this._props = { ...this._props, ...updatedValue };\n\n (\n Object.keys(updatedValue) as (keyof ModuleTypeToPropsMapping[T])[]\n ).forEach((key) => {\n const propValue = updatedValue[key];\n if (propValue !== undefined) {\n this.callPropHook(\"onAfterSet\", key, propValue);\n }\n });\n }\n\n private callPropHook<K extends keyof ModuleTypeToPropsMapping[T]>(\n hookType: \"onSet\" | \"onAfterSet\",\n key: K,\n value: ModuleTypeToPropsMapping[T][K],\n ): ModuleTypeToPropsMapping[T][K] | undefined {\n const hookName = `${hookType}${upperFirst(key as string)}`;\n const hook = this[hookName as keyof this];\n\n if (typeof hook === \"function\") {\n const result = (\n hook as (\n value: ModuleTypeToPropsMapping[T][K],\n ) => ModuleTypeToPropsMapping[T][K] | undefined\n ).call(this, value);\n return result;\n }\n return undefined;\n }\n\n get state(): ModuleTypeToStateMapping[T] {\n return this._state;\n }\n\n set state(value: Partial<ModuleTypeToStateMapping[T]>) {\n const updatedValue: Partial<ModuleTypeToStateMapping[T]> = {};\n\n (Object.keys(value) as (keyof ModuleTypeToStateMapping[T])[]).forEach(\n (key) => {\n const stateValue = value[key];\n if (stateValue !== undefined && this._state[key] !== stateValue) {\n const result = this.callStateHook(\"onSetState\", key, stateValue);\n updatedValue[key] = result ?? stateValue;\n }\n },\n );\n\n if (Object.keys(updatedValue).length === 0) return;\n\n this._state = { ...this._state, ...updatedValue };\n\n (\n Object.keys(updatedValue) as (keyof ModuleTypeToStateMapping[T])[]\n ).forEach((key) => {\n const stateValue = updatedValue[key];\n if (stateValue !== undefined) {\n this.callStateHook(\"onAfterSetState\", key, stateValue);\n }\n });\n }\n\n private callStateHook<K extends keyof ModuleTypeToStateMapping[T]>(\n hookType: \"onSetState\" | \"onAfterSetState\",\n key: K,\n value: ModuleTypeToStateMapping[T][K],\n ): ModuleTypeToStateMapping[T][K] | undefined {\n const hookName = `${hookType}${upperFirst(key as string)}`;\n const hook = this[hookName as keyof this];\n\n if (typeof hook === \"function\") {\n const result = (\n hook as (\n value: ModuleTypeToStateMapping[T][K],\n ) => ModuleTypeToStateMapping[T][K] | undefined\n ).call(this, value);\n return result;\n }\n return undefined;\n }\n\n serialize(): IModuleSerialize<T> {\n return {\n id: this.id,\n name: this.name,\n moduleType: this.moduleType,\n voiceNo: this.voiceNo,\n props: this.props,\n inputs: this.inputs.serialize(),\n outputs: this.outputs.serialize(),\n };\n }\n\n plug({\n audioModule,\n from,\n to,\n }: {\n audioModule: AnyModule;\n from: string;\n to: string;\n }) {\n const output = this.outputs.findByName(from);\n const input = audioModule.inputs.findByName(to);\n\n output.plug(input);\n }\n\n protected rePlugAll(callback?: () => void) {\n this.inputs.rePlugAll(callback);\n this.outputs.rePlugAll(callback);\n }\n\n protected unPlugAll() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n }\n\n start(_time: ContextTime): void {\n // Optional implementation in modules\n }\n\n stop(_time: ContextTime): void {\n // Optional implementation in modules\n }\n\n triggerAttack(note: Note, _triggeredAt: ContextTime): void {\n if (this.activeNotes.some((n) => n.fullName === note.fullName)) return;\n\n this.activeNotes.push(note);\n }\n\n triggerRelease(note: Note, _triggeredAt: ContextTime): void {\n this.activeNotes = this.activeNotes.filter(\n (n) => n.fullName !== note.fullName,\n );\n }\n\n handleCC(_event: MidiEvent, _triggeredAt: ContextTime): void {\n // Optional implementation in modules\n }\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n const { note, triggeredAt } = midiEvent;\n\n switch (midiEvent.type) {\n case MidiEventType.noteOn: {\n this.triggerAttack(note!, triggeredAt);\n break;\n }\n case MidiEventType.noteOff:\n this.triggerRelease(note!, triggeredAt);\n break;\n case MidiEventType.cc:\n this.handleCC(midiEvent, triggeredAt);\n break;\n default:\n throw Error(\"This type is not a note\");\n }\n };\n\n triggerPropsUpdate = () => {\n if (this.pendingUIUpdates) return;\n\n this.pendingUIUpdates = true;\n this.sheduleTriggerUpdate();\n };\n\n private sheduleTriggerUpdate() {\n requestAnimationFrame(() => {\n const updateParams: IModule<T> & {\n state?: ModuleTypeToStateMapping[T];\n } = {\n id: this.id,\n moduleType: this.moduleType,\n voiceNo: this.voiceNo,\n name: this.name,\n props: this.props,\n state: this._state,\n };\n\n this.engine._triggerPropsUpdate(updateParams);\n this.pendingUIUpdates = false;\n });\n }\n\n dispose() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n }\n\n protected registerDefaultIOs(value: \"both\" | \"in\" | \"out\" = \"both\") {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n\n if (!this.audioNode) return;\n\n if (value === \"in\" || value === \"both\") {\n this.registerAudioInput({\n name: \"in\",\n getAudioNode: () => this.audioNode!,\n });\n }\n\n if (value === \"out\" || value === \"both\") {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.audioNode!,\n });\n }\n }\n\n protected registerAudioInput(props: Omit<AudioInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.AudioInput });\n }\n\n protected registerAudioOutput(props: Omit<AudioOutputProps, \"ioType\">) {\n return this.outputs.add({ ...props, ioType: IOType.AudioOutput });\n }\n\n protected registerMidiInput(props: Omit<MidiInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.MidiInput });\n }\n\n protected registerMidiOutput(props: Omit<MidiOutputProps, \"ioType\">) {\n return this.outputs.add({\n ...props,\n ioType: IOType.MidiOutput,\n });\n }\n\n protected get engine() {\n return Engine.getById(this.engineId);\n }\n\n protected get context() {\n return this.engine.context;\n }\n}\n","import { assertNever } from \"@blibliki/utils\";\nimport { sortBy } from \"es-toolkit\";\nimport { ModuleType } from \"@/modules\";\nimport { Module } from \"../module\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport {\n AudioInput,\n AudioInputProps,\n AudioOutput,\n AudioOutputProps,\n} from \"./AudioIO\";\nimport { Base, IOType } from \"./Base\";\nimport {\n MidiInput,\n MidiInputProps,\n MidiOutput,\n MidiOutputProps,\n} from \"./MidiIO\";\nimport {\n PolyAudioInput,\n PolyAudioInputProps,\n PolyAudioOutput,\n PolyAudioOutputProps,\n} from \"./PolyAudioIO\";\n\nexport enum CollectionType {\n Input = \"Input\",\n Output = \"Output\",\n}\n\ntype IMappedIOProps = {\n [CollectionType.Input]:\n | AudioInputProps\n | PolyAudioInputProps\n | MidiInputProps;\n [CollectionType.Output]:\n | AudioOutputProps\n | PolyAudioOutputProps\n | MidiOutputProps;\n};\n\ntype IIOTypeTOClass = {\n [IOType.AudioInput]: AudioInput;\n [IOType.AudioOutput]: AudioOutput;\n [IOType.PolyAudioInput]: PolyAudioInput;\n [IOType.PolyAudioOutput]: PolyAudioOutput;\n [IOType.MidiInput]: MidiInput;\n [IOType.MidiOutput]: MidiOutput;\n};\n\nexport default abstract class IOCollection<T extends CollectionType> {\n module: Module<ModuleType> | PolyModule<ModuleType>;\n collection: Base[] = [];\n collectionType: T;\n\n constructor(\n collectionType: T,\n module: Module<ModuleType> | PolyModule<ModuleType>,\n ) {\n this.collectionType = collectionType;\n this.module = module;\n }\n\n add<TT extends IMappedIOProps[T]>(props: TT): IIOTypeTOClass[TT[\"ioType\"]] {\n let io:\n | AudioInput\n | AudioOutput\n | PolyAudioInput\n | PolyAudioOutput\n | MidiInput\n | MidiOutput;\n this.validateUniqName(props.name);\n\n switch (props.ioType) {\n case IOType.AudioInput:\n if (this.module instanceof PolyModule) throw Error(\"Not compatible\");\n io = new AudioInput(this.module, props);\n break;\n case IOType.AudioOutput:\n if (this.module instanceof PolyModule) throw Error(\"Not compatible\");\n io = new AudioOutput(this.module, props);\n break;\n case IOType.PolyAudioInput:\n if (this.module instanceof Module) throw Error(\"Not compatible\");\n io = new PolyAudioInput(this.module, props);\n break;\n case IOType.PolyAudioOutput:\n if (this.module instanceof Module) throw Error(\"Not compatible\");\n io = new PolyAudioOutput(this.module, props);\n break;\n case IOType.MidiInput:\n io = new MidiInput(this.module, props);\n break;\n case IOType.MidiOutput:\n io = new MidiOutput(this.module, props);\n break;\n default:\n assertNever(props);\n }\n\n this.collection.push(io);\n\n return io as IIOTypeTOClass[TT[\"ioType\"]];\n }\n\n unPlugAll() {\n this.collection.forEach((io) => {\n io.unPlugAll();\n });\n }\n\n rePlugAll(callback?: () => void) {\n this.collection.forEach((io) => {\n io.rePlugAll(callback);\n });\n }\n\n find(id: string) {\n const io = this.collection.find((io) => io.id === id);\n if (!io) throw Error(`The io with id ${id} is not exists`);\n\n return io;\n }\n\n findByName(name: string) {\n const io = this.collection.find((io) => io.name === name);\n if (!io) throw Error(`The io with name ${name} is not exists`);\n\n return io;\n }\n\n serialize() {\n return sortBy(this.collection, [(io) => (io.isMidi() ? -1 : 1)]).map((io) =>\n io.serialize(),\n );\n }\n\n private validateUniqName(name: string) {\n if (this.collection.some((io) => io.name === name)) {\n throw Error(`An io with name ${name} is already exists`);\n }\n }\n}\n\nexport class InputCollection extends IOCollection<CollectionType.Input> {\n constructor(module: Module<ModuleType> | PolyModule<ModuleType>) {\n super(CollectionType.Input, module);\n }\n}\n\nexport class OutputCollection extends IOCollection<CollectionType.Output> {\n constructor(module: Module<ModuleType> | PolyModule<ModuleType>) {\n super(CollectionType.Output, module);\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport {\n deterministicId,\n Optional,\n uuidv4,\n requestAnimationFrame,\n} from \"@blibliki/utils\";\nimport { Engine } from \"@/Engine\";\nimport { ModuleType, ModuleTypeToPropsMapping } from \"@/modules\";\nimport {\n IIOSerialize,\n InputCollection,\n IOType,\n MidiInputProps,\n MidiOutputProps,\n OutputCollection,\n} from \"../IO\";\nimport { PolyAudioInputProps, PolyAudioOutputProps } from \"../IO/PolyAudioIO\";\nimport MidiEvent from \"../midi/MidiEvent\";\nimport { IModule, IModuleConstructor, Module } from \"./Module\";\n\nexport type IPolyModule<T extends ModuleType> = Omit<IModule<T>, \"voiceNo\"> & {\n voices: number;\n};\n\nexport type IPolyModuleSerialize<T extends ModuleType> = IPolyModule<T> & {\n inputs: IIOSerialize[];\n outputs: IIOSerialize[];\n};\n\nexport type IPolyModuleConstructor<T extends ModuleType> = Optional<\n IPolyModule<T>,\n \"id\"\n> & {\n monoModuleConstructor: (\n engineId: string,\n params: IModuleConstructor<T>,\n ) => Module<T>;\n};\n\nexport abstract class PolyModule<\n T extends ModuleType,\n> implements IPolyModule<T> {\n id: string;\n engineId: string;\n moduleType: T;\n audioModules!: Module<T>[];\n inputs: InputCollection;\n outputs: OutputCollection;\n protected monoModuleConstructor: IPolyModuleConstructor<T>[\"monoModuleConstructor\"];\n protected _props!: ModuleTypeToPropsMapping[T];\n private _voices!: number;\n private _name!: string;\n private pendingUIUpdates = false;\n\n /**\n * Factory method for creating modules with proper initialization timing.\n *\n * This method ensures hooks are called AFTER the child class constructor completes,\n * solving the ES6 class field initialization problem where function properties like hooks\n * aren't available during super() call.\n *\n * @example\n * const gain = Module.create(Gain, engineId, {\n * name: \"gain\",\n * moduleType: ModuleType.Gain,\n * props: { gain: 0.5 }\n * });\n */\n static create<T extends ModuleType, M extends PolyModule<T>>(\n ModuleClass: new (engineId: string, params: IPolyModuleConstructor<T>) => M,\n engineId: string,\n params: IPolyModuleConstructor<T>,\n ): M {\n // Create instance with deferred prop initialization\n const instance = new ModuleClass(engineId, {\n ...params,\n });\n\n instance.props = { ...instance.props };\n\n return instance;\n }\n\n constructor(engineId: string, params: IPolyModuleConstructor<T>) {\n const { id, name, moduleType, voices, monoModuleConstructor, props } =\n params;\n\n this.audioModules = [];\n\n this.monoModuleConstructor = monoModuleConstructor;\n this.id = id ?? uuidv4();\n this.engineId = engineId;\n this.name = name;\n this.moduleType = moduleType;\n this._props = props;\n\n this.inputs = new InputCollection(\n this as unknown as PolyModule<ModuleType>,\n );\n this.outputs = new OutputCollection(\n this as unknown as PolyModule<ModuleType>,\n );\n\n // Defer hook calls until after subclass is fully initialized\n queueMicrotask(() => {\n this.voices = voices || 1;\n this.props = props;\n this.triggerPropsUpdate();\n });\n }\n\n get name() {\n return this._name;\n }\n\n set name(value: string) {\n this._name = value;\n this.audioModules.forEach((m) => (m.name = value));\n }\n\n get props(): ModuleTypeToPropsMapping[T] {\n return this._props;\n }\n\n set props(value: Partial<ModuleTypeToPropsMapping[T]>) {\n this._props = { ...this._props, ...value };\n this.audioModules.forEach((m) => (m.props = value));\n }\n\n get voices() {\n return this._voices;\n }\n\n set voices(value: number) {\n this._voices = value;\n this.adjustNumberOfModules();\n this.rePlugAll();\n }\n\n start(time: ContextTime): void {\n this.audioModules.forEach((m) => {\n m.start(time);\n });\n }\n\n stop(time: ContextTime): void {\n this.audioModules.forEach((m) => {\n m.stop(time);\n });\n }\n\n serialize(): IPolyModuleSerialize<T> {\n return {\n id: this.id,\n name: this.name,\n moduleType: this.moduleType,\n voices: this.voices,\n props: this.props,\n inputs: this.inputs.serialize(),\n outputs: this.outputs.serialize(),\n };\n }\n\n plug({\n audioModule,\n from,\n to,\n }: {\n audioModule: Module<ModuleType> | PolyModule<ModuleType>;\n from: string;\n to: string;\n }) {\n const output = this.outputs.findByName(from);\n const input = audioModule.inputs.findByName(to);\n\n output.plug(input);\n }\n\n rePlugAll(callback?: () => void) {\n this.inputs.rePlugAll(callback);\n this.outputs.rePlugAll(callback);\n }\n\n protected unPlugAll() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n }\n\n dispose() {\n this.inputs.unPlugAll();\n this.outputs.unPlugAll();\n this.audioModules.forEach((m) => {\n m.dispose();\n });\n }\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n if (midiEvent.cc) {\n this.audioModules.forEach((m) => {\n m.onMidiEvent(midiEvent);\n });\n return;\n }\n\n const voiceNo = midiEvent.voiceNo ?? 0;\n const audioModule = this.findVoice(voiceNo);\n audioModule.onMidiEvent(midiEvent);\n };\n\n triggerPropsUpdate = () => {\n if (this.pendingUIUpdates) return;\n\n this.pendingUIUpdates = true;\n this.sheduleTriggerUpdate();\n };\n\n private sheduleTriggerUpdate() {\n requestAnimationFrame(() => {\n this.engine._triggerPropsUpdate({\n id: this.id,\n moduleType: this.moduleType,\n voices: this.voices,\n name: this.name,\n props: this.props,\n });\n this.pendingUIUpdates = false;\n });\n }\n\n findVoice(voiceNo: number) {\n const moduleByVoice = this.audioModules.find((m) => m.voiceNo === voiceNo);\n if (!moduleByVoice)\n throw Error(`Voice ${voiceNo} on module ${this.name} not found`);\n\n return moduleByVoice;\n }\n\n protected registerDefaultIOs(value: \"both\" | \"in\" | \"out\" = \"both\") {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n\n if (value === \"in\" || value === \"both\") {\n this.registerAudioInput({\n name: \"in\",\n });\n }\n\n if (value === \"out\" || value === \"both\") {\n this.registerAudioOutput({\n name: \"out\",\n });\n }\n }\n\n protected registerAudioInput(props: Omit<PolyAudioInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.PolyAudioInput });\n }\n\n protected registerAudioOutput(props: Omit<PolyAudioOutputProps, \"ioType\">) {\n return this.outputs.add({ ...props, ioType: IOType.PolyAudioOutput });\n }\n\n protected registerMidiInput(props: Omit<MidiInputProps, \"ioType\">) {\n return this.inputs.add({ ...props, ioType: IOType.MidiInput });\n }\n\n protected registerMidiOutput(props: Omit<MidiOutputProps, \"ioType\">) {\n return this.outputs.add({\n ...props,\n ioType: IOType.MidiOutput,\n });\n }\n\n private adjustNumberOfModules() {\n if (this.audioModules.length === this.voices) return;\n\n if (this.audioModules.length > this.voices) {\n const audioModule = this.audioModules.pop();\n audioModule?.dispose();\n } else {\n const voiceNo = this.audioModules.length;\n const id = deterministicId(this.id, voiceNo.toString());\n\n const audioModule = this.monoModuleConstructor(this.engineId, {\n id,\n name: this.name,\n moduleType: this.moduleType,\n voiceNo,\n props: { ...this.props },\n });\n\n this.audioModules.push(audioModule);\n }\n\n this.adjustNumberOfModules();\n }\n\n protected get engine() {\n return Engine.getById(this.engineId);\n }\n\n protected get context() {\n return this.engine.context;\n }\n}\n","import { AudioParam } from \"@blibliki/utils/web-audio-api\";\nimport { ModuleType } from \"@/modules\";\nimport { Module } from \"../module\";\nimport IO, { IOProps, IOType } from \"./Base\";\nimport { PolyAudioInput, PolyAudioOutput } from \"./PolyAudioIO\";\n\nexport type AudioIO = AudioInput | AudioOutput;\n\nexport type AudioInputProps = IOProps & {\n ioType: IOType.AudioInput;\n getAudioNode: () => AudioNode | AudioParam | AudioDestinationNode;\n};\n\nexport type AudioOutputProps = IOProps & {\n ioType: IOType.AudioOutput;\n getAudioNode: () => AudioNode;\n};\n\nexport class AudioInput\n extends IO<AudioOutput | PolyAudioOutput>\n implements AudioInputProps\n{\n declare ioType: IOType.AudioInput;\n getAudioNode: AudioInputProps[\"getAudioNode\"];\n\n constructor(module: Module<ModuleType>, props: AudioInputProps) {\n super(module, props);\n this.getAudioNode = props.getAudioNode;\n }\n}\n\nexport class AudioOutput\n extends IO<AudioInput | PolyAudioInput>\n implements AudioOutputProps\n{\n declare ioType: IOType.AudioOutput;\n getAudioNode!: AudioOutputProps[\"getAudioNode\"];\n\n constructor(module: Module<ModuleType>, props: AudioOutputProps) {\n super(module, props);\n this.getAudioNode = props.getAudioNode;\n }\n\n plug(io: AudioInput | PolyAudioInput, plugOther = true) {\n super.plug(io, plugOther);\n if (io instanceof PolyAudioInput) return;\n\n const input = io.getAudioNode();\n\n if (input instanceof AudioParam) {\n this.getAudioNode().connect(input);\n } else {\n this.getAudioNode().connect(input);\n }\n }\n\n unPlug(io: AudioInput | PolyAudioInput, plugOther = true) {\n super.unPlug(io, plugOther);\n if (io instanceof PolyAudioInput) return;\n\n const input = io.getAudioNode();\n\n try {\n if (input instanceof AudioParam) {\n this.getAudioNode().disconnect(input);\n } else {\n this.getAudioNode().disconnect(input);\n }\n } catch {\n // Ignore disconnect errors\n }\n }\n}\n","import { deterministicId } from \"@blibliki/utils\";\nimport { ModuleType } from \"@/modules\";\nimport { Module } from \"../module\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport { AudioInput, AudioOutput } from \"./AudioIO\";\nimport { MidiInput, MidiOutput } from \"./MidiIO\";\nimport { PolyAudioInput, PolyAudioOutput } from \"./PolyAudioIO\";\n\nexport type IOProps = {\n name: string;\n ioType: IOType;\n};\n\nexport type IIOSerialize = IOProps & {\n id: string;\n moduleId: string;\n};\n\nexport enum IOType {\n AudioInput = \"audioInput\",\n AudioOutput = \"audioOutput\",\n PolyAudioInput = \"polyAudioInput\",\n PolyAudioOutput = \"polyAudioOutput\",\n MidiOutput = \"midiOutput\",\n MidiInput = \"midiInput\",\n}\n\nexport type IIO = {\n id: string;\n module: Module<ModuleType> | PolyModule<ModuleType>;\n} & IOProps;\n\nexport abstract class Base implements IIO {\n id: string;\n ioType: IOType;\n name: string;\n module: Module<ModuleType> | PolyModule<ModuleType>;\n connections: Base[];\n\n constructor(\n module: Module<ModuleType> | PolyModule<ModuleType>,\n props: IOProps,\n ) {\n this.module = module;\n this.name = props.name;\n this.ioType = props.ioType;\n this.id = deterministicId(this.module.id, this.name);\n this.connections = [];\n }\n\n plug(io: Base, plugOther = true) {\n this.connections.push(io);\n if (plugOther) io.plug(this, false);\n }\n\n unPlug(io: Base, plugOther = true) {\n this.connections = this.connections.filter(\n (currentIO) => currentIO.id !== io.id,\n );\n if (plugOther) io.unPlug(this, false);\n }\n\n rePlugAll(callback?: () => void) {\n const connections = this.connections;\n this.unPlugAll();\n if (callback) callback();\n\n connections.forEach((otherIO) => {\n this.plug(otherIO);\n });\n }\n\n unPlugAll() {\n this.connections.forEach((otherIO) => {\n this.unPlug(otherIO);\n });\n }\n\n isAudio(): this is\n | AudioInput\n | AudioOutput\n | PolyAudioInput\n | PolyAudioOutput {\n return (\n this.ioType === IOType.AudioInput ||\n this.ioType === IOType.AudioOutput ||\n this.ioType === IOType.PolyAudioInput ||\n this.ioType === IOType.PolyAudioOutput\n );\n }\n\n isMidi(): this is MidiInput | MidiOutput {\n return (\n this.ioType === IOType.MidiInput || this.ioType === IOType.MidiOutput\n );\n }\n\n serialize(): IIOSerialize {\n return {\n id: this.id,\n name: this.name,\n ioType: this.ioType,\n moduleId: this.module.id,\n };\n }\n}\n\nexport default abstract class IO<Connection extends Base> extends Base {\n declare connections: Connection[];\n\n plug(io: Connection, plugOther?: boolean): void {\n super.plug(io, plugOther);\n }\n\n unPlug(io: Connection, plugOther?: boolean): void {\n super.unPlug(io, plugOther);\n }\n}\n","import { ModuleType } from \"@/modules\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport { AudioInput, AudioOutput } from \"./AudioIO\";\nimport IO, { IOProps, IOType } from \"./Base\";\n\nexport type PolyAudioIO = PolyAudioInput | PolyAudioOutput;\n\nexport type PolyAudioInputProps = IOProps & {\n ioType: IOType.PolyAudioInput;\n};\n\nexport type PolyAudioOutputProps = IOProps & {\n ioType: IOType.PolyAudioOutput;\n};\n\nexport class PolyAudioInput\n extends IO<PolyAudioOutput | AudioOutput>\n implements PolyAudioInputProps\n{\n declare ioType: IOType.PolyAudioInput;\n declare module: PolyModule<ModuleType>;\n\n plug(io: PolyAudioOutput | AudioOutput, plugOther = true) {\n super.plug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioOutput) return;\n\n plugOrUnplug(this, io, true);\n }\n\n unPlug(io: PolyAudioOutput | AudioOutput, plugOther = true) {\n super.unPlug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioOutput) return;\n\n plugOrUnplug(this, io, false);\n }\n\n findIOByVoice(voice: number): AudioInput {\n return this.module\n .findVoice(voice)\n .inputs.findByName(this.name) as AudioInput;\n }\n}\n\nexport class PolyAudioOutput\n extends IO<PolyAudioInput | AudioInput>\n implements PolyAudioOutputProps\n{\n declare ioType: IOType.PolyAudioOutput;\n declare module: PolyModule<ModuleType>;\n\n plug(io: PolyAudioInput | AudioInput, plugOther = true) {\n super.plug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioInput) return;\n\n plugOrUnplug(this, io, true);\n }\n\n unPlug(io: PolyAudioInput | AudioInput, plugOther = true) {\n super.unPlug(io, plugOther);\n if (!plugOther && io instanceof PolyAudioInput) return;\n\n plugOrUnplug(this, io, false);\n }\n\n findIOByVoice(voice: number): AudioOutput {\n return this.module\n .findVoice(voice)\n .outputs.findByName(this.name) as AudioOutput;\n }\n}\n\nfunction plugOrUnplug(\n thisIO: PolyAudioInput,\n otherIO: PolyAudioOutput | AudioOutput,\n isPlug: boolean,\n): void;\nfunction plugOrUnplug(\n thisIO: PolyAudioOutput,\n otherIO: PolyAudioInput | AudioInput,\n isPlug: boolean,\n): void;\nfunction plugOrUnplug(\n thisIO: PolyAudioInput | PolyAudioOutput,\n otherIO: PolyAudioOutput | AudioOutput | PolyAudioInput | AudioInput,\n isPlug: boolean,\n) {\n if (otherIO instanceof PolyAudioInput || otherIO instanceof PolyAudioOutput) {\n const maxVoices = Math.max(thisIO.module.voices, otherIO.module.voices);\n\n for (let voice = 0; voice < maxVoices; voice++) {\n const thisMonoIO = thisIO.findIOByVoice(voice % thisIO.module.voices);\n const otherMonoIO = otherIO.findIOByVoice(voice % otherIO.module.voices);\n\n if (isPlug) {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.plug(otherMonoIO);\n } else {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.unPlug(otherMonoIO);\n }\n }\n } else {\n for (let voice = 0; voice < thisIO.module.voices; voice++) {\n const thisMonoIO = thisIO.findIOByVoice(voice);\n\n if (isPlug) {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.plug(otherIO);\n } else {\n // @ts-expect-error: temp solution until guard this input plug to output\n thisMonoIO.unPlug(otherIO);\n }\n }\n }\n}\n","import { ModuleType } from \"@/modules\";\nimport MidiEvent from \"../midi/MidiEvent\";\nimport { Module } from \"../module\";\nimport { PolyModule } from \"../module/PolyModule\";\nimport IO, { IOProps, IOType } from \"./Base\";\n\nexport type MidiIO = MidiInput | MidiOutput;\n\nexport type MidiInputProps = IOProps & {\n ioType: IOType.MidiInput;\n onMidiEvent: (event: MidiEvent) => void;\n};\n\nexport type MidiOutputProps = IOProps & {\n ioType: IOType.MidiOutput;\n};\n\nexport class MidiInput extends IO<MidiOutput> implements MidiInputProps {\n declare ioType: IOType.MidiInput;\n onMidiEvent: MidiInputProps[\"onMidiEvent\"];\n\n constructor(\n module: Module<ModuleType> | PolyModule<ModuleType>,\n props: MidiInputProps,\n ) {\n super(module, props);\n this.onMidiEvent = props.onMidiEvent;\n }\n}\n\nexport class MidiOutput extends IO<MidiInput> implements MidiOutputProps {\n declare ioType: IOType.MidiOutput;\n\n onMidiEvent = (event: MidiEvent) => {\n this.midiConnections.forEach((input) => {\n input.onMidiEvent(event);\n });\n };\n\n private get midiConnections() {\n return this.connections.filter((input) => input instanceof MidiInput);\n }\n}\n","const frequencyTable = new Map<string, number>([\n [\"C0\", 16.35],\n [\"C#0\", 17.32],\n [\"Db0\", 17.32],\n [\"D0\", 18.35],\n [\"D#0\", 19.45],\n [\"Eb0\", 19.45],\n [\"E0\", 20.6],\n [\"F0\", 21.83],\n [\"F#0\", 23.12],\n [\"Gb0\", 23.12],\n [\"G0\", 24.5],\n [\"G#0\", 25.96],\n [\"Ab0\", 25.96],\n [\"A0\", 27.5],\n [\"A#0\", 29.14],\n [\"Bb0\", 29.14],\n [\"B0\", 30.87],\n [\"C1\", 32.7],\n [\"C#1\", 34.65],\n [\"Db1\", 34.65],\n [\"D1\", 36.71],\n [\"D#1\", 38.89],\n [\"Eb1\", 38.89],\n [\"E1\", 41.2],\n [\"F1\", 43.65],\n [\"F#1\", 46.25],\n [\"Gb1\", 46.25],\n [\"G1\", 49.0],\n [\"G#1\", 51.91],\n [\"Ab1\", 51.91],\n [\"A1\", 55.0],\n [\"A#1\", 58.27],\n [\"Bb1\", 58.27],\n [\"B1\", 61.74],\n [\"C2\", 65.41],\n [\"C#2\", 69.3],\n [\"Db2\", 69.3],\n [\"D2\", 73.42],\n [\"D#2\", 77.78],\n [\"Eb2\", 77.78],\n [\"E2\", 82.41],\n [\"F2\", 87.31],\n [\"F#2\", 92.5],\n [\"Gb2\", 92.5],\n [\"G2\", 98.0],\n [\"G#2\", 103.83],\n [\"Ab2\", 103.83],\n [\"A2\", 110.0],\n [\"A#2\", 116.54],\n [\"Bb2\", 116.54],\n [\"B2\", 123.47],\n [\"C3\", 130.81],\n [\"C#3\", 138.59],\n [\"Db3\", 138.59],\n [\"D3\", 146.83],\n [\"D#3\", 155.56],\n [\"Eb3\", 155.56],\n [\"E3\", 164.81],\n [\"F3\", 174.61],\n [\"F#3\", 185.0],\n [\"Gb3\", 185.0],\n [\"G3\", 196.0],\n [\"G#3\", 207.65],\n [\"Ab3\", 207.65],\n [\"A3\", 220.0],\n [\"A#3\", 233.08],\n [\"Bb3\", 233.08],\n [\"B3\", 246.94],\n [\"C4\", 261.63],\n [\"C#4\", 277.18],\n [\"Db4\", 277.18],\n [\"D4\", 293.66],\n [\"D#4\", 311.13],\n [\"Eb4\", 311.13],\n [\"E4\", 329.63],\n [\"F4\", 349.23],\n [\"F#4\", 369.99],\n [\"Gb4\", 369.99],\n [\"G4\", 392.0],\n [\"G#4\", 415.3],\n [\"Ab4\", 415.3],\n [\"A4\", 440.0],\n [\"A#4\", 466.16],\n [\"Bb4\", 466.16],\n [\"B4\", 493.88],\n [\"C5\", 523.25],\n [\"C#5\", 554.37],\n [\"Db5\", 554.37],\n [\"D5\", 587.33],\n [\"D#5\", 622.25],\n [\"Eb5\", 622.25],\n [\"E5\", 659.26],\n [\"F5\", 698.46],\n [\"F#5\", 739.99],\n [\"Gb5\", 739.99],\n [\"G5\", 783.99],\n [\"G#5\", 830.61],\n [\"Ab5\", 830.61],\n [\"A5\", 880.0],\n [\"A#5\", 932.33],\n [\"Bb5\", 932.33],\n [\"B5\", 987.77],\n [\"C6\", 1046.5],\n [\"C#6\", 1108.73],\n [\"Db6\", 1108.73],\n [\"D6\", 1174.66],\n [\"D#6\", 1244.51],\n [\"Eb6\", 1244.51],\n [\"E6\", 1318.51],\n [\"F6\", 1396.91],\n [\"F#6\", 1479.98],\n [\"Gb6\", 1479.98],\n [\"G6\", 1567.98],\n [\"G#6\", 1661.22],\n [\"Ab6\", 1661.22],\n [\"A6\", 1760.0],\n [\"A#6\", 1864.66],\n [\"Bb6\", 1864.66],\n [\"B6\", 1975.53],\n [\"C7\", 2093.0],\n [\"C#7\", 2217.46],\n [\"Db7\", 2217.46],\n [\"D7\", 2349.32],\n [\"D#7\", 2489.02],\n [\"Eb7\", 2489.02],\n [\"E7\", 2637.02],\n [\"F7\", 2793.83],\n [\"F#7\", 2959.96],\n [\"Gb7\", 2959.96],\n [\"G7\", 3135.96],\n [\"G#7\", 3322.44],\n [\"Ab7\", 3322.44],\n [\"A7\", 3520.0],\n [\"A#7\", 3729.31],\n [\"Bb7\", 3729.31],\n [\"B7\", 3951.07],\n [\"C8\", 4186.01],\n [\"C#8\", 4434.92],\n [\"Db8\", 4434.92],\n [\"D8\", 4698.64],\n [\"D#8\", 4978.03],\n [\"Eb8\", 4978.03],\n]);\n\nexport default frequencyTable;\n","import { Seconds } from \"@blibliki/transport\";\nimport Message from \"../midi/Message\";\nimport frequencyTable from \"./frequencyTable\";\n\nconst Notes = [\"C\", \"C#\", \"D\", \"D#\", \"E\", \"F\", \"F#\", \"G\", \"G#\", \"A\", \"A#\", \"B\"];\n\nconst MIDI_OCTAVE_SYTSTEM = 2;\n\nexport type INote = {\n name: string;\n octave: number;\n frequency: number;\n duration?: Seconds;\n velocity?: number;\n};\n\nexport default class Note implements INote {\n static _notes: Note[];\n name!: string;\n octave!: number;\n velocity = 1;\n duration?: Seconds;\n\n static fromFrequency(frequency: number) {\n let noteName: string | undefined;\n\n for (const [note, freq] of frequencyTable) {\n if (freq !== frequency) continue;\n\n noteName = note;\n break;\n }\n\n if (!noteName) throw Error(\"Not matching frequency with a note\");\n\n return new Note(noteName);\n }\n\n static fromEvent(message: Message) {\n const dataByte = message.data[1];\n if (dataByte === undefined) {\n throw new Error(\"Invalid MIDI message: missing data byte\");\n }\n const name = Notes[dataByte % 12];\n if (!name) {\n throw new Error(`Invalid MIDI note number: ${dataByte}`);\n }\n const octave = Math.floor(dataByte / 12) - 2;\n\n return new Note(`${name}${octave}`);\n }\n\n static notes(octave = 3) {\n return Notes.map((note: string) => new Note(`${note}${octave}`));\n }\n\n constructor(note: Omit<INote, \"frequency\"> | string) {\n if (typeof note === \"string\") {\n this.fromString(note);\n } else {\n this.fromProps(note);\n }\n }\n\n get isSemi() {\n return this.name.endsWith(\"#\");\n }\n\n get fullName() {\n return `${this.name}${this.octave}`;\n }\n\n get frequency(): number {\n return frequencyTable.get(`${this.name}${this.octave}`)!;\n }\n\n midiData(noteOn = true): Uint8Array {\n const statusByte = noteOn ? 0x90 : 0x80;\n return new Uint8Array([statusByte, this.midiNumber, this.velocity * 100]);\n }\n\n get midiNumber(): number {\n return (this.octave + MIDI_OCTAVE_SYTSTEM) * 12 + this.noteIndex;\n }\n\n get noteIndex(): number {\n return Notes.indexOf(this.name);\n }\n\n valueOf() {\n return this.fullName;\n }\n\n serialize(): INote {\n return {\n name: this.name,\n octave: this.octave,\n frequency: this.frequency,\n velocity: this.velocity,\n duration: this.duration,\n };\n }\n\n private fromString(string: string) {\n const matches = /(\\w#?)(\\d)?/.exec(string);\n if (!matches) {\n throw new Error(`Invalid note string: ${string}`);\n }\n\n const name = matches[1];\n if (!name) {\n throw new Error(`Invalid note name in: ${string}`);\n }\n\n this.name = name;\n this.octave = matches[2] ? parseInt(matches[2]) : 1;\n }\n\n private fromProps(props: Omit<INote, \"frequency\">) {\n Object.assign(this, props);\n }\n}\n","/**\n * Simple wrapper around MIDI message data (Uint8Array)\n * Replaces the webmidi Message class with native Web MIDI API data\n */\nexport default class Message {\n public readonly data: Uint8Array;\n\n constructor(data: Uint8Array) {\n this.data = data;\n }\n\n /**\n * Returns the data bytes (excluding the status byte)\n */\n get dataBytes(): number[] {\n return Array.from(this.data.slice(1));\n }\n\n /**\n * Returns the MIDI message type based on the status byte\n */\n get type(): string {\n const statusByte = this.data[0];\n if (statusByte === undefined) return \"unknown\";\n const messageType = statusByte & 0xf0;\n\n switch (messageType) {\n case 0x90: // Note On\n // Check if velocity is 0 (which is actually Note Off)\n return this.data[2] === 0 ? \"noteoff\" : \"noteon\";\n case 0x80: // Note Off\n return \"noteoff\";\n case 0xb0: // Control Change\n return \"controlchange\";\n case 0xe0: // Pitch Bend\n return \"pitchbend\";\n case 0xd0: // Channel Pressure (Aftertouch)\n return \"channelaftertouch\";\n case 0xa0: // Polyphonic Key Pressure\n return \"keyaftertouch\";\n case 0xc0: // Program Change\n return \"programchange\";\n default:\n return \"unknown\";\n }\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport Note, { INote } from \"../Note\";\nimport Message from \"./Message\";\n\nexport enum MidiEventType {\n noteOn = \"noteon\",\n noteOff = \"noteoff\",\n cc = \"controlchange\",\n}\n\nexport default class MidiEvent {\n note?: Note;\n voiceNo?: number;\n readonly triggeredAt: ContextTime;\n private message: Message;\n\n static fromNote(\n noteName: string | Note | Omit<INote, \"frequency\">,\n noteOn = true,\n triggeredAt: ContextTime,\n ): MidiEvent {\n const note = noteName instanceof Note ? noteName : new Note(noteName);\n\n return new MidiEvent(new Message(note.midiData(noteOn)), triggeredAt);\n }\n\n static fromCC(\n cc: number,\n value: number,\n triggeredAt: ContextTime,\n ): MidiEvent {\n return new MidiEvent(\n new Message(new Uint8Array([0xb0, cc, value])),\n triggeredAt,\n );\n }\n\n constructor(message: Message, triggeredAt: ContextTime) {\n this.message = message;\n this.triggeredAt = triggeredAt;\n this.defineNotes();\n }\n\n get type() {\n return this.message.type as MidiEventType;\n }\n\n get isNote() {\n return (\n this.type === MidiEventType.noteOn || this.type === MidiEventType.noteOff\n );\n }\n\n get isCC() {\n return this.type === MidiEventType.cc;\n }\n\n get cc(): number | undefined {\n if (!this.isCC) return;\n\n return this.message.dataBytes[0];\n }\n\n get ccValue(): number | undefined {\n if (!this.isCC) return;\n\n return this.message.dataBytes[1];\n }\n\n defineNotes() {\n if (!this.isNote) return;\n if (this.note) return;\n\n this.note = Note.fromEvent(this.message);\n }\n\n get rawMessage() {\n return this.message;\n }\n\n clone(voiceNo?: number) {\n const newEvent = new MidiEvent(this.message, this.triggeredAt);\n newEvent.voiceNo = voiceNo;\n\n return newEvent;\n }\n}\n","import { Optional, uuidv4 } from \"@blibliki/utils\";\nimport { Engine } from \"@/Engine\";\n\ntype IPlug = {\n moduleId: string;\n ioName: string;\n};\n\nexport type IRoute = {\n id: string;\n source: IPlug;\n destination: IPlug;\n};\n\nexport class Routes {\n engine: Engine;\n routes: Map<string, IRoute>;\n\n constructor(engine: Engine) {\n this.engine = engine;\n this.routes = new Map();\n }\n\n addRoute(props: Optional<IRoute, \"id\">): IRoute {\n const id = props.id ?? uuidv4();\n const route = { ...props, id };\n this.routes.set(id, route);\n\n this.plug(id);\n\n return route;\n }\n\n removeRoute(id: string) {\n this.unPlug(id);\n this.routes.delete(id);\n }\n\n clear() {\n this.routes.forEach((_, id) => {\n this.removeRoute(id);\n });\n }\n\n replug() {\n this.routes.forEach((_, id) => {\n const { sourceIO, destinationIO } = this.getIOs(id);\n sourceIO.rePlugAll();\n destinationIO.rePlugAll();\n });\n }\n\n serialize(): IRoute[] {\n return Array.from(this.routes.values());\n }\n\n private plug(id: string) {\n const { sourceIO, destinationIO } = this.getIOs(id);\n sourceIO.plug(destinationIO);\n }\n\n private unPlug(id: string) {\n const { sourceIO, destinationIO } = this.getIOs(id);\n sourceIO.unPlug(destinationIO);\n }\n\n private find(id: string): IRoute {\n const route = this.routes.get(id);\n if (!route) throw Error(`Route with id ${id} not found`);\n\n return route;\n }\n\n private getIOs(id: string) {\n const route = this.find(id);\n const { source, destination } = route;\n\n const sourceIO = this.engine.findIO(\n source.moduleId,\n source.ioName,\n \"output\",\n );\n const destinationIO = this.engine.findIO(\n destination.moduleId,\n destination.ioName,\n \"input\",\n );\n\n return { sourceIO, destinationIO };\n }\n}\n","import { IMidiPort } from \"./adapters\";\n\nexport enum MidiPortState {\n connected = \"connected\",\n disconnected = \"disconnected\",\n}\n\nexport type IMidiDevice = {\n id: string;\n name: string;\n state: MidiPortState;\n};\n\nexport default abstract class BaseMidiDevice<\n T extends IMidiPort,\n> implements IMidiDevice {\n protected midiPort: T;\n\n constructor(props: T) {\n this.midiPort = props;\n this.connect();\n }\n\n abstract connect(): void;\n\n abstract disconnect(): void;\n\n get id() {\n return this.midiPort.id;\n }\n\n get name() {\n return this.midiPort.name;\n }\n\n get type() {\n return this.midiPort.type;\n }\n\n get state() {\n return this.midiPort.state as MidiPortState;\n }\n\n serialize() {\n return { id: this.id, name: this.name, type: this.type, state: this.state };\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport Note from \"../Note\";\nimport { MidiPortState } from \"./BaseMidiDevice\";\nimport MidiEvent from \"./MidiEvent\";\nimport { EventListerCallback, IMidiInput } from \"./MidiInputDevice\";\n\nconst MAP_KEYS: Record<string, Note> = {\n a: new Note(\"C3\"),\n s: new Note(\"D3\"),\n d: new Note(\"E3\"),\n f: new Note(\"F3\"),\n g: new Note(\"G3\"),\n h: new Note(\"A3\"),\n j: new Note(\"B3\"),\n k: new Note(\"C4\"),\n l: new Note(\"D4\"),\n w: new Note(\"C#3\"),\n e: new Note(\"D#3\"),\n t: new Note(\"F#3\"),\n y: new Note(\"G#3\"),\n u: new Note(\"A#3\"),\n o: new Note(\"C#4\"),\n p: new Note(\"D#4\"),\n};\n\nconst computerKeyboardData = () => ({\n id: \"computer_keyboard\",\n name: \"Computer Keyboard\",\n state: MidiPortState.connected,\n});\n\nexport default class ComputerKeyboardInput implements IMidiInput {\n id: string;\n name: string;\n state: MidiPortState;\n eventListerCallbacks: EventListerCallback[] = [];\n private context: Readonly<Context>;\n\n constructor(context: Context) {\n const { id, name, state } = computerKeyboardData();\n this.id = id;\n this.name = name;\n this.state = state;\n this.context = context;\n\n document.addEventListener(\"keydown\", this.onKeyTrigger(true));\n document.addEventListener(\"keyup\", this.onKeyTrigger(false));\n }\n\n addEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks.push(callback);\n }\n\n removeEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks = this.eventListerCallbacks.filter(\n (c) => c !== callback,\n );\n }\n\n serialize() {\n const { id, name, state } = this;\n\n return { id, name, state };\n }\n\n onKeyTrigger = (noteOn: boolean) => (event: KeyboardEvent) => {\n const note = this.extractNote(event);\n if (!note) return;\n\n const midiEvent = MidiEvent.fromNote(\n note,\n noteOn,\n this.context.browserToContextTime(event.timeStamp),\n );\n this.eventListerCallbacks.forEach((callback) => {\n callback(midiEvent);\n });\n };\n\n private extractNote(event: KeyboardEvent): Note | undefined {\n if (event.repeat) return;\n\n return MAP_KEYS[event.key];\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport BaseMidiDevice, { MidiPortState } from \"./BaseMidiDevice\";\nimport Message from \"./Message\";\nimport MidiEvent, { MidiEventType } from \"./MidiEvent\";\nimport type { IMidiInputPort, IMidiMessageEvent } from \"./adapters\";\n\nexport type IMidiInput = {\n id: string;\n name: string;\n state: MidiPortState;\n eventListerCallbacks: EventListerCallback[];\n};\n\nexport type EventListerCallback = (event: MidiEvent) => void;\n\nexport default class MidiInputDevice extends BaseMidiDevice<IMidiInputPort> {\n eventListerCallbacks: EventListerCallback[] = [];\n\n private context: Readonly<Context>;\n private messageHandler: ((event: IMidiMessageEvent) => void) | null = null;\n\n constructor(input: IMidiInputPort, context: Context) {\n super(input);\n this.context = context;\n }\n\n connect() {\n this.messageHandler = (e: IMidiMessageEvent) => {\n this.processEvent(e);\n };\n this.midiPort.addEventListener(this.messageHandler);\n }\n\n disconnect() {\n if (this.messageHandler) {\n this.midiPort.removeEventListener(this.messageHandler);\n this.messageHandler = null;\n }\n }\n\n addEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks.push(callback);\n }\n\n removeEventListener(callback: EventListerCallback) {\n this.eventListerCallbacks = this.eventListerCallbacks.filter(\n (c) => c !== callback,\n );\n }\n\n private processEvent(event: IMidiMessageEvent) {\n const message = new Message(event.data);\n const midiEvent = new MidiEvent(\n message,\n this.context.browserToContextTime(event.timeStamp),\n );\n\n switch (midiEvent.type) {\n case MidiEventType.noteOn:\n case MidiEventType.noteOff:\n case MidiEventType.cc:\n this.eventListerCallbacks.forEach((callback) => {\n callback(midiEvent);\n });\n }\n }\n}\n","import BaseMidiDevice from \"./BaseMidiDevice\";\nimport type { IMidiOutputPort } from \"./adapters\";\n\nexport default class MidiOutputDevice extends BaseMidiDevice<IMidiOutputPort> {\n constructor(output: IMidiOutputPort) {\n super(output);\n this.connect();\n }\n\n connect() {\n // Output ports don't require connection setup like inputs\n // This method exists to satisfy the BaseMidiDevice interface\n }\n\n disconnect() {\n // Output ports don't require disconnection cleanup like inputs\n // This method exists to satisfy the BaseMidiDevice interface\n }\n\n send(data: number[] | Uint8Array, timestamp?: number) {\n this.midiPort.send(data, timestamp);\n }\n}\n","/**\n * MIDI adapter factory\n * Automatically selects the correct MIDI implementation based on the platform\n */\nimport { isNode } from \"es-toolkit\";\nimport NodeMidiAdapter from \"./NodeMidiAdapter\";\nimport WebMidiAdapter from \"./WebMidiAdapter\";\nimport type { IMidiAdapter } from \"./types\";\n\nexport * from \"./types\";\n\n/**\n * Creates the appropriate MIDI adapter for the current platform\n * @returns The MIDI adapter (Web MIDI API for browsers, node-midi for Node.js)\n */\nexport function createMidiAdapter(): IMidiAdapter {\n if (isNode()) {\n return new NodeMidiAdapter();\n }\n\n // Default to Web MIDI API for browsers\n return new WebMidiAdapter();\n}\n","/**\n * node-midi adapter for Node.js\n */\nimport { isNode } from \"es-toolkit\";\nimport type {\n IMidiAccess,\n IMidiAdapter,\n IMidiInputPort,\n IMidiOutputPort,\n IMidiPort,\n MidiMessageCallback,\n} from \"./types\";\n\n// Dynamic import type for node-midi\ntype NodeMidiInput = {\n getPortCount(): number;\n getPortName(port: number): string;\n openPort(port: number): void;\n closePort(): void;\n on(\n event: \"message\",\n callback: (deltaTime: number, message: number[]) => void,\n ): void;\n off(\n event: \"message\",\n callback: (deltaTime: number, message: number[]) => void,\n ): void;\n isPortOpen(): boolean;\n};\n\ntype NodeMidiOutput = {\n getPortCount(): number;\n getPortName(port: number): string;\n openPort(port: number): void;\n closePort(): void;\n sendMessage(message: number[]): void;\n isPortOpen(): boolean;\n};\n\ntype NodeMidiModule = {\n Input: new () => NodeMidiInput;\n Output: new () => NodeMidiOutput;\n};\n\nclass NodeMidiInputPort implements IMidiInputPort {\n readonly id: string;\n readonly name: string;\n private portIndex: number;\n private input: NodeMidiInput;\n private callbacks = new Set<MidiMessageCallback>();\n private handler: ((deltaTime: number, message: number[]) => void) | null =\n null;\n private _state: \"connected\" | \"disconnected\" = \"disconnected\";\n\n constructor(portIndex: number, name: string, input: NodeMidiInput) {\n this.portIndex = portIndex;\n this.id = `node-midi-${portIndex}`;\n this.name = name;\n this.input = input;\n }\n\n get state(): \"connected\" | \"disconnected\" {\n return this._state;\n }\n\n get type() {\n return \"input\" as const;\n }\n\n addEventListener(callback: MidiMessageCallback): void {\n if (this.callbacks.size === 0) {\n this.handler = (_deltaTime: number, message: number[]) => {\n const event = {\n data: new Uint8Array(message),\n timeStamp: performance.now(),\n };\n\n this.callbacks.forEach((cb) => {\n cb(event);\n });\n };\n\n try {\n if (!this.input.isPortOpen()) {\n this.input.openPort(this.portIndex);\n this._state = \"connected\";\n }\n this.input.on(\"message\", this.handler);\n } catch (err) {\n console.error(`Error opening MIDI port ${this.portIndex}:`, err);\n }\n }\n this.callbacks.add(callback);\n }\n\n removeEventListener(callback: MidiMessageCallback): void {\n this.callbacks.delete(callback);\n\n if (this.callbacks.size === 0 && this.handler) {\n try {\n this.input.off(\"message\", this.handler);\n if (this.input.isPortOpen()) {\n this.input.closePort();\n this._state = \"disconnected\";\n }\n } catch (err) {\n console.error(`Error closing MIDI port ${this.portIndex}:`, err);\n }\n this.handler = null;\n }\n }\n}\n\nclass NodeMidiOutputPort implements IMidiOutputPort {\n readonly id: string;\n readonly name: string;\n private portIndex: number;\n private output: NodeMidiOutput;\n private _state: \"connected\" | \"disconnected\" = \"disconnected\";\n private isOpen = false;\n\n constructor(portIndex: number, name: string, output: NodeMidiOutput) {\n this.portIndex = portIndex;\n this.id = `node-midi-out-${portIndex}`;\n this.name = name;\n this.output = output;\n }\n\n get state(): \"connected\" | \"disconnected\" {\n return this._state;\n }\n\n get type() {\n return \"output\" as const;\n }\n\n private ensureOpen(): void {\n if (!this.isOpen) {\n try {\n this.output.openPort(this.portIndex);\n this.isOpen = true;\n this._state = \"connected\";\n } catch (err) {\n console.error(`Error opening MIDI output port ${this.portIndex}:`, err);\n }\n }\n }\n\n send(data: number[] | Uint8Array, _timestamp?: number): void {\n this.ensureOpen();\n try {\n const message = Array.isArray(data) ? data : Array.from(data);\n this.output.sendMessage(message);\n } catch (err) {\n console.error(`Error sending MIDI message:`, err);\n }\n }\n}\n\nclass NodeMidiAccess implements IMidiAccess {\n private inputPorts = new Map<string, NodeMidiInputPort>();\n private outputPorts = new Map<string, NodeMidiOutputPort>();\n private MidiModule: NodeMidiModule;\n\n constructor(MidiModule: NodeMidiModule) {\n this.MidiModule = MidiModule;\n this.scanPorts();\n }\n\n private scanPorts(): void {\n // Scan input ports\n try {\n const input = new this.MidiModule.Input();\n const inputCount = input.getPortCount();\n\n for (let i = 0; i < inputCount; i++) {\n const portName = input.getPortName(i);\n const id = `node-midi-${i}`;\n\n if (!this.inputPorts.has(id)) {\n const portInput = new this.MidiModule.Input();\n const port = new NodeMidiInputPort(i, portName, portInput);\n this.inputPorts.set(id, port);\n }\n }\n\n if (input.isPortOpen()) {\n input.closePort();\n }\n } catch (err) {\n console.error(\"Error scanning MIDI input ports:\", err);\n }\n\n // Scan output ports\n try {\n const output = new this.MidiModule.Output();\n const outputCount = output.getPortCount();\n\n for (let i = 0; i < outputCount; i++) {\n const portName = output.getPortName(i);\n const id = `node-midi-out-${i}`;\n\n if (!this.outputPorts.has(id)) {\n const portOutput = new this.MidiModule.Output();\n const port = new NodeMidiOutputPort(i, portName, portOutput);\n this.outputPorts.set(id, port);\n }\n }\n\n if (output.isPortOpen()) {\n output.closePort();\n }\n } catch (err) {\n console.error(\"Error scanning MIDI output ports:\", err);\n }\n }\n\n *inputs(): IterableIterator<IMidiInputPort> {\n for (const [, port] of this.inputPorts) {\n yield port;\n }\n }\n\n *outputs(): IterableIterator<IMidiOutputPort> {\n for (const [, port] of this.outputPorts) {\n yield port;\n }\n }\n\n addEventListener(\n _event: \"statechange\",\n _callback: (port: IMidiPort) => void,\n ): void {\n // node-midi doesn't support hot-plugging detection\n console.warn(\n \"Hot-plug detection not supported with node-midi adapter. Restart required for new devices.\",\n );\n }\n}\n\nexport default class NodeMidiAdapter implements IMidiAdapter {\n async requestMIDIAccess(): Promise<IMidiAccess | null> {\n try {\n // Dynamic import to avoid bundling in browser builds\n const midi = (await import(\"@julusian/midi\")) as\n | NodeMidiModule\n | { default: NodeMidiModule };\n const midiModule = \"default\" in midi ? midi.default : midi;\n return new NodeMidiAccess(midiModule);\n } catch (err) {\n console.error(\"Error loading node-midi:\", err);\n return null;\n }\n }\n\n isSupported(): boolean {\n // Check if we're in Node.js environment\n return isNode();\n }\n}\n","/**\n * Web MIDI API adapter for browsers\n */\nimport type {\n IMidiAccess,\n IMidiAdapter,\n IMidiInputPort,\n IMidiOutputPort,\n IMidiPort,\n MidiMessageCallback,\n} from \"./types\";\n\nclass WebMidiInputPort implements IMidiInputPort {\n private input: MIDIInput;\n private callbacks = new Set<MidiMessageCallback>();\n private handler: ((e: MIDIMessageEvent) => void) | null = null;\n\n constructor(input: MIDIInput) {\n this.input = input;\n }\n\n get id(): string {\n return this.input.id;\n }\n\n get name(): string {\n return this.input.name ?? `Device ${this.input.id}`;\n }\n\n get type() {\n return this.input.type;\n }\n\n get state(): \"connected\" | \"disconnected\" {\n return this.input.state as \"connected\" | \"disconnected\";\n }\n\n addEventListener(callback: MidiMessageCallback): void {\n if (this.callbacks.size === 0) {\n this.handler = (e: MIDIMessageEvent) => {\n if (!e.data) return;\n\n const event = {\n data: e.data,\n timeStamp: e.timeStamp,\n };\n\n this.callbacks.forEach((cb) => {\n cb(event);\n });\n };\n this.input.addEventListener(\"midimessage\", this.handler);\n }\n this.callbacks.add(callback);\n }\n\n removeEventListener(callback: MidiMessageCallback): void {\n this.callbacks.delete(callback);\n\n if (this.callbacks.size === 0 && this.handler) {\n this.input.removeEventListener(\"midimessage\", this.handler);\n this.handler = null;\n }\n }\n}\n\nclass WebMidiOutputPort implements IMidiOutputPort {\n private output: MIDIOutput;\n\n constructor(output: MIDIOutput) {\n this.output = output;\n }\n\n get id(): string {\n return this.output.id;\n }\n\n get name(): string {\n return this.output.name ?? `Device ${this.output.id}`;\n }\n\n get type() {\n return this.output.type;\n }\n\n get state(): \"connected\" | \"disconnected\" {\n return this.output.state as \"connected\" | \"disconnected\";\n }\n\n send(data: number[] | Uint8Array, timestamp?: number): void {\n this.output.send(data, timestamp);\n }\n}\n\nclass WebMidiAccess implements IMidiAccess {\n private midiAccess: MIDIAccess;\n private inputCache = new Map<string, WebMidiInputPort>();\n private outputCache = new Map<string, WebMidiOutputPort>();\n\n constructor(midiAccess: MIDIAccess) {\n this.midiAccess = midiAccess;\n }\n\n *inputs(): IterableIterator<IMidiInputPort> {\n for (const [, input] of this.midiAccess.inputs) {\n if (!this.inputCache.has(input.id)) {\n this.inputCache.set(input.id, new WebMidiInputPort(input));\n }\n yield this.inputCache.get(input.id)!;\n }\n }\n\n *outputs(): IterableIterator<IMidiOutputPort> {\n for (const [, output] of this.midiAccess.outputs) {\n if (!this.outputCache.has(output.id)) {\n this.outputCache.set(output.id, new WebMidiOutputPort(output));\n }\n yield this.outputCache.get(output.id)!;\n }\n }\n\n addEventListener(\n event: \"statechange\",\n callback: (port: IMidiPort) => void,\n ): void {\n this.midiAccess.addEventListener(event, (e) => {\n const port = e.port;\n if (!port) return;\n\n const midiPort: IMidiPort = {\n id: port.id,\n name: port.name ?? `Device ${port.id}`,\n state: port.state as \"connected\" | \"disconnected\",\n type: port.type as \"input\" | \"output\",\n };\n\n if (port.type === \"input\") {\n const input = port as MIDIInput;\n if (!this.inputCache.has(input.id)) {\n this.inputCache.set(input.id, new WebMidiInputPort(input));\n }\n } else {\n const output = port as MIDIOutput;\n if (!this.outputCache.has(output.id)) {\n this.outputCache.set(output.id, new WebMidiOutputPort(output));\n }\n }\n\n callback(midiPort);\n });\n }\n}\n\nexport default class WebMidiAdapter implements IMidiAdapter {\n async requestMIDIAccess(): Promise<IMidiAccess | null> {\n try {\n if (\n typeof navigator === \"undefined\" ||\n typeof navigator.requestMIDIAccess !== \"function\"\n ) {\n return null;\n }\n\n const midiAccess = await navigator.requestMIDIAccess();\n return new WebMidiAccess(midiAccess);\n } catch (err) {\n console.error(\"Error enabling Web MIDI API:\", err);\n return null;\n }\n }\n\n isSupported(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n typeof navigator.requestMIDIAccess === \"function\"\n );\n }\n}\n","/**\n * Utilities for fuzzy matching MIDI device names across different platforms\n *\n * MIDI devices can have different names in browser vs Node.js:\n * - Browser: \"Launchkey Mini MK3 MIDI 1\"\n * - Node.js: \"Launchkey Mini MK3:Launchkey Mini MK3 MIDI 1 20:0\"\n *\n * This module provides fuzzy matching to find the same physical device\n * across platforms based on name similarity.\n */\n\n/**\n * Normalizes a MIDI device name by:\n * - Converting to lowercase\n * - Removing common suffixes/prefixes\n * - Removing port numbers and ALSA identifiers\n * - Removing extra whitespace\n * - Removing special characters\n */\nexport function normalizeDeviceName(name: string): string {\n let normalized = name.toLowerCase();\n\n // First, remove ALSA port numbers (e.g., \"20:0\" at the very end)\n // Do this BEFORE splitting by colons\n normalized = normalized.replace(/\\s+\\d+:\\d+\\s*$/g, \"\");\n\n // Remove colon-separated duplicates (Node.js format: \"Device:Device Port\")\n const parts = normalized.split(\":\");\n if (parts.length > 1) {\n // Take the longest part as it usually has more info\n normalized = parts.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n }\n\n // Remove common port descriptors (but keep the number if it's part of the device name)\n // This regex only matches MIDI/Input/Output/Port at the END of the string\n normalized = normalized.replace(\n /\\s+(midi|input|output|port)(\\s+\\d+)?$/gi,\n \"\",\n );\n\n // Remove \"device\" prefix if present at the start\n normalized = normalized.replace(/^device\\s+/gi, \"\");\n\n // Remove multiple spaces\n normalized = normalized.replace(/\\s+/g, \" \").trim();\n\n return normalized;\n}\n\n/**\n * Extracts core device name tokens for matching\n * Removes generic words and focuses on manufacturer/model identifiers\n */\nexport function extractCoreTokens(name: string): string[] {\n const normalized = normalizeDeviceName(name);\n\n // Split into tokens\n const tokens = normalized.split(/[\\s\\-_:]+/);\n\n // Filter out very short tokens and common generic words\n const genericWords = new Set([\n \"midi\",\n \"input\",\n \"output\",\n \"port\",\n \"device\",\n \"in\",\n \"out\",\n ]);\n\n return tokens.filter((token) => token.length > 1 && !genericWords.has(token));\n}\n\n/**\n * Calculates Levenshtein distance between two strings\n * Used for fuzzy string matching\n */\nfunction levenshteinDistance(str1: string, str2: string): number {\n const len1 = str1.length;\n const len2 = str2.length;\n const matrix: number[][] = [];\n\n // Initialize matrix\n for (let i = 0; i <= len1; i++) {\n matrix[i] = [i];\n }\n for (let j = 0; j <= len2; j++) {\n if (matrix[0]) {\n matrix[0][j] = j;\n }\n }\n\n // Fill matrix\n for (let i = 1; i <= len1; i++) {\n for (let j = 1; j <= len2; j++) {\n const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;\n const prevRow = matrix[i - 1];\n const currRow = matrix[i];\n const prevCell = currRow?.[j - 1];\n const diagCell = prevRow?.[j - 1];\n\n const prevCellInRow = prevRow?.[j];\n if (\n currRow &&\n prevCellInRow !== undefined &&\n prevCell !== undefined &&\n diagCell !== undefined\n ) {\n currRow[j] = Math.min(\n prevCellInRow + 1, // deletion\n prevCell + 1, // insertion\n diagCell + cost, // substitution\n );\n }\n }\n }\n\n const lastRow = matrix[len1];\n return lastRow?.[len2] ?? 0;\n}\n\n/**\n * Calculates similarity score between two device names\n * Returns a score between 0 (no match) and 1 (perfect match)\n *\n * Uses multiple strategies:\n * 1. Exact normalized match (score: 1.0)\n * 2. Token overlap (Jaccard similarity)\n * 3. String similarity (based on Levenshtein distance)\n * 4. Substring matching\n */\nexport function calculateSimilarity(name1: string, name2: string): number {\n const normalized1 = normalizeDeviceName(name1);\n const normalized2 = normalizeDeviceName(name2);\n\n // Exact match after normalization\n if (normalized1 === normalized2) {\n return 1.0;\n }\n\n const tokens1 = new Set(extractCoreTokens(name1));\n const tokens2 = new Set(extractCoreTokens(name2));\n\n // If no tokens extracted, fall back to pure string similarity\n if (tokens1.size === 0 || tokens2.size === 0) {\n const maxLen = Math.max(normalized1.length, normalized2.length);\n if (maxLen === 0) return 0;\n const distance = levenshteinDistance(normalized1, normalized2);\n return 1 - distance / maxLen;\n }\n\n // Calculate Jaccard similarity for tokens\n const intersection = new Set([...tokens1].filter((x) => tokens2.has(x)));\n const union = new Set([...tokens1, ...tokens2]);\n const jaccardScore = intersection.size / union.size;\n\n // Calculate string similarity\n const maxLen = Math.max(normalized1.length, normalized2.length);\n const distance = levenshteinDistance(normalized1, normalized2);\n const stringScore = 1 - distance / maxLen;\n\n // Check for substring matches\n const substringScore =\n normalized1.includes(normalized2) || normalized2.includes(normalized1)\n ? 0.8\n : 0;\n\n // Weighted combination of scores\n // Token overlap is most important, then string similarity, then substring\n const finalScore =\n jaccardScore * 0.5 + stringScore * 0.3 + substringScore * 0.2;\n\n return finalScore;\n}\n\n/**\n * Finds the best matching device name from a list of candidates\n * Returns the best match and its confidence score\n *\n * @param targetName - The name to match against\n * @param candidateNames - List of possible matches\n * @param threshold - Minimum similarity score to consider a match (default: 0.6)\n * @returns Best match and score, or null if no match above threshold\n */\nexport function findBestMatch(\n targetName: string,\n candidateNames: string[],\n threshold = 0.6,\n): { name: string; score: number } | null {\n let bestMatch: { name: string; score: number } | null = null;\n\n for (const candidateName of candidateNames) {\n const score = calculateSimilarity(targetName, candidateName);\n\n if (score >= threshold && (!bestMatch || score > bestMatch.score)) {\n bestMatch = { name: candidateName, score };\n }\n }\n\n return bestMatch;\n}\n","import { Context } from \"@blibliki/utils\";\nimport ComputerKeyboardDevice from \"./ComputerKeyboardDevice\";\nimport MidiInputDevice from \"./MidiInputDevice\";\nimport MidiOutputDevice from \"./MidiOutputDevice\";\nimport { createMidiAdapter, type IMidiAccess } from \"./adapters\";\nimport { findBestMatch } from \"./deviceMatcher\";\n\ntype ListenerCallback = (device: MidiInputDevice | MidiOutputDevice) => void;\n\nexport default class MidiDeviceManager {\n inputDevices = new Map<string, MidiInputDevice | ComputerKeyboardDevice>();\n outputDevices = new Map<string, MidiOutputDevice>();\n private initialized = false;\n private listeners: ListenerCallback[] = [];\n private context: Readonly<Context>;\n private midiAccess: IMidiAccess | null = null;\n private adapter = createMidiAdapter();\n\n constructor(context: Context) {\n this.context = context;\n this.addComputerKeyboard();\n }\n\n async initialize() {\n await this.initializeDevices();\n\n this.listenChanges();\n this.initialized = true;\n }\n\n find(\n id: string,\n ): MidiInputDevice | ComputerKeyboardDevice | MidiOutputDevice | undefined {\n return this.findInput(id) ?? this.findOutput(id);\n }\n\n findByName(\n name: string,\n ): MidiInputDevice | ComputerKeyboardDevice | MidiOutputDevice | undefined {\n return this.findInputByName(name) ?? this.findOutputByName(name);\n }\n\n findByFuzzyName(\n name: string,\n threshold = 0.6,\n ): MidiInputDevice | ComputerKeyboardDevice | MidiOutputDevice | undefined {\n const input = this.findInputByFuzzyName(name, threshold);\n const output = this.findOutputByFuzzyName(name, threshold);\n\n if (!input) return output?.device;\n if (!output) return input.device;\n\n return input.score > output.score ? input.device : output.device;\n }\n\n findInput(id: string): MidiInputDevice | ComputerKeyboardDevice | undefined {\n return this.inputDevices.get(id);\n }\n\n findInputByName(\n name: string,\n ): MidiInputDevice | ComputerKeyboardDevice | undefined {\n return Array.from(this.inputDevices.values()).find((d) => d.name === name);\n }\n\n findOutput(id: string): MidiOutputDevice | undefined {\n return this.outputDevices.get(id);\n }\n\n findOutputByName(name: string): MidiOutputDevice | undefined {\n return Array.from(this.outputDevices.values()).find((d) => d.name === name);\n }\n\n /**\n * Finds a device using fuzzy name matching\n * Useful for matching devices across browser/Node.js environments where names differ\n *\n * @param targetName - The device name to match\n * @param threshold - Minimum similarity score (0-1, default: 0.6)\n * @returns The best matching device and confidence score, or null\n */\n findInputByFuzzyName(\n targetName: string,\n threshold = 0.6,\n ): {\n device: MidiInputDevice | ComputerKeyboardDevice;\n score: number;\n } | null {\n const deviceEntries = Array.from(this.inputDevices.values());\n const candidateNames = deviceEntries.map((d) => d.name);\n\n const match = findBestMatch(targetName, candidateNames, threshold);\n\n if (!match) return null;\n\n const device = deviceEntries.find((d) => d.name === match.name);\n\n return device ? { device, score: match.score } : null;\n }\n\n findOutputByFuzzyName(\n targetName: string,\n threshold = 0.6,\n ): { device: MidiOutputDevice; score: number } | null {\n const deviceEntries = Array.from(this.outputDevices.values());\n const candidateNames = deviceEntries.map((d) => d.name);\n\n const match = findBestMatch(targetName, candidateNames, threshold);\n\n if (!match) return null;\n\n const device = deviceEntries.find((d) => d.name === match.name);\n\n return device ? { device, score: match.score } : null;\n }\n\n addListener(callback: ListenerCallback) {\n this.listeners.push(callback);\n }\n\n private async initializeDevices() {\n if (this.initialized) return;\n\n try {\n if (!this.adapter.isSupported()) {\n console.warn(\"MIDI is not supported on this platform\");\n return;\n }\n\n this.midiAccess = await this.adapter.requestMIDIAccess();\n\n if (!this.midiAccess) {\n console.error(\"Failed to get MIDI access\");\n return;\n }\n\n for (const input of this.midiAccess.inputs()) {\n if (!this.inputDevices.has(input.id)) {\n this.inputDevices.set(\n input.id,\n new MidiInputDevice(input, this.context),\n );\n }\n }\n\n for (const output of this.midiAccess.outputs()) {\n if (!this.outputDevices.has(output.id)) {\n this.outputDevices.set(output.id, new MidiOutputDevice(output));\n }\n }\n } catch (err) {\n console.error(\"Error enabling MIDI:\", err);\n }\n }\n\n private addComputerKeyboard() {\n if (typeof document === \"undefined\") return;\n\n const computerKeyboardDevice = new ComputerKeyboardDevice(this.context);\n this.inputDevices.set(computerKeyboardDevice.id, computerKeyboardDevice);\n }\n\n private listenChanges() {\n if (!this.midiAccess) return;\n\n this.midiAccess.addEventListener(\"statechange\", (port) => {\n if (port.state === \"connected\") {\n // Device connected\n if (port.type === \"input\") {\n if (this.inputDevices.has(port.id)) return;\n\n // Find the actual input port from midiAccess\n for (const input of this.midiAccess!.inputs()) {\n if (input.id === port.id) {\n const device = new MidiInputDevice(input, this.context);\n this.inputDevices.set(device.id, device);\n\n this.listeners.forEach((listener) => {\n listener(device);\n });\n break;\n }\n }\n } else {\n // Output device connected\n if (this.outputDevices.has(port.id)) return;\n\n // Find the actual output port from midiAccess\n for (const output of this.midiAccess!.outputs()) {\n if (output.id === port.id) {\n const device = new MidiOutputDevice(output);\n this.outputDevices.set(device.id, device);\n break;\n }\n }\n }\n } else {\n // Device disconnected\n if (port.type === \"input\") {\n const device = this.inputDevices.get(port.id);\n if (!device) return;\n if (device instanceof ComputerKeyboardDevice) return;\n\n device.disconnect();\n this.inputDevices.delete(device.id);\n\n this.listeners.forEach((listener) => {\n listener(device);\n });\n } else {\n // Output device disconnected\n const device = this.outputDevices.get(port.id);\n if (!device) return;\n\n device.disconnect();\n this.outputDevices.delete(device.id);\n }\n }\n });\n }\n}\n","import { assertNever } from \"@blibliki/utils\";\nimport { IModule, Module } from \"@/core\";\nimport { IPolyModuleConstructor } from \"@/core/module/PolyModule\";\nimport VoiceScheduler, {\n IVoiceSchedulerProps,\n voiceSchedulerPropSchema,\n} from \"@/core/module/VoiceScheduler\";\nimport Chorus, { chorusPropSchema, IChorusProps } from \"./Chorus\";\nimport Constant, { constantPropSchema, IConstantProps } from \"./Constant\";\nimport Delay, { delayPropSchema, IDelayProps } from \"./Delay\";\nimport Distortion, {\n distortionPropSchema,\n IDistortionProps,\n} from \"./Distortion\";\nimport CustomEnvelope, {\n customEnvelopePropSchema,\n ICustomEnvelopeProps,\n} from \"./Envelope\";\nimport Filter, { filterPropSchema, IFilterProps } from \"./Filter\";\nimport Gain, { gainPropSchema, IGainProps } from \"./Gain\";\nimport Inspector, { IInspectorProps, inspectorPropSchema } from \"./Inspector\";\nimport LFO, { ILFOProps, lfoPropSchema } from \"./LFO\";\nimport LegacyEnvelope, {\n envelopePropSchema as legacyEnvelopePropSchema,\n IEnvelopeProps as ILegacyEnvelopeProps,\n} from \"./LegacyEnvelope\";\nimport Master, { IMasterProps, masterPropSchema } from \"./Master\";\nimport MidiInput, { IMidiInputProps, midiInputPropSchema } from \"./MidiInput\";\nimport MidiMapper, {\n IMidiMapperProps,\n midiMapperPropSchema,\n} from \"./MidiMapper\";\nimport MidiOutput, {\n IMidiOutputProps,\n midiOutputPropSchema,\n} from \"./MidiOutput\";\nimport Noise, { INoiseProps, noisePropSchema } from \"./Noise\";\nimport Oscillator, {\n IOscillatorProps,\n oscillatorPropSchema,\n} from \"./Oscillator\";\nimport Reverb, { IReverbProps, reverbPropSchema } from \"./Reverb\";\nimport Scale, { IScaleProps, scalePropSchema } from \"./Scale\";\nimport StepSequencer, {\n IStepSequencerProps,\n IStepSequencerState,\n stepSequencerPropSchema,\n} from \"./StepSequencer\";\nimport StereoPanner, {\n IStereoPannerProps,\n stereoPannerPropSchema,\n} from \"./StereoPanner\";\nimport VirtualMidi, {\n IVirtualMidiProps,\n virtualMidiPropSchema,\n} from \"./VirtualMidi\";\n\nexport enum ModuleType {\n Master = \"Master\",\n Oscillator = \"Oscillator\",\n Gain = \"Gain\",\n MidiInput = \"MidiInput\",\n MidiOutput = \"MidiOutput\",\n LegacyEnvelope = \"LegacyEnvelope\", // BACKWARD_COMPAT: Legacy envelope for old patches\n Envelope = \"Envelope\",\n Filter = \"Filter\",\n Scale = \"Scale\",\n StereoPanner = \"StereoPanner\",\n Inspector = \"Inspector\",\n Chorus = \"Chorus\",\n Constant = \"Constant\",\n Delay = \"Delay\",\n Distortion = \"Distortion\",\n MidiMapper = \"MidiMapper\",\n VirtualMidi = \"VirtualMidi\",\n StepSequencer = \"StepSequencer\",\n VoiceScheduler = \"VoiceScheduler\",\n LFO = \"LFO\",\n Noise = \"Noise\",\n Reverb = \"Reverb\",\n}\n\nexport type ModuleTypeToPropsMapping = {\n [ModuleType.Oscillator]: IOscillatorProps;\n [ModuleType.Gain]: IGainProps;\n [ModuleType.Master]: IMasterProps;\n [ModuleType.MidiInput]: IMidiInputProps;\n [ModuleType.MidiOutput]: IMidiOutputProps;\n [ModuleType.LegacyEnvelope]: ILegacyEnvelopeProps; // BACKWARD_COMPAT: Legacy envelope for old patches\n [ModuleType.Envelope]: ICustomEnvelopeProps;\n [ModuleType.Filter]: IFilterProps;\n [ModuleType.Scale]: IScaleProps;\n [ModuleType.StereoPanner]: IStereoPannerProps;\n [ModuleType.Inspector]: IInspectorProps;\n [ModuleType.Chorus]: IChorusProps;\n [ModuleType.Constant]: IConstantProps;\n [ModuleType.Delay]: IDelayProps;\n [ModuleType.Distortion]: IDistortionProps;\n [ModuleType.MidiMapper]: IMidiMapperProps;\n [ModuleType.VirtualMidi]: IVirtualMidiProps;\n [ModuleType.StepSequencer]: IStepSequencerProps;\n [ModuleType.VoiceScheduler]: IVoiceSchedulerProps;\n [ModuleType.LFO]: ILFOProps;\n [ModuleType.Noise]: INoiseProps;\n [ModuleType.Reverb]: IReverbProps;\n};\n\nexport type ModuleTypeToStateMapping = {\n [ModuleType.Oscillator]: never;\n [ModuleType.Gain]: never;\n [ModuleType.Master]: never;\n [ModuleType.MidiInput]: never;\n [ModuleType.MidiOutput]: never;\n [ModuleType.LegacyEnvelope]: never;\n [ModuleType.Envelope]: never;\n [ModuleType.Filter]: never;\n [ModuleType.Scale]: never;\n [ModuleType.StereoPanner]: never;\n [ModuleType.Inspector]: never;\n [ModuleType.Chorus]: never;\n [ModuleType.Constant]: never;\n [ModuleType.Delay]: never;\n [ModuleType.Distortion]: never;\n [ModuleType.MidiMapper]: never;\n [ModuleType.VirtualMidi]: never;\n [ModuleType.StepSequencer]: IStepSequencerState;\n [ModuleType.VoiceScheduler]: never;\n [ModuleType.LFO]: never;\n [ModuleType.Noise]: never;\n [ModuleType.Reverb]: never;\n};\n\nexport type ModuleTypeToModuleMapping = {\n [ModuleType.Oscillator]: Oscillator;\n [ModuleType.Gain]: Gain;\n [ModuleType.Master]: Master;\n [ModuleType.MidiInput]: MidiInput;\n [ModuleType.MidiOutput]: MidiOutput;\n [ModuleType.LegacyEnvelope]: LegacyEnvelope; // BACKWARD_COMPAT: Legacy envelope for old patches\n [ModuleType.Envelope]: CustomEnvelope;\n [ModuleType.Filter]: Filter;\n [ModuleType.Scale]: Scale;\n [ModuleType.StereoPanner]: StereoPanner;\n [ModuleType.Inspector]: Inspector;\n [ModuleType.Chorus]: Chorus;\n [ModuleType.Constant]: Constant;\n [ModuleType.Delay]: Delay;\n [ModuleType.Distortion]: Distortion;\n [ModuleType.MidiMapper]: MidiMapper;\n [ModuleType.VirtualMidi]: VirtualMidi;\n [ModuleType.StepSequencer]: StepSequencer;\n [ModuleType.VoiceScheduler]: VoiceScheduler;\n [ModuleType.LFO]: LFO;\n [ModuleType.Noise]: Noise;\n [ModuleType.Reverb]: Reverb;\n};\n\nexport const moduleSchemas = {\n [ModuleType.Oscillator]: oscillatorPropSchema,\n [ModuleType.Gain]: gainPropSchema,\n [ModuleType.Master]: masterPropSchema,\n [ModuleType.MidiInput]: midiInputPropSchema,\n [ModuleType.MidiOutput]: midiOutputPropSchema,\n [ModuleType.LegacyEnvelope]: legacyEnvelopePropSchema, // BACKWARD_COMPAT: Legacy envelope for old patches\n [ModuleType.Envelope]: customEnvelopePropSchema,\n [ModuleType.Filter]: filterPropSchema,\n [ModuleType.Scale]: scalePropSchema,\n [ModuleType.StereoPanner]: stereoPannerPropSchema,\n [ModuleType.Inspector]: inspectorPropSchema,\n [ModuleType.Chorus]: chorusPropSchema,\n [ModuleType.Constant]: constantPropSchema,\n [ModuleType.Delay]: delayPropSchema,\n [ModuleType.Distortion]: distortionPropSchema,\n [ModuleType.MidiMapper]: midiMapperPropSchema,\n [ModuleType.VirtualMidi]: virtualMidiPropSchema,\n [ModuleType.StepSequencer]: stepSequencerPropSchema,\n [ModuleType.VoiceScheduler]: voiceSchedulerPropSchema,\n [ModuleType.LFO]: lfoPropSchema,\n [ModuleType.Noise]: noisePropSchema,\n [ModuleType.Reverb]: reverbPropSchema,\n};\n\nexport type { IOscillator } from \"./Oscillator\";\nexport { OscillatorWave } from \"./Oscillator\";\nexport type { IGain } from \"./Gain\";\nexport type { IMaster } from \"./Master\";\nexport type { IMidiInput, IMidiInputProps } from \"./MidiInput\";\nexport type { IMidiOutput, IMidiOutputProps } from \"./MidiOutput\";\nexport type { IStereoPanner } from \"./StereoPanner\";\nexport type {\n IStepSequencer,\n IStepSequencerProps,\n IStepSequencerState,\n IStep,\n IPage,\n IPattern,\n IStepNote,\n IStepCC,\n} from \"./StepSequencer\";\nexport { Resolution, PlaybackMode, stepPropSchema } from \"./StepSequencer\";\nexport type { IMidiMapper, IMidiMapperProps, MidiMapping } from \"./MidiMapper\";\nexport { MidiMappingMode } from \"./MidiMapper\";\nexport type { ILFO, ILFOProps } from \"./LFO\";\nexport { LFOWaveform } from \"./LFO\";\nexport type { INoise } from \"./Noise\";\nexport { NoiseType } from \"./Noise\";\nexport type { IReverb, IReverbProps } from \"./Reverb\";\nexport { ReverbType } from \"./Reverb\";\nexport { DelayTimeMode } from \"./Delay\";\nexport type { IDelay, IDelayProps } from \"./Delay\";\nexport type { IDistortion, IDistortionProps } from \"./Distortion\";\nexport type { IChorus, IChorusProps } from \"./Chorus\";\n\nexport type AnyModule = Module<ModuleType>;\nexport type IAnyModule = IModule<ModuleType>;\n\nexport type ICreateModule<T extends ModuleType> = {\n id?: string;\n name: string;\n moduleType: T;\n props: Partial<ModuleTypeToPropsMapping[T]>;\n};\n\nexport type ModuleParams = {\n [K in ModuleType]: K extends\n | ModuleType.Oscillator\n | ModuleType.Gain\n | ModuleType.LegacyEnvelope\n | ModuleType.Envelope\n | ModuleType.Filter\n | ModuleType.StereoPanner\n | ModuleType.VoiceScheduler\n | ModuleType.Scale\n | ModuleType.LFO\n | ModuleType.Distortion\n ? IPolyModuleConstructor<K>\n : ICreateModule<K>;\n}[ModuleType];\n\nexport function createModule(\n engineId: string,\n params: ModuleParams,\n): ModuleTypeToModuleMapping[keyof ModuleTypeToModuleMapping] {\n switch (params.moduleType) {\n case ModuleType.Oscillator:\n return Oscillator.create(Oscillator, engineId, params);\n case ModuleType.Gain:\n return Gain.create(Gain, engineId, params);\n case ModuleType.Master:\n return Master.create(Master, engineId, params);\n case ModuleType.MidiInput:\n return MidiInput.create(MidiInput, engineId, params);\n case ModuleType.MidiOutput:\n return MidiOutput.create(MidiOutput, engineId, params);\n // BACKWARD_COMPAT: Legacy envelope for old patches\n case ModuleType.LegacyEnvelope:\n return LegacyEnvelope.create(LegacyEnvelope, engineId, params);\n case ModuleType.Envelope:\n return CustomEnvelope.create(CustomEnvelope, engineId, params);\n case ModuleType.Filter:\n return Filter.create(Filter, engineId, params);\n case ModuleType.Scale:\n return Scale.create(Scale, engineId, params);\n case ModuleType.StereoPanner:\n return StereoPanner.create(StereoPanner, engineId, params);\n case ModuleType.Inspector:\n return Inspector.create(Inspector, engineId, params);\n case ModuleType.Chorus:\n return Chorus.create(Chorus, engineId, params);\n case ModuleType.Constant:\n return Constant.create(Constant, engineId, params);\n case ModuleType.Delay:\n return Delay.create(Delay, engineId, params);\n case ModuleType.Distortion:\n return Distortion.create(Distortion, engineId, params);\n case ModuleType.MidiMapper:\n return MidiMapper.create(MidiMapper, engineId, params);\n case ModuleType.VirtualMidi:\n return VirtualMidi.create(VirtualMidi, engineId, params);\n case ModuleType.StepSequencer:\n return StepSequencer.create(StepSequencer, engineId, params);\n case ModuleType.VoiceScheduler:\n return VoiceScheduler.create(VoiceScheduler, engineId, params);\n case ModuleType.LFO:\n return LFO.create(LFO, engineId, params);\n case ModuleType.Noise:\n return Noise.create(Noise, engineId, params);\n case ModuleType.Reverb:\n return Reverb.create(Reverb, engineId, params);\n default:\n assertNever(params);\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { EmptyObject } from \"@blibliki/utils\";\nimport { ICreateModule, ModuleType } from \"@/modules\";\nimport { MidiOutput } from \"../IO\";\nimport MidiEvent, { MidiEventType } from \"../midi/MidiEvent\";\nimport { ModulePropSchema } from \"../schema\";\nimport { IModuleConstructor, Module } from \"./Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"./PolyModule\";\n\nexport type IVoiceSchedulerProps = EmptyObject;\nexport const voiceSchedulerPropSchema: ModulePropSchema<IVoiceSchedulerProps> =\n {};\nconst DEFAULT_PROPS = {};\n\ninterface OccupationRange {\n noteName: string;\n startTime: ContextTime;\n endTime: ContextTime; // Can be Infinity if noteOff hasn't been triggered yet\n}\n\nclass Voice extends Module<ModuleType.VoiceScheduler> {\n declare audioNode: undefined;\n activeNote: string | null = null;\n triggeredAt: ContextTime = 0;\n // Track all current and future occupation ranges\n private occupationRanges: OccupationRange[] = [];\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.VoiceScheduler>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n }\n\n /**\n * Check if this voice is occupied at a given time\n */\n isOccupiedAt(time: ContextTime): boolean {\n return this.occupationRanges.some(\n (range) => time >= range.startTime && time < range.endTime,\n );\n }\n\n /**\n * Get the earliest end time of all occupation ranges at or after the given time\n * Returns Infinity if the voice has infinite occupation\n */\n getEarliestEndTimeAfter(time: ContextTime): ContextTime {\n const relevantRanges = this.occupationRanges.filter(\n (range) => range.endTime > time,\n );\n\n if (relevantRanges.length === 0) return -Infinity;\n\n return Math.min(...relevantRanges.map((range) => range.endTime));\n }\n\n /**\n * Clean up past occupation ranges that are no longer needed\n */\n private cleanupPastRanges(currentTime: ContextTime) {\n this.occupationRanges = this.occupationRanges.filter(\n (range) => range.endTime > currentTime || range.endTime === Infinity,\n );\n }\n\n /**\n * Clear all occupation ranges\n * Useful when resetting or stopping playback\n */\n clearOccupationRanges() {\n this.occupationRanges = [];\n }\n\n midiTriggered = (midiEvent: MidiEvent) => {\n const { triggeredAt, note, type } = midiEvent;\n\n if (!note) return;\n const noteName = note.fullName;\n\n this.cleanupPastRanges(triggeredAt);\n\n // Determine if this is a future event (more than 10ms ahead)\n const currentTime = this.context.audioContext.currentTime;\n const isFutureEvent = triggeredAt - currentTime > 0.01;\n\n switch (type) {\n case MidiEventType.noteOn: {\n this.activeNote = noteName;\n this.triggeredAt = triggeredAt;\n\n // Only add occupation ranges for future events\n // Real-time events use activeNote field only\n if (isFutureEvent) {\n this.occupationRanges.push({\n noteName,\n startTime: triggeredAt,\n endTime: Infinity,\n });\n }\n\n break;\n }\n case MidiEventType.noteOff: {\n this.activeNote = null;\n\n // Always try to close any open occupation range for this note\n // This handles both scheduled future events and cleanup when stopping\n const range = this.occupationRanges.find(\n (r) => r.noteName === noteName && r.endTime === Infinity,\n );\n if (range) {\n range.endTime = triggeredAt;\n }\n break;\n }\n default:\n throw Error(\"This type is not a note\");\n }\n };\n}\n\nexport default class VoiceScheduler extends PolyModule<ModuleType.VoiceScheduler> {\n declare audioModules: Voice[];\n midiOutput!: MidiOutput;\n\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.VoiceScheduler>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.VoiceScheduler>,\n ) => new Voice(engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerInputs();\n this.registerOutputs();\n }\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n let voice: Voice | undefined;\n\n switch (midiEvent.type) {\n case MidiEventType.noteOn:\n voice = this.findFreeVoice(midiEvent.triggeredAt);\n\n break;\n case MidiEventType.noteOff:\n voice = this.audioModules.find(\n (v) => v.activeNote === midiEvent.note!.fullName,\n );\n break;\n case MidiEventType.cc:\n this.midiOutput.onMidiEvent(midiEvent);\n return;\n default:\n throw Error(\"This type is not a note\");\n }\n\n if (!voice) return;\n\n voice.midiTriggered(midiEvent);\n midiEvent.voiceNo = voice.voiceNo;\n this.midiOutput.onMidiEvent(midiEvent);\n };\n\n private findFreeVoice(targetTime: ContextTime): Voice {\n const currentTime = this.context.audioContext.currentTime;\n // Consider it real-time if within 10ms of current time\n const isRealTime = Math.abs(targetTime - currentTime) < 0.01;\n\n let voice: Voice | undefined;\n\n if (isRealTime) {\n // For real-time events, use simple activeNote check (original behavior)\n // This avoids issues with residual occupation ranges\n voice = this.audioModules.find((v) => !v.activeNote);\n } else {\n // For future events, use occupation range system\n voice = this.audioModules.find((v) => !v.isOccupiedAt(targetTime));\n }\n\n // If no available voice, steal one based on the strategy:\n // Primary: voice with earliest end time at or after target time\n // Secondary: among voices with similar end times, choose oldest (earliest triggeredAt)\n if (!voice) {\n if (isRealTime) {\n // For real-time, use original voice stealing strategy\n const sorted = this.audioModules.sort((a, b) => {\n return a.triggeredAt - b.triggeredAt;\n });\n voice = sorted[0];\n } else {\n // For future events, use occupation-aware strategy\n const sorted = this.audioModules.sort((a, b) => {\n const aEndTime = a.getEarliestEndTimeAfter(targetTime);\n const bEndTime = b.getEarliestEndTimeAfter(targetTime);\n\n // Primary sort by end time\n if (aEndTime !== bEndTime) {\n return aEndTime - bEndTime;\n }\n\n // Secondary sort by triggered time (oldest first)\n return a.triggeredAt - b.triggeredAt;\n });\n voice = sorted[0];\n }\n\n if (!voice) {\n throw new Error(\"No voices available in voice scheduler\");\n }\n\n // Important: Send a noteOff event for the stolen voice's current note\n // This ensures the envelope releases properly before the new note starts\n if (voice.activeNote) {\n const stolenNoteName = voice.activeNote;\n // Always release the stolen note at the current time for immediate audio release\n const releaseTime = this.context.audioContext.currentTime;\n const noteOffEvent = MidiEvent.fromNote(\n stolenNoteName,\n false, // noteOn = false means noteOff\n releaseTime,\n );\n noteOffEvent.voiceNo = voice.voiceNo;\n\n // Trigger the note off on this voice before reusing it\n voice.midiTriggered(noteOffEvent);\n this.midiOutput.onMidiEvent(noteOffEvent);\n }\n }\n\n return voice;\n }\n\n /**\n * Clear all occupation ranges for all voices\n * Call this when stopping playback or resetting the scheduler\n */\n clearAllOccupationRanges() {\n this.audioModules.forEach((voice) => {\n voice.clearOccupationRanges();\n });\n }\n\n private registerInputs() {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n }\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi out\" });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { ModulePropSchema } from \"@/core\";\nimport { IModule, Module, SetterHooks } from \"@/core/module/Module\";\nimport { WetDryMixer } from \"@/utils\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IChorusProps = {\n rate: number; // 0.1-10 Hz (LFO frequency)\n depth: number; // 0-1 (modulation depth)\n mix: number; // 0-1 (dry/wet blend)\n feedback: number; // 0-0.95 (feedback amount)\n};\n\nexport type IChorus = IModule<ModuleType.Chorus>;\n\nexport const chorusPropSchema: ModulePropSchema<IChorusProps> = {\n rate: {\n kind: \"number\",\n min: 0.1,\n max: 10,\n step: 0.1,\n exp: 2, // Exponential scaling for better low-frequency control\n label: \"Rate\",\n },\n depth: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Depth\",\n },\n mix: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Mix\",\n },\n feedback: {\n kind: \"number\",\n min: 0,\n max: 0.95,\n step: 0.01,\n label: \"Feedback\",\n },\n};\n\nconst DEFAULT_PROPS: IChorusProps = {\n rate: 0.5, // 0.5 Hz (slow modulation)\n depth: 0.5, // Medium depth\n mix: 0.5, // 50/50 blend\n feedback: 0.2, // Subtle feedback\n};\n\nexport default class Chorus\n extends Module<ModuleType.Chorus>\n implements\n Pick<\n SetterHooks<IChorusProps>,\n | \"onAfterSetRate\"\n | \"onAfterSetDepth\"\n | \"onAfterSetMix\"\n | \"onAfterSetFeedback\"\n >\n{\n declare audioNode: GainNode; // Input node (inherited from Module)\n private outputNode: GainNode; // Final output from mixer\n private feedbackGain: GainNode; // Feedback amount\n private delayLeft: DelayNode; // First chorus voice\n private delayRight: DelayNode; // Second chorus voice\n private lfoLeft: OscillatorNode; // LFO for left voice\n private lfoRight: OscillatorNode; // LFO for right voice\n private depthLeft: GainNode; // Modulation depth for left\n private depthRight: GainNode; // Modulation depth for right\n private merger: ChannelMergerNode; // Combine to stereo\n private wetDryMixer: WetDryMixer; // Wet/dry blending\n private rateModGain: GainNode; // External rate modulation input\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Chorus>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n const audioNodeConstructor = (context: Context) =>\n new GainNode(context.audioContext, { gain: 1 });\n\n super(engineId, {\n ...params,\n audioNodeConstructor,\n props,\n });\n\n // Create audio nodes\n const audioContext = this.context.audioContext;\n\n this.feedbackGain = audioContext.createGain();\n this.delayLeft = audioContext.createDelay(0.1); // Max 0.1s delay\n this.delayRight = audioContext.createDelay(0.1);\n this.lfoLeft = audioContext.createOscillator();\n this.lfoRight = audioContext.createOscillator();\n this.depthLeft = audioContext.createGain();\n this.depthRight = audioContext.createGain();\n this.merger = audioContext.createChannelMerger(2);\n this.wetDryMixer = new WetDryMixer(this.context);\n this.rateModGain = audioContext.createGain();\n\n // Configure LFOs\n this.lfoLeft.type = \"sine\";\n this.lfoRight.type = \"sine\";\n\n // Connect audio graph\n // Dry path\n this.wetDryMixer.connectInput(this.audioNode);\n\n // Rate modulation input connects to both LFO frequencies\n this.rateModGain.connect(this.lfoLeft.frequency);\n this.rateModGain.connect(this.lfoRight.frequency);\n\n // LFO modulation to delay times\n this.lfoLeft.connect(this.depthLeft);\n this.depthLeft.connect(this.delayLeft.delayTime);\n this.lfoRight.connect(this.depthRight);\n this.depthRight.connect(this.delayRight.delayTime);\n\n // Wet path with feedback\n this.audioNode.connect(this.feedbackGain);\n this.feedbackGain.connect(this.delayLeft);\n this.feedbackGain.connect(this.delayRight);\n\n // Delays to merger (stereo)\n this.delayLeft.connect(this.merger, 0, 0); // Left channel\n this.delayRight.connect(this.merger, 0, 1); // Right channel\n\n // Feedback loop\n this.merger.connect(this.feedbackGain);\n\n // Merger to wet path\n this.merger.connect(this.wetDryMixer.getWetInput());\n\n // Output\n this.outputNode = this.wetDryMixer.getOutput();\n\n // Apply initial parameters\n // Base delay time: 20ms typical for chorus\n this.delayLeft.delayTime.value = 0.02;\n this.delayRight.delayTime.value = 0.02;\n\n // Set LFO frequency (rate)\n this.lfoLeft.frequency.value = props.rate;\n this.lfoRight.frequency.value = props.rate;\n\n // Depth: convert (0-1) to seconds (0-0.01s = 0-10ms modulation)\n const depthInSeconds = props.depth * 0.01;\n this.depthLeft.gain.value = depthInSeconds;\n this.depthRight.gain.value = depthInSeconds;\n\n // Feedback\n this.feedbackGain.gain.value = props.feedback;\n\n // Mix\n this.wetDryMixer.setMix(props.mix);\n\n // Start oscillators with phase offset for stereo width\n const now = this.context.audioContext.currentTime;\n this.lfoLeft.start(now);\n // Start right LFO with 180° phase offset (half period)\n const phaseOffset = 1 / (2 * props.rate);\n this.lfoRight.start(now + phaseOffset);\n\n // Register IOs\n this.registerDefaultIOs(\"in\");\n this.registerAdditionalInputs();\n this.registerCustomOutput();\n }\n\n private registerAdditionalInputs() {\n // External rate modulation input\n this.registerAudioInput({\n name: \"rate\",\n getAudioNode: () => this.rateModGain,\n });\n }\n\n private registerCustomOutput() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.outputNode,\n });\n }\n\n onAfterSetRate: SetterHooks<IChorusProps>[\"onAfterSetRate\"] = (value) => {\n this.lfoLeft.frequency.value = value;\n this.lfoRight.frequency.value = value;\n // Note: Phase offset maintained by start time, not adjustable at runtime\n };\n\n onAfterSetDepth: SetterHooks<IChorusProps>[\"onAfterSetDepth\"] = (value) => {\n const depthInSeconds = value * 0.01; // Convert to 0-10ms range\n this.depthLeft.gain.value = depthInSeconds;\n this.depthRight.gain.value = depthInSeconds;\n };\n\n onAfterSetMix: SetterHooks<IChorusProps>[\"onAfterSetMix\"] = (value) => {\n this.wetDryMixer.setMix(value);\n };\n\n onAfterSetFeedback: SetterHooks<IChorusProps>[\"onAfterSetFeedback\"] = (\n value,\n ) => {\n const cappedValue = Math.min(value, 0.95); // Prevent runaway feedback\n this.feedbackGain.gain.value = cappedValue;\n };\n\n dispose() {\n try {\n this.lfoLeft.stop();\n this.lfoRight.stop();\n } catch {\n // Ignore errors if already stopped\n }\n super.dispose();\n }\n}\n","import { Context } from \"@blibliki/utils\";\n\n/**\n * WetDryMixer - Reusable wet/dry signal mixer for audio effects\n *\n * Provides equal-power crossfading between dry (unprocessed) and wet (processed) signals.\n * Used by effects modules to blend the original signal with the effect output.\n *\n * Audio Graph:\n * ```\n * INPUT\n * |\n * +------------+------------+\n * | |\n * [DryGainNode] [WetGainNode]\n * (cos(mix*π/2)) (sin(mix*π/2))\n * | |\n * +------------+------------+\n * |\n * [OutputNode]\n * ```\n *\n * Usage:\n * ```typescript\n * const mixer = new WetDryMixer(context);\n *\n * // Connect input to mixer\n * mixer.connectInput(sourceNode);\n *\n * // Get wet input node and connect effect chain\n * const wetInput = mixer.getWetInput();\n * sourceNode.connect(effectNode);\n * effectNode.connect(wetInput);\n *\n * // Set mix amount\n * mixer.setMix(0.5); // 50/50 blend\n *\n * // Connect output\n * const output = mixer.getOutput();\n * output.connect(destination);\n * ```\n */\nexport class WetDryMixer {\n private dryGainNode: GainNode;\n private wetGainNode: GainNode;\n private outputNode: GainNode;\n\n constructor(context: Context) {\n const audioContext = context.audioContext;\n\n // Create nodes\n this.dryGainNode = audioContext.createGain();\n this.wetGainNode = audioContext.createGain();\n this.outputNode = audioContext.createGain();\n\n // Initialize gains\n this.dryGainNode.gain.value = 1; // Full dry initially\n this.wetGainNode.gain.value = 0; // No wet initially\n this.outputNode.gain.value = 1; // Unity output\n\n // Connect to output\n this.dryGainNode.connect(this.outputNode);\n this.wetGainNode.connect(this.outputNode);\n }\n\n /**\n * Connect an audio source to the dry path.\n * The source should also be connected separately to the wet path processing.\n */\n connectInput(source: AudioNode): void {\n source.connect(this.dryGainNode);\n }\n\n /**\n * Get the wet input node to connect your effect chain to.\n * Connect your effect output to this node.\n */\n getWetInput(): GainNode {\n return this.wetGainNode;\n }\n\n /**\n * Get the dry input node (for special routing needs).\n */\n getDryInput(): GainNode {\n return this.dryGainNode;\n }\n\n /**\n * Get the output node to connect to destination.\n */\n getOutput(): GainNode {\n return this.outputNode;\n }\n\n /**\n * Set the mix amount between dry and wet signals.\n * Uses equal-power crossfade to maintain constant perceived loudness.\n *\n * @param mix - Mix amount (0-1)\n * - 0 = 100% dry (no effect)\n * - 0.5 = 50/50 blend\n * - 1 = 100% wet (full effect)\n */\n setMix(mix: number): void {\n // Equal-power crossfade\n // This maintains constant perceived loudness across all mix values\n const dryGain = Math.cos((mix * Math.PI) / 2);\n const wetGain = Math.sin((mix * Math.PI) / 2);\n\n this.dryGainNode.gain.value = dryGain;\n this.wetGainNode.gain.value = wetGain;\n }\n\n /**\n * Disconnect all nodes (cleanup).\n */\n disconnect(): void {\n this.dryGainNode.disconnect();\n this.wetGainNode.disconnect();\n this.outputNode.disconnect();\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { ConstantSourceNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, ModulePropSchema, SetterHooks } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IConstant = IModule<ModuleType.Constant>;\nexport type IConstantProps = {\n value: number;\n};\n\nexport const constantPropSchema: ModulePropSchema<IConstantProps> = {\n value: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Value\",\n },\n};\n\nconst DEFAULT_PROPS: IConstantProps = { value: 1 };\n\nexport default class Constant\n extends Module<ModuleType.Constant>\n implements Pick<SetterHooks<IConstantProps>, \"onAfterSetValue\">\n{\n declare audioNode: ConstantSourceNode;\n isStated = false;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Constant>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new ConstantSourceNode(context.audioContext);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs(\"out\");\n }\n\n onAfterSetValue: SetterHooks<IConstantProps>[\"onAfterSetValue\"] = (value) => {\n this.audioNode.offset.value = value;\n };\n\n start(time: ContextTime) {\n if (this.isStated) return;\n\n this.isStated = true;\n this.audioNode.start(time);\n }\n\n stop(time: ContextTime) {\n if (!this.isStated) return;\n\n this.audioNode.stop(time);\n this.rePlugAll(() => {\n this.audioNode = new ConstantSourceNode(this.context.audioContext, {\n offset: this.props.value,\n });\n });\n\n this.isStated = false;\n }\n\n triggerAttack = (note: Note, triggeredAt: ContextTime) => {\n this.audioNode.offset.setValueAtTime(note.frequency, triggeredAt);\n this.start(triggeredAt);\n };\n\n triggerRelease = (_: Note, triggeredAt: ContextTime) => {\n this.stop(triggeredAt);\n };\n}\n","import { Division, divisionToMilliseconds } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { EnumProp, ModulePropSchema } from \"@/core\";\nimport { Module, SetterHooks } from \"@/core/module/Module\";\nimport { WetDryMixer } from \"@/utils\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport enum DelayTimeMode {\n short = \"short\",\n long = \"long\",\n}\n\nexport type IDelayProps = {\n time: number; // 0-2000 ms (short) or 0-5000 ms (long)\n timeMode: DelayTimeMode; // short (2s) or long (5s)\n sync: boolean; // Enable BPM sync\n division: Division; // Note division when sync is enabled\n feedback: number; // 0-0.95 (feedback amount)\n mix: number; // 0-1 (dry/wet)\n stereo: boolean; // Enable ping-pong stereo mode\n};\n\nexport type IDelay = Module<ModuleType.Delay>;\n\nconst NOTE_DIVISIONS: Division[] = [\n \"1/64\",\n \"1/48\",\n \"1/32\",\n \"1/24\",\n \"1/16\",\n \"1/12\",\n \"1/8\",\n \"1/6\",\n \"3/16\",\n \"1/4\",\n \"5/16\",\n \"1/3\",\n \"3/8\",\n \"1/2\",\n \"3/4\",\n \"1\",\n \"1.5\",\n \"2\",\n \"3\",\n \"4\",\n \"6\",\n \"8\",\n \"16\",\n \"32\",\n];\n\nexport const delayPropSchema: ModulePropSchema<\n IDelayProps,\n {\n timeMode: EnumProp<DelayTimeMode>;\n division: EnumProp<Division>;\n }\n> = {\n time: {\n kind: \"number\",\n min: 0,\n max: 5000, // UI max for manual mode (long = 5s)\n step: 1,\n label: \"Delay Time\",\n },\n timeMode: {\n kind: \"enum\",\n options: [DelayTimeMode.short, DelayTimeMode.long],\n label: \"Time Mode\",\n },\n sync: {\n kind: \"boolean\",\n label: \"Sync\",\n },\n division: {\n kind: \"enum\",\n options: NOTE_DIVISIONS,\n label: \"Division\",\n },\n feedback: {\n kind: \"number\",\n min: 0,\n max: 0.95,\n step: 0.01,\n label: \"Feedback\",\n },\n mix: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Mix\",\n },\n stereo: {\n kind: \"boolean\",\n label: \"Stereo\",\n },\n};\n\nconst DEFAULT_DELAY_PROPS: IDelayProps = {\n time: 250,\n timeMode: DelayTimeMode.short,\n sync: false,\n division: \"1/4\",\n feedback: 0.3,\n mix: 0.3,\n stereo: false,\n};\n\n// ============================================================================\n// Module Class\n// ============================================================================\n\nexport default class Delay\n extends Module<ModuleType.Delay>\n implements\n Pick<\n SetterHooks<IDelayProps>,\n | \"onAfterSetTime\"\n | \"onAfterSetTimeMode\"\n | \"onAfterSetSync\"\n | \"onAfterSetDivision\"\n | \"onAfterSetFeedback\"\n | \"onAfterSetMix\"\n | \"onAfterSetStereo\"\n >\n{\n // Audio graph nodes\n declare audioNode: GainNode; // Input node\n private outputNode!: GainNode; // Final output node\n private wetDryMixer: WetDryMixer;\n\n // Mono delay nodes\n private delayNode: DelayNode;\n private feedbackGain: GainNode;\n\n // Stereo ping-pong nodes (created when stereo=true)\n private delayLeft: DelayNode | null = null;\n private delayRight: DelayNode | null = null;\n private feedbackLeft: GainNode | null = null;\n private feedbackRight: GainNode | null = null;\n private merger: ChannelMergerNode | null = null;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Delay>) {\n const props = { ...DEFAULT_DELAY_PROPS, ...params.props };\n\n // Input node\n const audioNodeConstructor = (context: Context) =>\n context.audioContext.createGain();\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n // Set input gain\n this.audioNode.gain.value = 1;\n\n // Create WetDryMixer\n this.wetDryMixer = new WetDryMixer(this.context);\n\n // Create mono delay graph (default)\n // Max 179s (Web Audio API limit: must be < 180s / 3 minutes)\n this.delayNode = this.context.audioContext.createDelay(179);\n this.feedbackGain = this.context.audioContext.createGain();\n\n // Connect mono graph initially\n this.connectMonoGraph();\n\n // Apply initial parameters\n this.wetDryMixer.setMix(props.mix);\n this.feedbackGain.gain.value = props.feedback;\n this.updateDelayTime();\n\n // Switch to stereo if needed\n if (props.stereo) {\n this.switchToStereo();\n }\n\n // Setup BPM listener for sync mode\n this.setupBPMListener();\n\n this.registerDefaultIOs(\"in\");\n this.registerCustomOutput();\n }\n\n private setupBPMListener() {\n this.engine.transport.addPropertyChangeCallback(\"bpm\", () => {\n if (!this.props.sync) return;\n\n this.updateDelayTime();\n });\n }\n\n private updateDelayTime() {\n let timeInSeconds: number;\n\n if (this.props.sync) {\n // BPM-based timing\n const bpm = this.engine.transport.bpm;\n const timeMs = divisionToMilliseconds(this.props.division, bpm);\n timeInSeconds = timeMs / 1000;\n } else {\n // Manual timing\n timeInSeconds = this.props.time / 1000;\n }\n\n this.delayNode.delayTime.value = timeInSeconds;\n if (this.delayLeft) this.delayLeft.delayTime.value = timeInSeconds;\n if (this.delayRight) this.delayRight.delayTime.value = timeInSeconds;\n }\n\n private connectMonoGraph() {\n // Disconnect any existing connections\n this.disconnectAll();\n\n // Connect: Input -> WetDryMixer (dry)\n this.wetDryMixer.connectInput(this.audioNode);\n\n // Connect: Input -> Delay -> Feedback -> Delay (loop)\n this.audioNode.connect(this.delayNode);\n this.delayNode.connect(this.feedbackGain);\n this.feedbackGain.connect(this.delayNode); // Feedback loop\n\n // Connect: Delay -> WetDryMixer (wet)\n this.delayNode.connect(this.wetDryMixer.getWetInput());\n\n // Output\n this.outputNode = this.wetDryMixer.getOutput();\n }\n\n private switchToStereo() {\n if (!this.delayLeft) {\n // Create stereo nodes (max 179s - Web Audio API limit: < 180s)\n this.delayLeft = this.context.audioContext.createDelay(179);\n this.delayRight = this.context.audioContext.createDelay(179);\n this.feedbackLeft = this.context.audioContext.createGain();\n this.feedbackRight = this.context.audioContext.createGain();\n this.merger = this.context.audioContext.createChannelMerger(2);\n }\n\n // Disconnect mono graph\n this.disconnectAll();\n\n // Connect stereo ping-pong graph\n this.wetDryMixer.connectInput(this.audioNode);\n\n // Input -> DelayLeft\n this.audioNode.connect(this.delayLeft);\n\n // DelayLeft -> Output to left channel\n this.delayLeft.connect(this.merger!, 0, 0); // Left to channel 0\n\n // DelayLeft -> FeedbackRight -> DelayRight (ping-pong to right)\n this.delayLeft.connect(this.feedbackRight!);\n this.feedbackRight!.connect(this.delayRight!);\n\n // DelayRight -> Output to right channel\n this.delayRight!.connect(this.merger!, 0, 1); // Right to channel 1\n\n // DelayRight -> FeedbackLeft -> DelayLeft (ping-pong back to left)\n this.delayRight!.connect(this.feedbackLeft!);\n this.feedbackLeft!.connect(this.delayLeft);\n\n // Merger -> WetDryMixer (wet)\n this.merger!.connect(this.wetDryMixer.getWetInput());\n\n this.outputNode = this.wetDryMixer.getOutput();\n\n // Sync delay times and feedback gains\n this.delayLeft.delayTime.value = this.delayNode.delayTime.value;\n this.delayRight!.delayTime.value = this.delayNode.delayTime.value;\n this.feedbackLeft!.gain.value = this.feedbackGain.gain.value;\n this.feedbackRight!.gain.value = this.feedbackGain.gain.value;\n }\n\n private disconnectAll() {\n try {\n this.audioNode.disconnect();\n this.delayNode.disconnect();\n this.feedbackGain.disconnect();\n if (this.delayLeft) this.delayLeft.disconnect();\n if (this.delayRight) this.delayRight.disconnect();\n if (this.feedbackLeft) this.feedbackLeft.disconnect();\n if (this.feedbackRight) this.feedbackRight.disconnect();\n if (this.merger) this.merger.disconnect();\n } catch {\n // Ignore disconnect errors\n }\n }\n\n private registerCustomOutput() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.outputNode,\n });\n }\n\n // ============================================================================\n // SetterHooks\n // ============================================================================\n\n onAfterSetTime = (_value: number) => {\n if (this.props.sync) return;\n\n this.updateDelayTime();\n };\n\n onAfterSetTimeMode = (value: DelayTimeMode) => {\n // Clamp time if it exceeds the new mode's maximum\n const maxTime = value === DelayTimeMode.short ? 2000 : 5000;\n if (this.props.time > maxTime) {\n this.props = { time: maxTime };\n }\n };\n\n onAfterSetSync = (_value: boolean) => {\n this.updateDelayTime();\n };\n\n onAfterSetDivision = (_value: Division) => {\n if (this.props.sync) {\n this.updateDelayTime();\n }\n };\n\n onAfterSetFeedback = (value: number) => {\n // Cap at 0.95 to prevent runaway feedback\n const cappedValue = Math.min(value, 0.95);\n this.feedbackGain.gain.value = cappedValue;\n if (this.feedbackLeft) this.feedbackLeft.gain.value = cappedValue;\n if (this.feedbackRight) this.feedbackRight.gain.value = cappedValue;\n };\n\n onAfterSetMix = (value: number) => {\n this.wetDryMixer.setMix(value);\n };\n\n onAfterSetStereo = (value: boolean) => {\n if (value) {\n this.switchToStereo();\n } else {\n this.connectMonoGraph();\n }\n };\n}\n","import { Context } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { ModulePropSchema } from \"@/core\";\nimport { IModuleConstructor, Module, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { WetDryMixer } from \"@/utils\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IDistortionProps = {\n drive: number; // 0-10 (controls distortion amount)\n tone: number; // 200-20000 Hz (lowpass filter cutoff)\n mix: number; // 0-1 (dry/wet blend)\n};\n\nexport type IDistortion = Module<ModuleType.Distortion>;\n\nexport const distortionPropSchema: ModulePropSchema<IDistortionProps> = {\n drive: {\n kind: \"number\",\n min: 0,\n max: 10,\n step: 0.1,\n label: \"Drive\",\n },\n tone: {\n kind: \"number\",\n min: 200,\n max: 20000,\n step: 1,\n exp: 3,\n label: \"Tone\",\n },\n mix: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Mix\",\n },\n};\n\nconst DEFAULT_PROPS: IDistortionProps = {\n drive: 2.0, // Moderate distortion\n tone: 8000, // Mid-bright tone\n mix: 1.0, // 100% wet (full effect)\n};\n\nexport class MonoDistortion\n extends Module<ModuleType.Distortion>\n implements\n Pick<\n SetterHooks<IDistortionProps>,\n \"onAfterSetDrive\" | \"onAfterSetTone\" | \"onAfterSetMix\"\n >\n{\n declare audioNode: GainNode; // Input node (inherited from Module)\n private outputNode: GainNode; // Final output from mixer\n private inputGain: GainNode; // Drive stage (pre-distortion gain)\n private waveshaper: WaveShaperNode; // Tanh distortion\n private filter: BiquadFilterNode; // Post-distortion lowpass filter\n private wetDryMixer: WetDryMixer; // Wet/dry blending\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Distortion>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n const audioNodeConstructor = (context: Context) =>\n new GainNode(context.audioContext, { gain: 1 });\n\n super(engineId, {\n ...params,\n audioNodeConstructor,\n props,\n });\n\n // Create audio nodes\n const audioContext = this.context.audioContext;\n\n this.inputGain = audioContext.createGain();\n this.waveshaper = audioContext.createWaveShaper();\n this.filter = audioContext.createBiquadFilter();\n this.wetDryMixer = new WetDryMixer(this.context);\n\n // Configure filter\n this.filter.type = \"lowpass\";\n this.filter.Q.value = 0.707; // Butterworth response (no resonance peak)\n\n // Connect audio graph\n // Dry path: Input -> WetDryMixer\n this.wetDryMixer.connectInput(this.audioNode);\n\n // Wet path: Input -> InputGain -> WaveShaper -> Filter -> WetDryMixer\n this.audioNode.connect(this.inputGain);\n this.inputGain.connect(this.waveshaper);\n this.waveshaper.connect(this.filter);\n this.filter.connect(this.wetDryMixer.getWetInput());\n\n // Output from mixer\n this.outputNode = this.wetDryMixer.getOutput();\n\n // Apply initial parameters\n this.updateInputGain(props.drive);\n this.updateDistortionCurve(props.drive);\n this.filter.frequency.value = props.tone;\n this.wetDryMixer.setMix(props.mix);\n\n // Register IOs\n this.registerDefaultIOs(\"in\");\n this.registerCustomOutput();\n }\n\n private registerCustomOutput() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.outputNode,\n });\n }\n\n private generateDistortionCurve(drive: number): Float32Array | null {\n const samples = 65536; // High resolution for smooth distortion\n const buffer = new ArrayBuffer(samples * Float32Array.BYTES_PER_ELEMENT);\n const curve: Float32Array = new Float32Array(buffer) as Float32Array;\n\n for (let i = 0; i < samples; i++) {\n // Map sample index to input range [-1, 1]\n const x = (i * 2) / samples - 1;\n\n // Apply tanh waveshaping with drive scaling\n // Higher drive values push more of the signal into saturation\n const driven = x * drive;\n curve[i] = Math.tanh(driven);\n }\n\n return curve;\n }\n\n private updateDistortionCurve(drive: number): void {\n // @ts-expect-error - TypeScript strict mode issue with Float32Array<ArrayBufferLike> vs Float32Array<ArrayBuffer>\n this.waveshaper.curve = this.generateDistortionCurve(drive);\n }\n\n private updateInputGain(drive: number): void {\n // Exponential scaling: each increment doubles the gain\n // drive=0 -> 1x, drive=1 -> 2x, drive=2 -> 4x, ..., drive=10 -> 1024x\n this.inputGain.gain.value = Math.pow(2, drive);\n }\n\n onAfterSetDrive: SetterHooks<IDistortionProps>[\"onAfterSetDrive\"] = (\n value,\n ) => {\n this.updateInputGain(value);\n this.updateDistortionCurve(value);\n };\n\n onAfterSetTone: SetterHooks<IDistortionProps>[\"onAfterSetTone\"] = (value) => {\n this.filter.frequency.value = value;\n };\n\n onAfterSetMix: SetterHooks<IDistortionProps>[\"onAfterSetMix\"] = (value) => {\n this.wetDryMixer.setMix(value);\n };\n}\n\nexport default class PolyDistortion extends PolyModule<ModuleType.Distortion> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Distortion>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Distortion>,\n ) => Module.create(MonoDistortion, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerDefaultIOs();\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { Module } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { IModuleConstructor, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { CustomWorklet, newAudioWorklet } from \"@/processors\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type ICustomEnvelopeProps = {\n attack: number;\n attackCurve: number;\n decay: number;\n sustain: number;\n release: number;\n};\n\nconst DEFAULT_PROPS: ICustomEnvelopeProps = {\n attack: 0.1,\n attackCurve: 0.5,\n decay: 0.1,\n sustain: 1,\n release: 0.1,\n};\n\nexport const customEnvelopePropSchema: ModulePropSchema<ICustomEnvelopeProps> =\n {\n attack: {\n kind: \"number\",\n min: 0,\n max: 10,\n step: 0.01,\n exp: 7,\n label: \"Attack\",\n },\n attackCurve: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Attack Curve\",\n },\n decay: {\n kind: \"number\",\n min: 0,\n max: 10,\n step: 0.01,\n exp: 6.6,\n label: \"Decay\",\n },\n sustain: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Sustain\",\n },\n release: {\n kind: \"number\",\n min: 0,\n max: 10,\n step: 0.01,\n exp: 5,\n label: \"Release\",\n },\n };\n\ntype CustomEnvelopeSetterHooks = SetterHooks<ICustomEnvelopeProps>;\n\nclass MonoCustomEnvelope\n extends Module<ModuleType.Envelope>\n implements\n Pick<\n CustomEnvelopeSetterHooks,\n | \"onAfterSetAttack\"\n | \"onAfterSetAttackCurve\"\n | \"onAfterSetDecay\"\n | \"onAfterSetSustain\"\n | \"onAfterSetRelease\"\n >\n{\n declare audioNode: ReturnType<typeof newAudioWorklet>;\n private gainNode!: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Envelope>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) => {\n const audioNode = newAudioWorklet(\n context,\n CustomWorklet.CustomEnvelopeProcessor,\n );\n\n // Set initial parameter values\n audioNode.parameters.get(\"attack\")!.value = props.attack;\n audioNode.parameters.get(\"attackcurve\")!.value = props.attackCurve;\n audioNode.parameters.get(\"decay\")!.value = props.decay;\n audioNode.parameters.get(\"sustain\")!.value = props.sustain;\n audioNode.parameters.get(\"release\")!.value = props.release;\n\n return audioNode;\n };\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.gainNode = new GainNode(this.context.audioContext, {\n gain: 0,\n });\n this.audioNode.connect(this.gainNode.gain);\n\n this.registerIOs();\n }\n\n // AudioParam getters\n get attackParam() {\n return this.audioNode.parameters.get(\"attack\")!;\n }\n\n get attackCurveParam() {\n return this.audioNode.parameters.get(\"attackcurve\")!;\n }\n\n get decayParam() {\n return this.audioNode.parameters.get(\"decay\")!;\n }\n\n get sustainParam() {\n return this.audioNode.parameters.get(\"sustain\")!;\n }\n\n get releaseParam() {\n return this.audioNode.parameters.get(\"release\")!;\n }\n\n get triggerParam() {\n return this.audioNode.parameters.get(\"trigger\")!;\n }\n\n // Setter hooks that update AudioParams\n onAfterSetAttack: CustomEnvelopeSetterHooks[\"onAfterSetAttack\"] = (value) => {\n this.attackParam.value = value;\n };\n\n onAfterSetAttackCurve: CustomEnvelopeSetterHooks[\"onAfterSetAttackCurve\"] = (\n value,\n ) => {\n this.attackCurveParam.value = value;\n };\n\n onAfterSetDecay: CustomEnvelopeSetterHooks[\"onAfterSetDecay\"] = (value) => {\n this.decayParam.value = value;\n };\n\n onAfterSetSustain: CustomEnvelopeSetterHooks[\"onAfterSetSustain\"] = (\n value,\n ) => {\n this.sustainParam.value = value;\n };\n\n onAfterSetRelease: CustomEnvelopeSetterHooks[\"onAfterSetRelease\"] = (\n value,\n ) => {\n this.releaseParam.value = value;\n };\n\n triggerAttack(note: Note, triggeredAt: ContextTime) {\n super.triggerAttack(note, triggeredAt);\n\n this.triggerParam.setValueAtTime(1, triggeredAt);\n }\n\n triggerRelease(note: Note, triggeredAt: ContextTime) {\n super.triggerRelease(note, triggeredAt);\n\n // Only release if this is the last active note\n if (this.activeNotes.length > 0) return;\n\n this.triggerParam.setValueAtTime(0, triggeredAt);\n }\n\n dispose() {\n this.gainNode.disconnect();\n super.dispose();\n }\n\n private registerIOs() {\n this.registerAudioInput({\n name: \"in\",\n getAudioNode: () => this.gainNode,\n });\n\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.gainNode,\n });\n }\n}\n\nexport default class CustomEnvelope extends PolyModule<ModuleType.Envelope> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Envelope>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Envelope>,\n ) => Module.create(MonoCustomEnvelope, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerDefaultIOs();\n }\n}\n","import { assertNever, Context } from \"@blibliki/utils\";\nimport { customEnvelopeProcessorURL } from \"./custom-envelope-processor\";\nimport { filterProcessorURL } from \"./filter-processor\";\nimport { lfoProcessorURL } from \"./lfo-processor\";\nimport { scaleProcessorURL } from \"./scale-processor\";\n\nexport enum CustomWorklet {\n ScaleProcessor = \"ScaleProcessor\",\n FilterProcessor = \"FilterProcessor\",\n LFOProcessor = \"LFOProcessor\",\n CustomEnvelopeProcessor = \"CustomEnvelopeProcessor\",\n}\n\nexport async function loadProcessors(context: Context) {\n await context.addModule(scaleProcessorURL);\n await context.addModule(filterProcessorURL);\n await context.addModule(lfoProcessorURL);\n await context.addModule(customEnvelopeProcessorURL);\n}\n\nexport function newAudioWorklet(context: Context, worklet: CustomWorklet) {\n switch (worklet) {\n case CustomWorklet.ScaleProcessor:\n return context.newAudioWorklet(\"scale-processor\");\n case CustomWorklet.FilterProcessor:\n return context.newAudioWorklet(\"filter-processor\");\n case CustomWorklet.LFOProcessor:\n return context.newAudioWorklet(\"lfo-processor\");\n case CustomWorklet.CustomEnvelopeProcessor:\n return context.newAudioWorklet(\"custom-envelope-processor\");\n default:\n assertNever(worklet);\n }\n}\n","export const customEnvelopeProcessorURL = URL.createObjectURL(\n new Blob(\n [\n \"(\",\n (() => {\n class CustomEnvelopeProcessor extends AudioWorkletProcessor {\n private _lasttrig = 0;\n private _trig = 0;\n private _phase = 0;\n private _value = 0;\n\n static get parameterDescriptors() {\n return [\n {\n name: \"attack\",\n defaultValue: 0.1,\n minValue: 0,\n maxValue: 60,\n automationRate: \"k-rate\",\n },\n {\n name: \"attackcurve\",\n defaultValue: 0.5,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\",\n },\n {\n name: \"decay\",\n defaultValue: 0.1,\n minValue: 0,\n maxValue: 60,\n automationRate: \"k-rate\",\n },\n {\n name: \"sustain\",\n defaultValue: 1,\n minValue: 0,\n maxValue: 1,\n automationRate: \"k-rate\",\n },\n {\n name: \"release\",\n defaultValue: 0.1,\n minValue: 0,\n maxValue: 60,\n automationRate: \"k-rate\",\n },\n {\n name: \"trigger\",\n defaultValue: 0,\n minValue: 0,\n maxValue: 1,\n automationRate: \"a-rate\",\n },\n ];\n }\n\n process(\n _inputs: Float32Array[][],\n outputs: Float32Array[][],\n parameters: Record<string, Float32Array>,\n ) {\n const output = outputs[0];\n if (!output?.[0]) return true;\n\n const trigs = parameters.trigger;\n const atk = parameters.attack![0]!;\n const dec = parameters.decay![0]!;\n const sus = parameters.sustain![0]!;\n const rel = parameters.release![0]!;\n const atkmax = 1.01 / Math.max(0.01, parameters.attackcurve![0]!);\n const atkRatio =\n 1 - Math.pow(1 - 1 / atkmax, 1 / (sampleRate * atk));\n const decRatio = 1 - Math.pow(0.36787944, 1 / (sampleRate * dec));\n const relRatio = 1 - Math.pow(0.36787944, 1 / (sampleRate * rel));\n\n if (trigs?.length === 1) this._trig = trigs[0]!;\n\n for (let i = 0; i < output[0].length; ++i) {\n if (trigs && trigs.length > 1) this._trig = trigs[i]!;\n\n // Trigger detection\n if (this._trig >= 0.5) {\n if (this._lasttrig < 0.5) this._phase = 1;\n } else {\n this._phase = 0;\n }\n\n // Attack phase\n if (this._phase === 1) {\n this._value += (atkmax - this._value) * atkRatio;\n if (this._value >= 1.0) {\n this._value = 1.0;\n this._phase = 0;\n }\n }\n // Decay phase (only while trigger is held)\n else if (this._trig >= 0.5 && this._value > sus) {\n this._value += (sus - this._value) * decRatio;\n }\n\n // Release phase\n if (this._trig < 0.5) {\n this._value += -this._value * relRatio;\n }\n\n for (const channel of output) {\n channel[i] = this._value;\n }\n\n this._lasttrig = this._trig;\n }\n\n return true;\n }\n }\n\n registerProcessor(\"custom-envelope-processor\", CustomEnvelopeProcessor);\n }).toString(),\n \")()\",\n ],\n { type: \"application/javascript\" },\n ),\n);\n","export const filterProcessorURL = URL.createObjectURL(\n new Blob(\n [\n \"(\",\n (() => {\n class FilterProcessor extends AudioWorkletProcessor {\n s0: number;\n s1: number;\n\n constructor() {\n super();\n this.s0 = 0;\n this.s1 = 0;\n }\n\n static get parameterDescriptors() {\n return [\n {\n name: \"cutoff\",\n defaultValue: 1000,\n minValue: 20,\n maxValue: 20000,\n },\n {\n name: \"resonance\",\n defaultValue: 0.0,\n minValue: 0.0,\n maxValue: 4.0,\n },\n ];\n }\n\n process(\n inputs: Float32Array[][],\n outputs: Float32Array[][],\n parameters: Record<string, Float32Array>,\n ): boolean {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n\n const cutoff = parameters.cutoff;\n const resonance = parameters.resonance;\n if (!cutoff || !resonance) return true;\n\n for (let channelNum = 0; channelNum < input.length; channelNum++) {\n const inputChannel = input[channelNum];\n const outputChannel = output[channelNum];\n if (!inputChannel || !outputChannel) continue;\n\n for (let i = 0; i < inputChannel.length; i++) {\n const s = inputChannel[i];\n if (s === undefined) continue;\n\n // Convert Hz to normalized frequency using logarithmic scale\n // This better matches human hearing perception\n const cutoffHz =\n cutoff.length > 1 ? (cutoff[i] ?? cutoff[0]) : cutoff[0];\n if (cutoffHz === undefined) continue;\n\n const clampedHz = Math.max(20, Math.min(20000, cutoffHz));\n const normalizedCutoff =\n Math.log(clampedHz / 20) / Math.log(20000 / 20);\n const c = Math.pow(0.5, (1 - normalizedCutoff) / 0.125);\n\n const resonanceValue =\n resonance.length > 1\n ? (resonance[i] ?? resonance[0])\n : resonance[0];\n if (resonanceValue === undefined) continue;\n\n const r = Math.pow(0.5, (resonanceValue + 0.125) / 0.125);\n const mrc = 1 - r * c;\n\n this.s0 = mrc * this.s0 - c * this.s1 + c * s;\n this.s1 = mrc * this.s1 + c * this.s0;\n\n outputChannel[i] = this.s1;\n }\n }\n\n return true;\n }\n }\n\n registerProcessor(\"filter-processor\", FilterProcessor);\n }).toString(),\n \")()\",\n ],\n { type: \"application/javascript\" },\n ),\n);\n","export const lfoProcessorURL = URL.createObjectURL(\n new Blob(\n [\n \"(\",\n (() => {\n class LFOProcessor extends AudioWorkletProcessor {\n private phase = 0;\n private randomValue = Math.random() * 2 - 1;\n\n static get parameterDescriptors() {\n return [\n {\n name: \"frequency\",\n defaultValue: 1.0,\n minValue: 0.01,\n maxValue: 100,\n },\n {\n name: \"waveform\",\n defaultValue: 0, // 0=sine, 1=triangle, 2=square, 3=sawtooth, 4=rampDown, 5=random\n minValue: 0,\n maxValue: 5,\n },\n {\n name: \"phase\",\n defaultValue: 0.0,\n minValue: 0.0,\n maxValue: 1.0,\n },\n ];\n }\n\n process(\n _inputs: Float32Array[][],\n outputs: Float32Array[][],\n parameters: Record<string, Float32Array>,\n ) {\n const output = outputs[0];\n if (!output) return true;\n\n const frequencyValues = parameters.frequency;\n const waveformValues = parameters.waveform;\n const phaseValues = parameters.phase;\n\n if (!frequencyValues || !waveformValues || !phaseValues)\n return true;\n\n const blockSize = output[0]?.length ?? 128;\n\n for (let i = 0; i < blockSize; i++) {\n // Get parameter values (can be per-sample or per-block)\n const frequency =\n frequencyValues.length > 1\n ? (frequencyValues[i] ?? 1.0)\n : (frequencyValues[0] ?? 1.0);\n const waveformIdx = Math.round(\n waveformValues.length > 1\n ? (waveformValues[i] ?? 0)\n : (waveformValues[0] ?? 0),\n );\n const phaseOffset =\n phaseValues.length > 1\n ? (phaseValues[i] ?? 0)\n : (phaseValues[0] ?? 0);\n\n // Calculate current phase with offset\n const currentPhase = (this.phase + phaseOffset) % 1.0;\n\n // Generate sample based on waveform type\n let sample = 0;\n switch (waveformIdx) {\n case 0: // Sine\n sample = Math.sin(2 * Math.PI * currentPhase);\n break;\n case 1: // Triangle\n sample = 2 * Math.abs(2 * currentPhase - 1) - 1;\n break;\n case 2: // Square\n sample = currentPhase < 0.5 ? 1 : -1;\n break;\n case 3: // Sawtooth\n sample = 2 * currentPhase - 1;\n break;\n case 4: // Ramp Down\n sample = 1 - 2 * currentPhase;\n break;\n case 5: // Random (sample & hold)\n sample = this.randomValue;\n break;\n default:\n sample = Math.sin(2 * Math.PI * currentPhase);\n }\n\n // Write to all output channels\n for (const channel of output) {\n channel[i] = sample;\n }\n\n // Update phase\n const phaseIncrement = frequency / sampleRate;\n this.phase += phaseIncrement;\n\n // Wrap phase and update random value on wrap\n if (this.phase >= 1.0) {\n this.phase -= 1.0;\n // Update random value only for random waveform\n if (waveformIdx === 5) {\n this.randomValue = Math.random() * 2 - 1;\n }\n }\n }\n\n return true;\n }\n }\n\n registerProcessor(\"lfo-processor\", LFOProcessor);\n }).toString(),\n \")()\",\n ],\n { type: \"application/javascript\" },\n ),\n);\n","export const scaleProcessorURL = URL.createObjectURL(\n new Blob(\n [\n \"(\",\n (() => {\n class ScaleProcessor extends AudioWorkletProcessor {\n static get parameterDescriptors() {\n return [\n {\n name: \"min\",\n defaultValue: 1e-10,\n },\n {\n name: \"max\",\n defaultValue: 1,\n },\n {\n name: \"current\",\n defaultValue: 0.5,\n },\n {\n name: \"mode\",\n defaultValue: 0, // 0 = exponential, 1 = linear\n },\n ];\n }\n\n process(\n inputs: Float32Array[][],\n outputs: Float32Array[][],\n parameters: Record<string, Float32Array>,\n ) {\n const input = inputs[0];\n const output = outputs[0];\n if (!input || !output) return true;\n\n const minValues = parameters.min;\n const maxValues = parameters.max;\n const currentValues = parameters.current;\n const modeValues = parameters.mode;\n if (!minValues || !maxValues || !currentValues || !modeValues)\n return true;\n\n const firstInput = input[0];\n if (!firstInput || firstInput.length === 0) {\n for (const outputChannel of output) {\n const current =\n currentValues.length > 1\n ? (currentValues[0] ?? 0.5)\n : (currentValues[0] ?? 0.5);\n\n outputChannel.fill(current);\n }\n\n return true;\n }\n\n for (let channel = 0; channel < input.length; channel++) {\n const inputChannel = input[channel];\n const outputChannel = output[channel];\n if (!inputChannel || !outputChannel) continue;\n\n for (let i = 0; i < inputChannel.length; i++) {\n const x = inputChannel[i];\n if (x === undefined) continue;\n\n const min =\n minValues.length > 1\n ? (minValues[i] ?? minValues[0])\n : minValues[0];\n const max =\n maxValues.length > 1\n ? (maxValues[i] ?? maxValues[0])\n : maxValues[0];\n const current =\n currentValues.length > 1\n ? (currentValues[i] ?? currentValues[0])\n : currentValues[0];\n const mode =\n modeValues.length > 1\n ? (modeValues[i] ?? modeValues[0])\n : modeValues[0];\n\n if (\n min === undefined ||\n max === undefined ||\n current === undefined ||\n mode === undefined\n )\n continue;\n\n const isLinearMode = mode >= 0.5; // 0 = exponential, 1 = linear\n\n if (isLinearMode) {\n // Linear interpolation\n if (x < 0) {\n outputChannel[i] = current + -x * (min - current);\n } else {\n outputChannel[i] = current + x * (max - current);\n }\n } else {\n // Exponential scaling\n // Handle edge cases where exponential would fail\n if (\n current === 0 ||\n (x < 0 && min === 0) ||\n (x > 0 && max === 0)\n ) {\n // Fallback to linear for invalid exponential cases\n if (x < 0) {\n outputChannel[i] = current + -x * (min - current);\n } else {\n outputChannel[i] = current + x * (max - current);\n }\n } else {\n if (x < 0) {\n outputChannel[i] = current * Math.pow(min / current, -x);\n } else {\n outputChannel[i] = current * Math.pow(max / current, x);\n }\n }\n }\n }\n }\n\n return true;\n }\n }\n\n registerProcessor(\"scale-processor\", ScaleProcessor);\n }).toString(),\n \")()\",\n ],\n { type: \"application/javascript\" },\n ),\n);\n","import { Context } from \"@blibliki/utils\";\nimport { BiquadFilterNode } from \"@blibliki/utils/web-audio-api\";\nimport { EnumProp, ModulePropSchema } from \"@/core\";\nimport { IModuleConstructor, Module, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ICreateModule, ModuleType } from \".\";\nimport { MonoGain } from \"./Gain\";\nimport { MonoScale } from \"./Scale\";\n\nexport type IFilterProps = {\n cutoff: number;\n envelopeAmount: number;\n type: BiquadFilterType;\n Q: number;\n};\n\nconst MIN_FREQ = 20;\nconst MAX_FREQ = 20000;\n\nconst DEFAULT_PROPS: IFilterProps = {\n cutoff: MAX_FREQ,\n envelopeAmount: 0,\n type: \"lowpass\",\n Q: 1,\n};\n\nexport const filterPropSchema: ModulePropSchema<\n IFilterProps,\n {\n type: EnumProp<BiquadFilterType>;\n }\n> = {\n cutoff: {\n kind: \"number\",\n min: MIN_FREQ,\n max: MAX_FREQ,\n step: 1,\n exp: 5,\n label: \"Cutoff\",\n },\n envelopeAmount: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Envelope Amount\",\n },\n type: {\n kind: \"enum\",\n options: [\"lowpass\", \"highpass\", \"bandpass\"] satisfies BiquadFilterType[],\n label: \"Type\",\n },\n Q: {\n kind: \"number\",\n min: 0.0001,\n max: 1000,\n step: 0.1,\n exp: 5,\n label: \"Q\",\n },\n};\n\nclass MonoFilter\n extends Module<ModuleType.Filter>\n implements\n Pick<\n SetterHooks<IFilterProps>,\n | \"onAfterSetType\"\n | \"onAfterSetCutoff\"\n | \"onAfterSetQ\"\n | \"onAfterSetEnvelopeAmount\"\n >\n{\n declare audioNode: BiquadFilterNode;\n private scale: MonoScale;\n private amount: MonoGain;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Filter>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n const audioNodeConstructor = (context: Context) =>\n new BiquadFilterNode(context.audioContext, {\n type: props.type,\n frequency: 0,\n Q: props.Q,\n });\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.amount = Module.create(MonoGain, engineId, {\n name: \"amount\",\n moduleType: ModuleType.Gain,\n props: { gain: props.envelopeAmount },\n });\n\n this.scale = Module.create(MonoScale, engineId, {\n name: \"scale\",\n moduleType: ModuleType.Scale,\n props: { min: MIN_FREQ, max: MAX_FREQ, current: this.props.cutoff },\n });\n\n this.amount.plug({ audioModule: this.scale, from: \"out\", to: \"in\" });\n this.scale.audioNode.connect(this.audioNode.frequency);\n\n this.registerDefaultIOs();\n this.registerInputs();\n }\n\n onAfterSetType: SetterHooks<IFilterProps>[\"onAfterSetType\"] = (value) => {\n this.audioNode.type = value;\n };\n\n onAfterSetCutoff: SetterHooks<IFilterProps>[\"onAfterSetCutoff\"] = (value) => {\n this.scale.props = { current: value };\n };\n\n onAfterSetQ: SetterHooks<IFilterProps>[\"onAfterSetQ\"] = (value) => {\n this.audioNode.Q.value = value;\n };\n\n onAfterSetEnvelopeAmount: SetterHooks<IFilterProps>[\"onAfterSetEnvelopeAmount\"] =\n (value) => {\n this.amount.props = { gain: value };\n };\n\n private registerInputs() {\n this.registerAudioInput({\n name: \"cutoff\",\n getAudioNode: () => this.audioNode.frequency,\n });\n\n this.registerAudioInput({\n name: \"cutoffMod\",\n getAudioNode: () => this.amount.audioNode,\n });\n\n this.registerAudioInput({\n name: \"Q\",\n getAudioNode: () => this.audioNode.Q,\n });\n }\n}\n\nexport default class Filter extends PolyModule<ModuleType.Filter> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Filter>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Filter>,\n ) => Module.create(MonoFilter, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerInputs();\n this.registerDefaultIOs();\n }\n\n private registerInputs() {\n this.registerAudioInput({ name: \"cutoff\" });\n this.registerAudioInput({ name: \"cutoffMod\" });\n this.registerAudioInput({ name: \"Q\" });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, ModulePropSchema, SetterHooks } from \"@/core\";\nimport { IModuleConstructor } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IGain = IModule<ModuleType.Gain>;\nexport type IGainProps = {\n gain: number;\n};\n\nexport const gainPropSchema: ModulePropSchema<IGainProps> = {\n gain: {\n kind: \"number\",\n min: 0,\n max: 2,\n step: 0.01,\n label: \"Gain\",\n },\n};\n\nconst DEFAULT_PROPS: IGainProps = { gain: 1 };\n\nexport class MonoGain\n extends Module<ModuleType.Gain>\n implements Pick<SetterHooks<IGainProps>, \"onAfterSetGain\">\n{\n declare audioNode: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Gain>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new GainNode(context.audioContext);\n\n super(engineId, {\n ...params,\n audioNodeConstructor,\n props,\n });\n\n this.registerDefaultIOs();\n this.registerAdditionalInputs();\n }\n\n onAfterSetGain: SetterHooks<IGainProps>[\"onAfterSetGain\"] = (value) => {\n this.audioNode.gain.value = value;\n };\n\n private registerAdditionalInputs() {\n this.registerAudioInput({\n name: \"gain\",\n getAudioNode: () => this.audioNode.gain,\n });\n }\n}\n\nexport default class Gain extends PolyModule<ModuleType.Gain> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Gain>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Gain>,\n ) => Module.create(MonoGain, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerAdditionalInputs();\n this.registerDefaultIOs();\n }\n\n private registerAdditionalInputs() {\n this.registerAudioInput({ name: \"gain\" });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { IModule, Module, SetterHooks } from \"@/core\";\nimport { IModuleConstructor } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { CustomWorklet, newAudioWorklet } from \"@/processors\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IScale = IModule<ModuleType.Scale>;\nexport type IScaleProps = {\n min: number;\n max: number;\n current: number;\n mode: \"exponential\" | \"linear\";\n};\n\nexport const scalePropSchema: ModulePropSchema<\n IScaleProps,\n { mode: import(\"@/core/schema\").EnumProp<\"exponential\" | \"linear\"> }\n> = {\n min: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Min\",\n },\n max: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Max\",\n },\n current: {\n kind: \"number\",\n min: -Infinity,\n max: Infinity,\n step: 0.01,\n label: \"Current\",\n },\n mode: {\n kind: \"enum\",\n label: \"Mode\",\n options: [\"exponential\", \"linear\"],\n },\n};\n\nconst DEFAULT_PROPS: IScaleProps = {\n min: 0,\n max: 1,\n current: 0.5,\n mode: \"exponential\",\n};\n\nexport class MonoScale\n extends Module<ModuleType.Scale>\n implements\n Pick<\n SetterHooks<IScaleProps>,\n \"onAfterSetMin\" | \"onAfterSetMax\" | \"onAfterSetCurrent\" | \"onAfterSetMode\"\n >\n{\n declare audioNode: AudioWorkletNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Scale>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n newAudioWorklet(context, CustomWorklet.ScaleProcessor);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs();\n }\n\n get current() {\n return this.audioNode.parameters.get(\"current\")!;\n }\n\n get min() {\n return this.audioNode.parameters.get(\"min\")!;\n }\n\n get max() {\n return this.audioNode.parameters.get(\"max\")!;\n }\n\n get mode() {\n return this.audioNode.parameters.get(\"mode\")!;\n }\n\n onAfterSetMin: SetterHooks<IScaleProps>[\"onAfterSetMin\"] = (value) => {\n this.min.value = value;\n };\n\n onAfterSetMax: SetterHooks<IScaleProps>[\"onAfterSetMax\"] = (value) => {\n this.max.value = value;\n };\n\n onAfterSetCurrent: SetterHooks<IScaleProps>[\"onAfterSetCurrent\"] = (\n value,\n ) => {\n this.current.value = value;\n };\n\n onAfterSetMode: SetterHooks<IScaleProps>[\"onAfterSetMode\"] = (value) => {\n this.mode.value = value === \"exponential\" ? 0 : 1;\n };\n}\n\nexport default class Scale extends PolyModule<ModuleType.Scale> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Scale>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Scale>,\n ) => Module.create(MonoScale, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerDefaultIOs();\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { AnalyserNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, SetterHooks } from \"@/core\";\nimport { EnumProp, ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IInspector = IModule<ModuleType.Inspector>;\nexport type IInspectorProps = {\n fftSize: number;\n};\n\nexport const inspectorPropSchema: ModulePropSchema<\n IInspectorProps,\n {\n fftSize: EnumProp<number>;\n }\n> = {\n fftSize: {\n kind: \"enum\",\n options: [32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768],\n label: \"FFT size\",\n },\n};\n\nconst DEFAULT_PROPS: IInspectorProps = { fftSize: 512 };\n\nexport default class Inspector\n extends Module<ModuleType.Inspector>\n implements Pick<SetterHooks<IInspectorProps>, \"onAfterSetFftSize\">\n{\n declare audioNode: AnalyserNode;\n private _buffer?: Float32Array<ArrayBuffer>;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Inspector>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new AnalyserNode(context.audioContext);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs(\"in\");\n }\n\n onAfterSetFftSize: SetterHooks<IInspectorProps>[\"onAfterSetFftSize\"] = (\n value,\n ) => {\n this._buffer = new Float32Array(value);\n };\n\n get buffer() {\n if (this._buffer) return this._buffer;\n\n this._buffer = new Float32Array(this.props.fftSize);\n\n return this._buffer;\n }\n\n getValue(): number {\n const value = this.getValues()[0];\n return value ?? 0;\n }\n\n getValues(): Float32Array {\n this.audioNode.getFloatTimeDomainData(this.buffer);\n\n return this.buffer;\n }\n}\n","import { Division, divisionToFrequency } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { ConstantSourceNode, GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module } from \"@/core\";\nimport { IModuleConstructor, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { EnumProp, ModulePropSchema } from \"@/core/schema\";\nimport { CustomWorklet, newAudioWorklet } from \"@/processors\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type ILFO = IModule<ModuleType.LFO>;\n\nexport enum LFOWaveform {\n sine = \"sine\",\n triangle = \"triangle\",\n square = \"square\",\n sawtooth = \"sawtooth\",\n rampDown = \"rampDown\",\n random = \"random\",\n}\n\nconst DIVISIONS: Division[] = [\n \"1/64\",\n \"1/48\",\n \"1/32\",\n \"1/24\",\n \"1/16\",\n \"1/12\",\n \"1/8\",\n \"1/6\",\n \"3/16\",\n \"1/4\",\n \"5/16\",\n \"1/3\",\n \"3/8\",\n \"1/2\",\n \"3/4\",\n \"1\",\n \"1.5\",\n \"2\",\n \"3\",\n \"4\",\n \"6\",\n \"8\",\n \"16\",\n \"32\",\n];\n\nexport type ILFOProps = {\n sync: boolean;\n frequency: number;\n division: Division;\n waveform: LFOWaveform;\n offset: number;\n amount: number;\n};\n\nconst DEFAULT_PROPS: ILFOProps = {\n sync: false,\n frequency: 1.0,\n division: \"1/4\",\n waveform: LFOWaveform.sine,\n offset: 0,\n amount: 1,\n};\n\nexport const lfoPropSchema: ModulePropSchema<\n ILFOProps,\n {\n division: EnumProp<Division>;\n waveform: EnumProp<LFOWaveform>;\n }\n> = {\n sync: {\n kind: \"boolean\",\n label: \"Sync\",\n },\n frequency: {\n kind: \"number\",\n min: 0.01,\n max: 40,\n step: 0.01,\n exp: 3,\n label: \"Frequency (Hz)\",\n },\n division: {\n kind: \"enum\",\n options: DIVISIONS,\n label: \"Division\",\n },\n waveform: {\n kind: \"enum\",\n options: Object.values(LFOWaveform),\n label: \"Waveform\",\n },\n offset: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Offset\",\n },\n amount: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Amount\",\n },\n};\n\ntype LFOSetterHooks = SetterHooks<ILFOProps>;\n\nexport class MonoLFO\n extends Module<ModuleType.LFO>\n implements\n Pick<\n LFOSetterHooks,\n | \"onAfterSetSync\"\n | \"onAfterSetFrequency\"\n | \"onAfterSetDivision\"\n | \"onAfterSetWaveform\"\n | \"onAfterSetOffset\"\n | \"onAfterSetAmount\"\n >\n{\n declare audioNode: AudioWorkletNode;\n private offsetConstant!: ConstantSourceNode;\n private offsetGain!: GainNode;\n private amplitudeGain!: GainNode;\n private mixerGain!: GainNode;\n private amountGain!: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.LFO>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n newAudioWorklet(context, CustomWorklet.LFOProcessor);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.setupAudioGraph();\n this.setupBPMListener();\n this.registerOutputs();\n this.updateFrequency();\n }\n\n private setupAudioGraph() {\n const ctx = this.context.audioContext;\n\n // Create nodes\n this.offsetConstant = new ConstantSourceNode(ctx, { offset: 1 });\n this.offsetGain = new GainNode(ctx, { gain: 0 });\n this.amplitudeGain = new GainNode(ctx, { gain: 1 });\n this.mixerGain = new GainNode(ctx, { gain: 1 });\n this.amountGain = new GainNode(ctx, { gain: this.props.amount });\n\n // Set initial waveform\n const waveformIndex = Object.values(LFOWaveform).indexOf(\n this.props.waveform,\n );\n this.waveformParam.value = waveformIndex;\n\n // Apply offset calculation\n this.updateOffsetGains();\n\n // Connect audio graph:\n // LFO → amplitudeGain → mixerGain\n this.audioNode.connect(this.amplitudeGain);\n this.amplitudeGain.connect(this.mixerGain);\n\n // ConstantSource(1) → offsetGain → mixerGain\n this.offsetConstant.connect(this.offsetGain);\n this.offsetGain.connect(this.mixerGain);\n\n // mixerGain → amountGain → (output registered separately)\n this.mixerGain.connect(this.amountGain);\n\n // Start constant source\n this.offsetConstant.start();\n }\n\n private setupBPMListener() {\n this.engine.transport.addPropertyChangeCallback(\"bpm\", () => {\n if (!this.props.sync) return;\n\n this.updateFrequencyFromBPM();\n });\n }\n\n private registerOutputs() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.amountGain,\n });\n }\n\n private updateFrequency() {\n if (this.props.sync) {\n this.updateFrequencyFromBPM();\n } else {\n this.frequencyParam.value = this.props.frequency;\n }\n }\n\n private updateFrequencyFromBPM() {\n const bpm = this.engine.transport.bpm;\n const frequency = divisionToFrequency(this.props.division, bpm);\n this.frequencyParam.value = frequency;\n }\n\n private updateOffsetGains() {\n const offset = this.props.offset;\n // Formula: output = lfo_signal * (1 - |offset|/2) + offset/2\n const amplitude = 1 - Math.abs(offset) / 2;\n const dcOffset = offset / 2;\n\n this.amplitudeGain.gain.value = amplitude;\n this.offsetGain.gain.value = dcOffset;\n }\n\n get frequencyParam() {\n return this.audioNode.parameters.get(\"frequency\")!;\n }\n\n get waveformParam() {\n return this.audioNode.parameters.get(\"waveform\")!;\n }\n\n get phaseParam() {\n return this.audioNode.parameters.get(\"phase\")!;\n }\n\n onAfterSetSync: LFOSetterHooks[\"onAfterSetSync\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetFrequency: LFOSetterHooks[\"onAfterSetFrequency\"] = (value) => {\n this.frequencyParam.value = value;\n };\n\n onAfterSetDivision: LFOSetterHooks[\"onAfterSetDivision\"] = () => {\n this.updateFrequencyFromBPM();\n };\n\n onAfterSetWaveform: LFOSetterHooks[\"onAfterSetWaveform\"] = (value) => {\n const waveformIndex = Object.values(LFOWaveform).indexOf(value);\n this.waveformParam.value = waveformIndex;\n };\n\n onAfterSetOffset: LFOSetterHooks[\"onAfterSetOffset\"] = () => {\n this.updateOffsetGains();\n };\n\n onAfterSetAmount: LFOSetterHooks[\"onAfterSetAmount\"] = (value) => {\n this.amountGain.gain.value = value;\n };\n\n dispose() {\n this.offsetConstant.stop();\n super.dispose();\n }\n}\n\nexport default class LFO extends PolyModule<ModuleType.LFO> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.LFO>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.LFO>,\n ) => Module.create(MonoLFO, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerDefaultIOs(\"out\");\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { GainNode } from \"@blibliki/utils/web-audio-api\";\nimport { Module } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { IModuleConstructor } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IEnvelopeProps = {\n attack: number;\n decay: number;\n sustain: number;\n release: number;\n};\n\nconst DEFAULT_PROPS: IEnvelopeProps = {\n attack: 0.01,\n decay: 0.1,\n sustain: 0.7,\n release: 0.3,\n};\n\nexport const envelopePropSchema: ModulePropSchema<IEnvelopeProps> = {\n attack: {\n kind: \"number\",\n min: 0.001,\n max: 10,\n step: 0.001,\n exp: 3,\n label: \"Attack\",\n },\n decay: {\n kind: \"number\",\n min: 0.001,\n max: 10,\n step: 0.001,\n exp: 3,\n label: \"Decay\",\n },\n sustain: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Sustain\",\n },\n release: {\n kind: \"number\",\n min: 0.001,\n max: 10,\n step: 0.001,\n exp: 3,\n label: \"Release\",\n },\n};\n\n// Constants for safe audio parameter automation\nconst MIN_GAIN = 0.00001;\n\nclass MonoEnvelope extends Module<ModuleType.LegacyEnvelope> {\n declare audioNode: GainNode;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.LegacyEnvelope>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) => {\n const audioNode = new GainNode(context.audioContext);\n audioNode.gain.value = 0;\n return audioNode;\n };\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.registerDefaultIOs();\n }\n\n triggerAttack(note: Note, triggeredAt: ContextTime) {\n super.triggerAttack(note, triggeredAt);\n\n const { attack, decay, sustain } = this.props;\n const gain = this.audioNode.gain;\n\n // Exponential reset to avoid clicks when retriggering (production-style)\n gain.cancelAndHoldAtTime(triggeredAt);\n const resetTimeConstant = 0.002; // 2ms time constant for exponential decay\n const resetDuration = resetTimeConstant * 5; // ~10ms total (5 time constants ≈ 99% complete)\n gain.setTargetAtTime(MIN_GAIN, triggeredAt, resetTimeConstant);\n\n // Attack phase: linear ramp to peak\n const attackStartTime = triggeredAt + resetDuration;\n const attackEndTime = attackStartTime + attack;\n gain.setValueAtTime(MIN_GAIN, attackStartTime); // Ensure we start from MIN_GAIN\n gain.linearRampToValueAtTime(1.0, attackEndTime);\n\n // Decay phase: exponential ramp to sustain level\n if (sustain < 1) {\n const decayEndTime = attackEndTime + decay;\n // exponentialRampToValueAtTime cannot reach 0, use MIN_GAIN instead\n const sustainValue = sustain > 0 ? sustain : MIN_GAIN;\n gain.exponentialRampToValueAtTime(sustainValue, decayEndTime);\n }\n }\n\n triggerRelease(note: Note, triggeredAt: ContextTime) {\n super.triggerRelease(note, triggeredAt);\n\n // Only release if this is the last active note\n if (this.activeNotes.length > 0) return;\n\n const { release } = this.props;\n const gain = this.audioNode.gain;\n\n // Release phase: exponential fade to silence (analog-style)\n gain.cancelAndHoldAtTime(triggeredAt);\n gain.setTargetAtTime(MIN_GAIN, triggeredAt, release);\n }\n}\n\nexport default class Envelope extends PolyModule<ModuleType.LegacyEnvelope> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.LegacyEnvelope>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.LegacyEnvelope>,\n ) => Module.create(MonoEnvelope, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerDefaultIOs();\n }\n}\n","import { Context, EmptyObject } from \"@blibliki/utils\";\nimport { IModule, Module, ModulePropSchema } from \"@/core\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IMaster = IModule<ModuleType.Master>;\nexport type IMasterProps = EmptyObject;\n\nconst DEFAULT_PROPS: IMasterProps = {};\n\nexport const masterPropSchema: ModulePropSchema<IMasterProps> = {};\n\nexport default class Master extends Module<ModuleType.Master> {\n declare audioNode: AudioDestinationNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Master>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) => context.destination;\n\n super(engineId, { ...params, audioNodeConstructor, props });\n\n this.registerDefaultIOs(\"in\");\n }\n}\n","import {\n IModule,\n Module,\n MidiOutput,\n SetterHooks,\n MidiInputDevice,\n} from \"@/core\";\nimport ComputerKeyboardInput from \"@/core/midi/ComputerKeyboardDevice\";\nimport MidiEvent from \"@/core/midi/MidiEvent\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IMidiInput = IModule<ModuleType.MidiInput>;\nexport type IMidiInputProps = {\n selectedId: string | undefined | null;\n selectedName: string | undefined | null;\n};\n\nexport const midiInputPropSchema: ModulePropSchema<IMidiInputProps> = {\n selectedId: {\n kind: \"string\",\n label: \"Midi device ID\",\n },\n selectedName: {\n kind: \"string\",\n label: \"Midi device name\",\n },\n};\n\nconst DEFAULT_PROPS: IMidiInputProps = {\n selectedId: undefined,\n selectedName: undefined,\n};\n\nexport default class MidiInput\n extends Module<ModuleType.MidiInput>\n implements Pick<SetterHooks<IMidiInputProps>, \"onSetSelectedId\">\n{\n declare audioNode: undefined;\n midiOutput!: MidiOutput;\n _forwardMidiEvent?: (midiEvent: MidiEvent) => void;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.MidiInput>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n // Try to find device in order of preference:\n // 1. By exact ID match\n // 2. By exact name match\n // 3. By fuzzy name match (for cross-platform compatibility)\n let midiDevice =\n this.props.selectedId &&\n this.engine.findMidiInputDevice(this.props.selectedId);\n\n if (!midiDevice && this.props.selectedName) {\n midiDevice = this.engine.findMidiInputDeviceByName(\n this.props.selectedName,\n );\n\n // If exact name match fails, try fuzzy matching\n if (!midiDevice) {\n const fuzzyMatch = this.engine.findMidiInputDeviceByFuzzyName(\n this.props.selectedName,\n 0.6, // 60% similarity threshold\n );\n\n if (fuzzyMatch) {\n midiDevice = fuzzyMatch.device;\n console.log(\n `MIDI device fuzzy matched: \"${this.props.selectedName}\" -> \"${midiDevice.name}\" (confidence: ${Math.round(fuzzyMatch.score * 100)}%)`,\n );\n }\n }\n }\n\n if (midiDevice) {\n this.addEventListener(midiDevice);\n }\n\n this.registerOutputs();\n }\n\n onSetSelectedId: SetterHooks<IMidiInputProps>[\"onSetSelectedId\"] = (\n value,\n ) => {\n this.removeEventListener();\n if (!value) return value;\n\n const midiDevice = this.engine.findMidiInputDevice(value);\n if (!midiDevice) return value;\n\n if (this.props.selectedName !== midiDevice.name) {\n this.props = { selectedName: midiDevice.name };\n this.triggerPropsUpdate();\n }\n this.addEventListener(midiDevice);\n\n return value;\n };\n\n private get forwardMidiEvent() {\n if (this._forwardMidiEvent) return this._forwardMidiEvent;\n\n this._forwardMidiEvent = (midiEvent: MidiEvent) => {\n this.midiOutput.onMidiEvent(midiEvent);\n };\n\n return this._forwardMidiEvent;\n }\n\n private addEventListener(\n midiDevice: MidiInputDevice | ComputerKeyboardInput,\n ) {\n midiDevice.addEventListener(this.forwardMidiEvent);\n }\n\n private removeEventListener() {\n if (!this.props.selectedId) return;\n\n const midiDevice = this.engine.findMidiInputDevice(this.props.selectedId);\n midiDevice?.removeEventListener(this.forwardMidiEvent);\n }\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi out\" });\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { IModule, MidiEvent, MidiOutput, Module, SetterHooks } from \"@/core\";\nimport { ModulePropSchema, NumberProp, PropSchema } from \"@/core/schema\";\nimport { ICreateModule, moduleSchemas, ModuleType } from \".\";\n\nexport type IMidiMapper = IModule<ModuleType.MidiMapper>;\nexport type IMidiMapperProps = {\n pages: MidiMappingPage[];\n activePage: number;\n globalMappings: MidiMapping<ModuleType>[];\n};\n\nexport type MidiMappingPage = {\n name?: string;\n mappings: MidiMapping<ModuleType>[];\n};\n\nexport enum MidiMappingMode {\n direct = \"direct\",\n directRev = \"directRev\",\n toggleInc = \"toggleInc\",\n toggleDec = \"toggleDec\",\n incDec = \"incDec\",\n incDecRev = \"incDecRev\",\n}\n\nexport type MidiMapping<T extends ModuleType> = {\n cc?: number;\n value?: number;\n moduleId?: string;\n moduleType?: T;\n propName?: string;\n autoAssign?: boolean;\n mode?: MidiMappingMode;\n threshold?: number; // For incDec mode (default: 64)\n step?: number;\n};\n\nexport const midiMapperPropSchema: ModulePropSchema<IMidiMapperProps> = {\n pages: {\n kind: \"array\",\n label: \"Midi mapping pages\",\n },\n activePage: {\n kind: \"number\",\n label: \"Active page\",\n min: 0,\n max: 100,\n step: 1,\n },\n globalMappings: {\n kind: \"array\",\n label: \"Global midi mappings\",\n },\n};\n\nconst DEFAULT_PROPS: IMidiMapperProps = {\n pages: [{ name: \"Page 1\", mappings: [{}] }],\n activePage: 0,\n globalMappings: [{}],\n};\n\nfunction getMidiFromMappedValue({\n value,\n midiValue,\n propSchema,\n mapping,\n}: {\n value: number;\n propSchema: NumberProp;\n midiValue: number;\n mapping: MidiMapping<ModuleType>;\n}): number {\n const min = propSchema.min ?? 0;\n const max = propSchema.max ?? 1;\n const exp = propSchema.exp ?? 1;\n\n const { threshold = 64, mode } = mapping;\n\n // Reverse the range mapping: get curvedValue\n const curvedValue = (value - min) / (max - min);\n\n // Reverse the exponential curve: get normalizedMidi\n const normalizedMidi = Math.pow(curvedValue, 1 / exp);\n\n // Reverse the MIDI normalization: get midiValue\n let newMidiValue = normalizedMidi * 127;\n newMidiValue =\n (midiValue >= threshold && mode === MidiMappingMode.incDec) ||\n (midiValue <= threshold && mode === MidiMappingMode.incDecRev)\n ? newMidiValue + 1\n : newMidiValue - 1;\n return Math.round(Math.max(0, Math.min(127, newMidiValue))); // Valid MIDI range\n}\n\ntype MidiMapperSetterHooks = Pick<\n SetterHooks<IMidiMapperProps>,\n \"onSetActivePage\"\n>;\n\nexport default class MidiMapper\n extends Module<ModuleType.MidiMapper>\n implements MidiMapperSetterHooks\n{\n declare audioNode: undefined;\n private _midiOut: MidiOutput; // Will be used to send CC values on page change\n\n constructor(engineId: string, params: ICreateModule<ModuleType.MidiMapper>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n\n this._midiOut = this.registerMidiOutput({\n name: \"midi out\",\n });\n }\n\n onSetActivePage: MidiMapperSetterHooks[\"onSetActivePage\"] = (value) => {\n const activePage = Math.max(\n Math.min(value, this.props.pages.length - 1),\n 0,\n );\n\n const newPage = this.props.pages[activePage];\n\n // Send stored CC values to MIDI output when changing pages\n const now = this.context.currentTime;\n newPage?.mappings.forEach((mapping) => {\n if (mapping.cc !== undefined && mapping.value !== undefined) {\n // Create CC MIDI event and send it\n const midiEvent = MidiEvent.fromCC(mapping.cc, mapping.value, now);\n this._midiOut.onMidiEvent(midiEvent);\n }\n });\n\n return activePage;\n };\n\n handleCC = (event: MidiEvent, triggeredAt: ContextTime) => {\n this.checkAutoAssign(event);\n\n const activePage = this.props.pages[this.props.activePage];\n if (!activePage) return;\n\n const matchingMappings = [\n ...this.props.globalMappings.filter((m) => m.cc === event.cc),\n ...activePage.mappings.filter((m) => m.cc === event.cc),\n ];\n\n // Forward all matching mappings\n matchingMappings.forEach((mapping) => {\n this.forwardMapping(event, mapping, triggeredAt);\n });\n\n // Update mapping values if we have matching CCs\n if (matchingMappings.length > 0 && event.ccValue !== undefined) {\n const updatedGlobalMappings = this.props.globalMappings.map((mapping) => {\n if (mapping.cc === event.cc) {\n return { ...mapping, value: event.ccValue };\n }\n return mapping;\n });\n\n const updatedPageMappings = activePage.mappings.map((mapping) => {\n if (mapping.cc === event.cc) {\n return { ...mapping, value: event.ccValue };\n }\n return mapping;\n });\n\n const updatedPages = this.props.pages.map((page, index) =>\n index === this.props.activePage\n ? { ...page, mappings: updatedPageMappings }\n : page,\n );\n\n this.props = {\n pages: updatedPages,\n globalMappings: updatedGlobalMappings,\n };\n this.triggerPropsUpdate();\n }\n };\n\n forwardMapping = (\n event: MidiEvent,\n mapping: MidiMapping<ModuleType>,\n _triggeredAt: ContextTime,\n ) => {\n if (\n mapping.moduleId === undefined ||\n mapping.moduleType === undefined ||\n mapping.propName === undefined\n )\n return;\n\n const propName = mapping.propName;\n let midiValue = event.ccValue;\n if (midiValue === undefined) return;\n\n const mode = mapping.mode ?? \"direct\";\n\n // Toggle mode: only respond to 127 (button press), ignore 0\n if (\n (mode === MidiMappingMode.toggleInc ||\n mode === MidiMappingMode.toggleDec) &&\n midiValue !== 127\n ) {\n return;\n }\n\n const mappedModule = this.engine.findModule(mapping.moduleId);\n // @ts-expect-error TS7053 ignore this error\n const propSchema = moduleSchemas[mappedModule.moduleType][\n propName\n ] as PropSchema;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let mappedValue: any;\n\n // Direct mode (default) or Toggle mode: map value directly\n switch (propSchema.kind) {\n case \"number\": {\n // @ts-expect-error TS7053 ignore this error\n const currentValue = mappedModule.props[propName] as number;\n\n if (\n mode === MidiMappingMode.incDec ||\n mode === MidiMappingMode.incDecRev\n ) {\n midiValue = getMidiFromMappedValue({\n value: currentValue,\n propSchema,\n mapping,\n midiValue,\n });\n } else if (mode === MidiMappingMode.directRev) {\n midiValue = 127 - midiValue;\n }\n\n if (mode === MidiMappingMode.toggleInc) {\n mappedValue = currentValue + (propSchema.step ?? 1);\n } else if (mode === MidiMappingMode.toggleDec) {\n mappedValue = currentValue - (propSchema.step ?? 1);\n } else {\n const min = propSchema.min ?? 0;\n const max = propSchema.max ?? 1;\n const normalizedMidi = midiValue / 127;\n const curvedValue = Math.pow(normalizedMidi, propSchema.exp ?? 1);\n mappedValue = min + curvedValue * (max - min);\n\n // Round to step if defined\n if (\n propSchema.step !== undefined &&\n (!propSchema.exp || propSchema.exp === 1)\n ) {\n const steps = Math.round((mappedValue - min) / propSchema.step);\n mappedValue = min + steps * propSchema.step;\n }\n }\n\n break;\n }\n case \"enum\": {\n const optionIndex = Math.floor(\n (midiValue / 127) * propSchema.options.length,\n );\n const clampedIndex = Math.min(\n optionIndex,\n propSchema.options.length - 1,\n );\n mappedValue = propSchema.options[clampedIndex];\n break;\n }\n case \"boolean\":\n mappedValue = midiValue >= 64;\n break;\n case \"string\":\n throw Error(\"MidiMapper not support string type of values\");\n case \"array\":\n throw Error(\"MidiMapper not support array type of values\");\n\n default:\n throw Error(\"MidiMapper unknown type\");\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n mappedModule.props = { [propName]: mappedValue };\n mappedModule.triggerPropsUpdate();\n };\n\n private checkAutoAssign(event: MidiEvent) {\n if (event.cc === undefined) return;\n\n const activePage = this.props.pages[this.props.activePage];\n if (!activePage) return;\n\n const hasGlobalAutoAssign = this.props.globalMappings.some(\n ({ autoAssign }) => autoAssign,\n );\n const hasPageAutoAssign = activePage.mappings.some(\n ({ autoAssign }) => autoAssign,\n );\n\n if (!hasGlobalAutoAssign && !hasPageAutoAssign) return;\n\n // Update global mappings if needed\n const updatedGlobalMappings = hasGlobalAutoAssign\n ? this.props.globalMappings.map((mapping) => {\n if (!mapping.autoAssign) return mapping;\n\n return {\n ...mapping,\n cc: event.cc,\n autoAssign: false,\n };\n })\n : this.props.globalMappings;\n\n // Update page mappings if needed\n const updatedPageMappings = hasPageAutoAssign\n ? activePage.mappings.map((mapping) => {\n if (!mapping.autoAssign) return mapping;\n\n return {\n ...mapping,\n cc: event.cc,\n autoAssign: false,\n };\n })\n : activePage.mappings;\n\n const updatedPages = this.props.pages.map((page, index) =>\n index === this.props.activePage\n ? { ...page, mappings: updatedPageMappings }\n : page,\n );\n\n this.props = { pages: updatedPages, globalMappings: updatedGlobalMappings };\n this.triggerPropsUpdate();\n }\n}\n","import {\n IModule,\n Module,\n MidiInput,\n SetterHooks,\n MidiOutputDevice,\n} from \"@/core\";\nimport MidiEvent from \"@/core/midi/MidiEvent\";\nimport { ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IMidiOutput = IModule<ModuleType.MidiOutput>;\nexport type IMidiOutputProps = {\n selectedId: string | undefined | null;\n selectedName: string | undefined | null;\n};\n\nexport const midiOutputPropSchema: ModulePropSchema<IMidiOutputProps> = {\n selectedId: {\n kind: \"string\",\n label: \"Midi device ID\",\n },\n selectedName: {\n kind: \"string\",\n label: \"Midi device name\",\n },\n};\n\nconst DEFAULT_PROPS: IMidiOutputProps = {\n selectedId: undefined,\n selectedName: undefined,\n};\n\nexport default class MidiOutput\n extends Module<ModuleType.MidiOutput>\n implements Pick<SetterHooks<IMidiOutputProps>, \"onSetSelectedId\">\n{\n declare audioNode: undefined;\n midiInput!: MidiInput;\n private currentDevice?: MidiOutputDevice;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.MidiOutput>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n // Try to find device in order of preference:\n // 1. By exact ID match\n // 2. By exact name match\n // 3. By fuzzy name match (for cross-platform compatibility)\n let midiDevice =\n this.props.selectedId &&\n this.engine.findMidiOutputDevice(this.props.selectedId);\n\n if (!midiDevice && this.props.selectedName) {\n midiDevice = this.engine.findMidiOutputDeviceByName(\n this.props.selectedName,\n );\n\n // If exact name match fails, try fuzzy matching\n if (!midiDevice) {\n const fuzzyMatch = this.engine.findMidiOutputDeviceByFuzzyName(\n this.props.selectedName,\n 0.6, // 60% similarity threshold\n );\n\n if (fuzzyMatch) {\n midiDevice = fuzzyMatch.device;\n console.log(\n `MIDI device fuzzy matched: \"${this.props.selectedName}\" -> \"${midiDevice.name}\" (confidence: ${Math.round(fuzzyMatch.score * 100)}%)`,\n );\n }\n }\n }\n\n if (midiDevice) {\n this.currentDevice = midiDevice;\n }\n\n this.registerInputs();\n }\n\n onSetSelectedId: SetterHooks<IMidiOutputProps>[\"onSetSelectedId\"] = (\n value,\n ) => {\n if (!value) {\n this.currentDevice = undefined;\n return value;\n }\n\n const midiDevice = this.engine.findMidiOutputDevice(value);\n if (!midiDevice) return value;\n\n if (this.props.selectedName !== midiDevice.name) {\n this.props = { selectedName: midiDevice.name };\n this.triggerPropsUpdate();\n }\n\n this.currentDevice = midiDevice;\n\n return value;\n };\n\n onMidiEvent = (midiEvent: MidiEvent) => {\n if (!this.currentDevice) return;\n\n // Send raw MIDI data to hardware\n const rawData = midiEvent.rawMessage.data;\n this.currentDevice.send(rawData);\n };\n\n private registerInputs() {\n this.midiInput = this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context } from \"@blibliki/utils\";\nimport { AudioBufferSourceNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module } from \"@/core\";\nimport { SetterHooks } from \"@/core/module/Module\";\nimport { EnumProp, ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type INoise = IModule<ModuleType.Noise>;\n\nexport enum NoiseType {\n white = \"white\",\n pink = \"pink\",\n brown = \"brown\",\n blue = \"blue\",\n}\n\n/**\n * Props for the Noise module.\n *\n * @property type - Type of noise to generate.\n * One of: \"white\", \"pink\", \"brown\", or \"blue\".\n */\nexport type INoiseProps = {\n type: NoiseType;\n};\n\nexport const noisePropSchema: ModulePropSchema<\n INoiseProps,\n {\n type: EnumProp<NoiseType>;\n }\n> = {\n type: {\n kind: \"enum\",\n options: Object.values(NoiseType),\n label: \"Type\",\n },\n};\n\nconst DEFAULT_PROPS: INoiseProps = {\n type: NoiseType.white,\n};\n\ntype NoiseSetterHooks = Pick<SetterHooks<INoiseProps>, \"onAfterSetType\">;\n\n/**\n * Generates a buffer containing white noise.\n */\nfunction generateWhiteNoise(\n context: BaseAudioContext,\n duration: number,\n): AudioBuffer {\n const sampleRate = context.sampleRate;\n const length = sampleRate * duration;\n const buffer = context.createBuffer(2, length, sampleRate);\n\n for (let channel = 0; channel < 2; channel++) {\n const data = buffer.getChannelData(channel);\n for (let i = 0; i < length; i++) {\n data[i] = Math.random() * 2 - 1;\n }\n }\n\n return buffer;\n}\n\n/**\n * Generates a buffer containing pink noise using the Voss-McCartney algorithm.\n * Pink noise has equal energy per octave (1/f spectrum).\n */\nfunction generatePinkNoise(\n context: BaseAudioContext,\n duration: number,\n): AudioBuffer {\n const sampleRate = context.sampleRate;\n const length = sampleRate * duration;\n const buffer = context.createBuffer(2, length, sampleRate);\n\n for (let channel = 0; channel < 2; channel++) {\n const data = buffer.getChannelData(channel);\n\n // Paul Kellet pink noise generator state\n let b0 = 0,\n b1 = 0,\n b2 = 0,\n b3 = 0,\n b4 = 0,\n b5 = 0,\n b6 = 0;\n\n for (let i = 0; i < length; i++) {\n const white = Math.random() * 2 - 1;\n\n b0 = 0.99886 * b0 + white * 0.0555179;\n b1 = 0.99332 * b1 + white * 0.0750759;\n b2 = 0.969 * b2 + white * 0.153852;\n b3 = 0.8665 * b3 + white * 0.3104856;\n b4 = 0.55 * b4 + white * 0.5329522;\n b5 = -0.7616 * b5 - white * 0.016898;\n\n const pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;\n b6 = white * 0.115926;\n\n data[i] = pink * 0.11; // Scale to approximately -1 to 1\n }\n }\n\n return buffer;\n}\n\n/**\n * Generates a buffer containing brown noise (Brownian/red noise).\n * Brown noise has a 1/f² spectrum with heavy low-end emphasis.\n */\nfunction generateBrownNoise(\n context: BaseAudioContext,\n duration: number,\n): AudioBuffer {\n const sampleRate = context.sampleRate;\n const length = sampleRate * duration;\n const buffer = context.createBuffer(2, length, sampleRate);\n\n for (let channel = 0; channel < 2; channel++) {\n const data = buffer.getChannelData(channel);\n let lastOut = 0;\n\n for (let i = 0; i < length; i++) {\n const white = Math.random() * 2 - 1;\n lastOut = (lastOut + white * 0.02) * 0.99;\n data[i] = lastOut * 3.5; // Scale to approximately -1 to 1\n }\n }\n\n return buffer;\n}\n\n/**\n * Generates a buffer containing blue noise.\n * Blue noise emphasizes high frequencies (opposite of pink noise).\n */\nfunction generateBlueNoise(\n context: BaseAudioContext,\n duration: number,\n): AudioBuffer {\n const sampleRate = context.sampleRate;\n const length = sampleRate * duration;\n const buffer = context.createBuffer(2, length, sampleRate);\n\n for (let channel = 0; channel < 2; channel++) {\n const data = buffer.getChannelData(channel);\n let lastWhite = 0;\n\n for (let i = 0; i < length; i++) {\n const white = Math.random() * 2 - 1;\n // Blue noise is the derivative of white noise\n data[i] = (white - lastWhite) * 0.5;\n lastWhite = white;\n }\n }\n\n return buffer;\n}\n\n/**\n * Noise generator module supporting white, pink, brown, and blue noise types.\n */\nexport default class Noise\n extends Module<ModuleType.Noise>\n implements NoiseSetterHooks\n{\n declare audioNode: AudioBufferSourceNode;\n isStated = false;\n private noiseBuffers: Map<NoiseType, AudioBuffer>;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Noise>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n const audioNodeConstructor = (context: Context) => {\n const node = new AudioBufferSourceNode(context.audioContext);\n return node;\n };\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n // Pre-generate all noise buffers (2 seconds of noise, looped)\n const bufferDuration = 2;\n this.noiseBuffers = new Map<NoiseType, AudioBuffer>([\n [\n NoiseType.white,\n generateWhiteNoise(this.context.audioContext, bufferDuration),\n ],\n [\n NoiseType.pink,\n generatePinkNoise(this.context.audioContext, bufferDuration),\n ],\n [\n NoiseType.brown,\n generateBrownNoise(this.context.audioContext, bufferDuration),\n ],\n [\n NoiseType.blue,\n generateBlueNoise(this.context.audioContext, bufferDuration),\n ],\n ]);\n\n // Set the initial buffer\n this.audioNode.buffer = this.noiseBuffers.get(props.type)!;\n this.audioNode.loop = true;\n\n this.registerDefaultIOs(\"out\");\n }\n\n onAfterSetType: NoiseSetterHooks[\"onAfterSetType\"] = (type) => {\n const wasStarted = this.isStated;\n const currentTime = this.context.audioContext.currentTime;\n\n if (wasStarted) {\n this.stop(currentTime);\n }\n\n // Replace the audio node with a new one using the selected buffer\n this.rePlugAll(() => {\n this.audioNode = new AudioBufferSourceNode(this.context.audioContext, {\n buffer: this.noiseBuffers.get(type)!,\n loop: true,\n });\n });\n\n if (wasStarted) {\n this.start(currentTime);\n }\n };\n\n start(time: ContextTime) {\n if (this.isStated) return;\n\n this.isStated = true;\n this.audioNode.start(time);\n }\n\n stop(time: ContextTime) {\n if (!this.isStated) return;\n\n this.audioNode.stop(time);\n this.rePlugAll(() => {\n this.audioNode = new AudioBufferSourceNode(this.context.audioContext, {\n buffer: this.noiseBuffers.get(this.props.type)!,\n loop: true,\n });\n });\n\n this.isStated = false;\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Context, dbToGain } from \"@blibliki/utils\";\nimport { GainNode, OscillatorNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module } from \"@/core\";\nimport Note from \"@/core/Note\";\nimport { IModuleConstructor, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { EnumProp, ModulePropSchema } from \"@/core/schema\";\nimport { ICreateModule, ModuleType } from \".\";\n\nconst LOW_GAIN = -18;\n\nexport type IOscillator = IModule<ModuleType.Oscillator>;\n\nexport enum OscillatorWave {\n sine = \"sine\",\n triangle = \"triangle\",\n square = \"square\",\n sawtooth = \"sawtooth\",\n}\n\n/**\n * Props for the Oscillator module.\n *\n * @property wave - Waveform shape of the oscillator.\n * One of: \"sine\", \"square\", \"sawtooth\", \"triangle\", or \"custom\".\n * @property frequency - Base frequency in Hz (e.g. 440 for A4).\n * @property fine - Fine tuning factor in the range [-1, 1], where ±1 represents ±1 semitone.\n * @property coarse - Coarse tuning factor in the range [-1, 1], scaled to ±12 semitones.\n * @property octave - Octave transposition value (e.g. +1 for one octave up, -2 for two octaves down).\n * @property lowGain - Whether to gain reduction (-18dB). When false, oscillator runs at full gain.\n */\nexport type IOscillatorProps = {\n wave: OscillatorWave;\n frequency: number;\n fine: number;\n coarse: number;\n octave: number;\n lowGain: boolean;\n};\n\nexport const oscillatorPropSchema: ModulePropSchema<\n IOscillatorProps,\n {\n wave: EnumProp<OscillatorWave>;\n }\n> = {\n wave: {\n kind: \"enum\",\n options: Object.values(OscillatorWave),\n label: \"Waveform\",\n },\n frequency: {\n kind: \"number\",\n min: 0,\n max: 25000,\n step: 1,\n label: \"Frequency\",\n },\n fine: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Fine\",\n },\n coarse: {\n kind: \"number\",\n min: -12,\n max: 12,\n step: 1,\n label: \"Coarse\",\n },\n octave: {\n kind: \"number\",\n min: -1,\n max: 2,\n step: 1,\n label: \"Octave\",\n },\n lowGain: {\n kind: \"boolean\",\n label: `Use ${LOW_GAIN}db Gain`,\n },\n};\n\nconst DEFAULT_PROPS: IOscillatorProps = {\n wave: OscillatorWave.sine,\n frequency: 440,\n fine: 0,\n coarse: 0,\n octave: 0,\n lowGain: false,\n};\n\ntype OscillatorSetterHooks = Pick<\n SetterHooks<IOscillatorProps>,\n | \"onAfterSetWave\"\n | \"onAfterSetFrequency\"\n | \"onAfterSetFine\"\n | \"onAfterSetCoarse\"\n | \"onAfterSetOctave\"\n | \"onAfterSetLowGain\"\n>;\n\nexport class MonoOscillator\n extends Module<ModuleType.Oscillator>\n implements OscillatorSetterHooks\n{\n declare audioNode: OscillatorNode;\n isStated = false;\n outputGain: GainNode;\n detuneGain!: GainNode;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Oscillator>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new OscillatorNode(context.audioContext);\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n this.outputGain = new GainNode(this.context.audioContext, {\n gain: dbToGain(LOW_GAIN),\n });\n\n this.applyOutputGain();\n this.initializeGainDetune();\n this.registerInputs();\n this.registerOutputs();\n }\n\n onAfterSetWave: OscillatorSetterHooks[\"onAfterSetWave\"] = (value) => {\n this.audioNode.type = value;\n };\n\n onAfterSetFrequency: OscillatorSetterHooks[\"onAfterSetFrequency\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetFine: OscillatorSetterHooks[\"onAfterSetFine\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetCoarse: OscillatorSetterHooks[\"onAfterSetCoarse\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetOctave: OscillatorSetterHooks[\"onAfterSetOctave\"] = () => {\n this.updateFrequency();\n };\n\n onAfterSetLowGain: OscillatorSetterHooks[\"onAfterSetLowGain\"] = (lowGain) => {\n this.outputGain.gain.value = lowGain ? dbToGain(LOW_GAIN) : 1;\n };\n\n start(time: ContextTime) {\n if (this.isStated) return;\n\n this.isStated = true;\n this.audioNode.start(time);\n }\n\n stop(time: ContextTime) {\n if (!this.isStated) return;\n\n this.audioNode.stop(time);\n this.rePlugAll(() => {\n this.audioNode = new OscillatorNode(this.context.audioContext, {\n type: this.props.wave,\n frequency: this.finalFrequency,\n });\n this.applyOutputGain();\n this.detuneGain.connect(this.audioNode.detune);\n });\n\n this.isStated = false;\n }\n\n triggerAttack = (note: Note, triggeredAt: ContextTime) => {\n super.triggerAttack(note, triggeredAt);\n\n this._props.frequency = note.frequency;\n this.updateFrequency(triggeredAt);\n this.start(triggeredAt);\n };\n\n triggerRelease(note: Note, triggeredAt: ContextTime) {\n super.triggerRelease(note, triggeredAt);\n\n const lastNote = this.activeNotes.length\n ? this.activeNotes[this.activeNotes.length - 1]\n : null;\n if (!lastNote) return;\n\n this._props.frequency = lastNote.frequency;\n this.updateFrequency(triggeredAt);\n }\n\n private get finalFrequency(): number | undefined {\n const { frequency, coarse, octave, fine } = this.props;\n\n const transposed =\n frequency * Math.pow(2, coarse / 12 + octave + fine / 12);\n return transposed;\n }\n\n private updateFrequency(actionAt?: ContextTime) {\n if (this.finalFrequency === undefined) return;\n\n if (actionAt) {\n this.audioNode.frequency.setValueAtTime(this.finalFrequency, actionAt);\n } else {\n this.audioNode.frequency.value = this.finalFrequency;\n }\n }\n\n private applyOutputGain() {\n this.audioNode.connect(this.outputGain);\n }\n\n private initializeGainDetune() {\n this.detuneGain = new GainNode(this.context.audioContext, { gain: 100 });\n this.detuneGain.connect(this.audioNode.detune);\n }\n\n private registerInputs() {\n this.registerAudioInput({\n name: \"detune\",\n getAudioNode: () => this.detuneGain,\n });\n\n this.registerAudioInput({\n name: \"fm\",\n getAudioNode: () => this.audioNode.frequency,\n });\n }\n\n private registerOutputs() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.outputGain,\n });\n }\n}\n\nexport default class Oscillator extends PolyModule<ModuleType.Oscillator> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.Oscillator>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.Oscillator>,\n ) => Module.create(MonoOscillator, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerInputs();\n this.registerDefaultIOs(\"out\");\n }\n\n start(time: ContextTime) {\n this.audioModules.forEach((audioModule) => {\n audioModule.start(time);\n });\n }\n\n stop(time: ContextTime) {\n this.audioModules.forEach((audioModule) => {\n audioModule.stop(time);\n });\n }\n\n private registerInputs() {\n this.registerAudioInput({ name: \"detune\" });\n this.registerAudioInput({ name: \"fm\" });\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { ModulePropSchema } from \"@/core\";\nimport { Module, SetterHooks } from \"@/core/module/Module\";\nimport { WetDryMixer } from \"@/utils\";\nimport { ICreateModule, ModuleType } from \".\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport enum ReverbType {\n room = \"room\",\n hall = \"hall\",\n plate = \"plate\",\n spring = \"spring\",\n chamber = \"chamber\",\n reflections = \"reflections\",\n}\n\nexport type IReverbProps = {\n mix: number; // 0-1 (dry/wet)\n decayTime: number; // 0.1-10 seconds\n preDelay: number; // 0-100 ms\n type: ReverbType;\n};\n\nexport type IReverb = Module<ModuleType.Reverb>;\n\n// ============================================================================\n// Schema\n// ============================================================================\n\nexport const reverbPropSchema: ModulePropSchema<\n IReverbProps,\n { type: { kind: \"enum\"; options: ReverbType[] } }\n> = {\n mix: {\n kind: \"number\",\n min: 0,\n max: 1,\n step: 0.01,\n label: \"Mix\",\n },\n decayTime: {\n kind: \"number\",\n min: 0.1,\n max: 10,\n step: 0.1,\n exp: 2,\n label: \"Decay Time\",\n },\n preDelay: {\n kind: \"number\",\n min: 0,\n max: 100,\n step: 1,\n label: \"Pre-delay\",\n },\n type: {\n kind: \"enum\",\n options: [\n ReverbType.room,\n ReverbType.hall,\n ReverbType.plate,\n ReverbType.spring,\n ReverbType.chamber,\n ReverbType.reflections,\n ],\n },\n};\n\nconst DEFAULT_REVERB_PROPS: IReverbProps = {\n mix: 0.3,\n decayTime: 1.5,\n preDelay: 0,\n type: ReverbType.room,\n};\n\n// ============================================================================\n// Impulse Response Generation\n// ============================================================================\n\nfunction generateImpulseResponse(\n context: Context,\n type: ReverbType,\n decayTime: number,\n): AudioBuffer {\n const sampleRate = context.audioContext.sampleRate;\n\n // Special handling for reflections - use short buffer\n const effectiveDecayTime =\n type === ReverbType.reflections\n ? Math.min(decayTime, 0.2) // Max 200ms for reflections\n : decayTime;\n\n const length = Math.floor(sampleRate * effectiveDecayTime);\n const buffer = context.audioContext.createBuffer(2, length, sampleRate);\n\n // Room type tuning parameters\n const tuning = getRoomTuning(type);\n\n // Reflections type uses discrete early reflections\n if (type === ReverbType.reflections) {\n generateEarlyReflections(buffer, sampleRate, tuning);\n } else {\n // Standard diffuse reverb tail\n generateDiffuseTail(buffer, sampleRate, length, tuning);\n }\n\n // Normalize\n normalizeBuffer(buffer);\n\n return buffer;\n}\n\n// Generate discrete early reflections for small spaces\nfunction generateEarlyReflections(\n buffer: AudioBuffer,\n sampleRate: number,\n tuning: { decayFactor: number; damping: number; cutoff: number },\n) {\n // Reflection times in milliseconds (psychoacoustic early reflection pattern)\n const reflectionTimes = [\n 0, 7, 11, 17, 23, 31, 41, 47, 59, 67, 79, 89, 103, 127,\n ];\n\n for (let channel = 0; channel < 2; channel++) {\n const data = buffer.getChannelData(channel);\n\n // Add slight variation between channels for stereo width\n const channelOffset = channel * 2.3;\n\n for (const reflectionMs of reflectionTimes) {\n const time = (reflectionMs + channelOffset) / 1000;\n const position = Math.floor(time * sampleRate);\n\n if (position >= data.length) break;\n\n // Each reflection has a short burst\n const burstLength = Math.floor(sampleRate * 0.003); // 3ms burst\n const amplitude = Math.exp(-time * tuning.decayFactor);\n\n for (let i = 0; i < burstLength && position + i < data.length; i++) {\n const noise = Math.random() * 2 - 1;\n const envelope = Math.exp(-i / (burstLength * 0.3)); // Quick decay within burst\n data[position + i] = data[position + i]! + noise * amplitude * envelope;\n }\n }\n\n // Apply lowpass filter\n applyLowpass(data, tuning.cutoff);\n }\n}\n\n// Generate standard diffuse reverb tail\nfunction generateDiffuseTail(\n buffer: AudioBuffer,\n sampleRate: number,\n length: number,\n tuning: { decayFactor: number; damping: number; cutoff: number },\n) {\n for (let channel = 0; channel < 2; channel++) {\n const data = buffer.getChannelData(channel);\n\n for (let i = 0; i < length; i++) {\n // White noise\n const noise = Math.random() * 2 - 1;\n\n // Exponential decay\n const time = i / sampleRate;\n const decay = Math.exp(-time * tuning.decayFactor);\n\n // High-frequency damping (simple one-pole lowpass)\n const damping = 1 - tuning.damping * (i / length);\n\n data[i] = noise * decay * damping;\n }\n\n // Apply simple lowpass filter for damping\n applyLowpass(data, tuning.cutoff);\n }\n}\n\nfunction getRoomTuning(type: ReverbType) {\n switch (type) {\n case ReverbType.room:\n // Small to medium space - intimate, clear\n return {\n decayFactor: 3, // Moderate decay (~1-2s)\n damping: 0.5, // Moderate high-freq loss\n cutoff: 0.7, // Moderate brightness\n };\n\n case ReverbType.hall:\n // Large concert hall - spacious, smooth, long decay\n return {\n decayFactor: 1.5, // Slower decay (longer reverb tail)\n damping: 0.3, // Less damping (preserve highs longer)\n cutoff: 0.85, // Brighter (less filtering)\n };\n\n case ReverbType.plate:\n // Vintage plate reverb - bright, dense, metallic\n return {\n decayFactor: 2.5, // Medium decay\n damping: 0.2, // Very little damping (bright character)\n cutoff: 0.9, // Very bright (minimal filtering)\n };\n\n case ReverbType.spring:\n // Vintage spring reverb - metallic, bright, resonant\n return {\n decayFactor: 4, // Fast decay (spring tanks have quick decay)\n damping: 0.1, // Very little damping (bright, metallic)\n cutoff: 0.95, // Very bright (minimal filtering, metallic character)\n };\n\n case ReverbType.chamber:\n // Echo chamber - medium-large space, smooth, diffuse\n return {\n decayFactor: 2.0, // Medium decay\n damping: 0.35, // Moderate damping\n cutoff: 0.75, // Moderate brightness\n };\n\n case ReverbType.reflections:\n // Early reflections only - small space acoustics\n return {\n decayFactor: 5, // Fast decay for discrete reflections\n damping: 0.4, // Moderate damping\n cutoff: 0.8, // Fairly bright\n };\n\n default:\n return {\n decayFactor: 3,\n damping: 0.5,\n cutoff: 0.7,\n };\n }\n}\n\nfunction applyLowpass(data: Float32Array, cutoff: number) {\n let y1 = 0;\n const a = 1 - cutoff; // Simple coefficient\n\n for (let i = 0; i < data.length; i++) {\n const sample = data[i]!;\n y1 = a * sample + (1 - a) * y1;\n data[i] = y1;\n }\n}\n\nfunction normalizeBuffer(buffer: AudioBuffer) {\n for (let channel = 0; channel < buffer.numberOfChannels; channel++) {\n const data = buffer.getChannelData(channel);\n let max = 0;\n\n // Find peak\n for (const sample of data) {\n max = Math.max(max, Math.abs(sample));\n }\n\n // Normalize to 0.5 (avoid clipping)\n if (max > 0) {\n const scale = 0.5 / max;\n for (let i = 0; i < data.length; i++) {\n data[i] = data[i]! * scale;\n }\n }\n }\n}\n\n// ============================================================================\n// Module Class\n// ============================================================================\n\nexport default class Reverb\n extends Module<ModuleType.Reverb>\n implements\n Pick<\n SetterHooks<IReverbProps>,\n | \"onAfterSetMix\"\n | \"onAfterSetDecayTime\"\n | \"onAfterSetPreDelay\"\n | \"onAfterSetType\"\n >\n{\n // Audio graph nodes\n declare audioNode: GainNode; // Input node\n private outputNode: GainNode; // Final output node\n private convolverNode: ConvolverNode;\n private preDelayNode: DelayNode;\n private wetDryMixer: WetDryMixer;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.Reverb>) {\n const props = { ...DEFAULT_REVERB_PROPS, ...params.props };\n\n // Input node (this will be audioNode for Module interface)\n const audioNodeConstructor = (context: Context) =>\n context.audioContext.createGain();\n\n super(engineId, {\n ...params,\n props,\n audioNodeConstructor,\n });\n\n // Set input gain\n this.audioNode.gain.value = 1;\n\n // Create wet/dry mixer\n this.wetDryMixer = new WetDryMixer(this.context);\n\n // Create audio processing nodes\n this.convolverNode = this.context.audioContext.createConvolver();\n this.preDelayNode = this.context.audioContext.createDelay(0.1); // 100ms max\n\n // Connect graph:\n // audioNode (input) -> wetDryMixer (dry path)\n // -> preDelay -> convolver -> wetDryMixer (wet path)\n // wetDryMixer -> outputNode\n this.wetDryMixer.connectInput(this.audioNode);\n this.audioNode.connect(this.preDelayNode);\n this.preDelayNode.connect(this.convolverNode);\n this.convolverNode.connect(this.wetDryMixer.getWetInput());\n this.outputNode = this.wetDryMixer.getOutput();\n\n // Generate initial impulse response\n this.regenerateImpulseResponse();\n\n // Set initial parameters\n this.wetDryMixer.setMix(props.mix);\n this.preDelayNode.delayTime.value = props.preDelay / 1000;\n\n this.registerDefaultIOs(\"in\");\n this.registerCustomOutput();\n }\n\n private registerCustomOutput() {\n this.registerAudioOutput({\n name: \"out\",\n getAudioNode: () => this.outputNode,\n });\n }\n\n // ============================================================================\n // SetterHooks\n // ============================================================================\n\n onAfterSetMix = (value: number) => {\n this.wetDryMixer.setMix(value);\n };\n\n onAfterSetDecayTime = () => {\n this.regenerateImpulseResponse();\n };\n\n onAfterSetPreDelay = (value: number) => {\n this.preDelayNode.delayTime.value = value / 1000; // ms to seconds\n };\n\n onAfterSetType = () => {\n this.regenerateImpulseResponse();\n };\n\n // ============================================================================\n // Private Methods\n // ============================================================================\n\n private regenerateImpulseResponse() {\n const impulse = generateImpulseResponse(\n this.context,\n this.props.type,\n this.props.decayTime,\n );\n this.convolverNode.buffer = impulse;\n }\n}\n","import {\n ContextTime,\n Division,\n divisionToMilliseconds,\n TPB,\n StepSequencerSource,\n StepSequencerSourceEvent,\n Resolution,\n PlaybackMode,\n IStep,\n IStepNote,\n IStepCC,\n IPage,\n IPattern,\n} from \"@blibliki/transport\";\nimport {\n Module,\n IModule,\n MidiOutput,\n Note,\n ModulePropSchema,\n EnumProp,\n SetterHooks,\n} from \"@/core\";\nimport MidiEvent from \"@/core/midi/MidiEvent\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IStepSequencer = IModule<ModuleType.StepSequencer>;\n\n// Re-export types from transport for backward compatibility\nexport type { IStep, IStepNote, IStepCC, IPage, IPattern };\nexport { Resolution, PlaybackMode };\n\n// Module props (serialized)\nexport type IStepSequencerProps = {\n patterns: IPattern[];\n activePatternNo: number; // Currently selected pattern index\n activePageNo: number; // Currently selected page within pattern\n stepsPerPage: number; // 1-16 steps per page\n resolution: Resolution; // Step resolution (16th, 8th, etc.)\n playbackMode: PlaybackMode; // loop or oneShot\n patternSequence: string; // Pattern sequence notation (e.g., \"2A4B2AC\")\n enableSequence: boolean; // Toggle to enable/disable sequence mode\n};\n\n// Module state (temporal/runtime only, not serialized)\nexport type IStepSequencerState = {\n isRunning: boolean;\n currentStep: number; // For UI indicator\n sequencePosition?: string; // UI display: \"A (2/2)\"\n};\n\nconst MICROTIMING_STEP = TPB / 4 / 10;\n\nexport const stepSequencerPropSchema: ModulePropSchema<\n Pick<\n IStepSequencerProps,\n | \"activePatternNo\"\n | \"activePageNo\"\n | \"stepsPerPage\"\n | \"resolution\"\n | \"playbackMode\"\n | \"patternSequence\"\n | \"enableSequence\"\n >,\n {\n resolution: EnumProp<Resolution>;\n playbackMode: EnumProp<PlaybackMode>;\n }\n> = {\n activePatternNo: {\n kind: \"number\",\n label: \"Active pattern\",\n min: 0,\n max: 100,\n step: 1,\n },\n activePageNo: {\n kind: \"number\",\n label: \"Active page\",\n min: 0,\n max: 100,\n step: 1,\n },\n stepsPerPage: {\n kind: \"number\",\n min: 1,\n max: 16,\n step: 1,\n label: \"Steps per Page\",\n },\n resolution: {\n kind: \"enum\",\n options: Object.values(Resolution),\n label: \"Resolution\",\n },\n playbackMode: {\n kind: \"enum\",\n options: Object.values(PlaybackMode),\n label: \"Playback Mode\",\n },\n patternSequence: {\n kind: \"string\",\n label: \"Pattern Sequence\",\n },\n enableSequence: {\n kind: \"boolean\",\n label: \"Enable Sequence\",\n },\n};\n\nconst NOTE_DIVISIONS: Division[] = [\n \"1/64\",\n \"1/48\",\n \"1/32\",\n \"1/24\",\n \"1/16\",\n \"1/12\",\n \"1/8\",\n \"1/6\",\n \"3/16\",\n \"1/4\",\n \"5/16\",\n \"1/3\",\n \"3/8\",\n \"1/2\",\n \"3/4\",\n \"1\",\n \"1.5\",\n \"2\",\n \"3\",\n \"4\",\n \"6\",\n \"8\",\n \"16\",\n \"32\",\n];\n\nexport const stepPropSchema: ModulePropSchema<\n Pick<IStep, \"probability\" | \"duration\" | \"microtimeOffset\">,\n {\n duration: EnumProp<Division>;\n }\n> = {\n probability: {\n kind: \"number\",\n label: \"Probability\",\n min: 0,\n max: 100,\n step: 1,\n },\n duration: {\n kind: \"enum\",\n label: \"Duration\",\n options: NOTE_DIVISIONS,\n },\n microtimeOffset: {\n kind: \"number\",\n label: \"Microtiming\",\n min: -100,\n max: 100,\n step: 1,\n },\n};\n\n// Create a default empty step\nconst createDefaultStep = (): IStep => ({\n active: false,\n notes: [],\n ccMessages: [],\n probability: 100,\n microtimeOffset: 0,\n duration: \"1/16\",\n});\n\n// Create a default page with 16 empty steps\nconst createDefaultPage = (name: string): IPage => ({\n name,\n steps: Array.from({ length: 16 }, () => createDefaultStep()),\n});\n\n// Create a default pattern with one page\nconst createDefaultPattern = (name: string): IPattern => ({\n name,\n pages: [createDefaultPage(\"Page 1\")],\n});\n\nconst DEFAULT_PROPS: IStepSequencerProps = {\n patterns: [createDefaultPattern(\"A\")],\n activePatternNo: 0,\n activePageNo: 0,\n stepsPerPage: 16,\n resolution: Resolution.sixteenth,\n playbackMode: PlaybackMode.loop,\n patternSequence: \"\",\n enableSequence: false,\n};\n\nconst DEFAULT_STATE: IStepSequencerState = {\n isRunning: false,\n currentStep: 0,\n sequencePosition: undefined,\n};\n\ntype StepSequencerSetterHooks = Pick<\n SetterHooks<IStepSequencerProps>,\n | \"onSetActivePatternNo\"\n | \"onAfterSetPatternSequence\"\n | \"onAfterSetPatterns\"\n | \"onAfterSetResolution\"\n | \"onAfterSetPlaybackMode\"\n | \"onAfterSetEnableSequence\"\n>;\n\nexport default class StepSequencer\n extends Module<ModuleType.StepSequencer>\n implements StepSequencerSetterHooks\n{\n declare audioNode: undefined;\n midiOutput!: MidiOutput;\n\n private scheduledNotes = new Map<string, ContextTime>(); // Track scheduled note-offs\n private source?: StepSequencerSource;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.StepSequencer>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n // Initialize state\n this._state = { ...DEFAULT_STATE };\n\n this.registerOutputs();\n this.initializeSource();\n }\n\n onSetActivePatternNo: StepSequencerSetterHooks[\"onSetActivePatternNo\"] = (\n value,\n ) => {\n return Math.max(Math.min(value, this.props.patterns.length - 1), 0);\n };\n\n onAfterSetPatternSequence: StepSequencerSetterHooks[\"onAfterSetPatternSequence\"] =\n (value) => {\n if (!this.source) return;\n\n this.source.props = {\n ...this.source.props,\n patternSequence: value,\n };\n };\n\n onAfterSetPatterns: StepSequencerSetterHooks[\"onAfterSetPatterns\"] = (\n value,\n ) => {\n if (!this.source) return;\n\n this.source.props = {\n ...this.source.props,\n patterns: value,\n };\n };\n\n onAfterSetResolution: StepSequencerSetterHooks[\"onAfterSetResolution\"] = (\n value,\n ) => {\n if (!this.source) return;\n\n this.source.props = {\n ...this.source.props,\n resolution: value,\n };\n };\n\n onAfterSetPlaybackMode: StepSequencerSetterHooks[\"onAfterSetPlaybackMode\"] = (\n value,\n ) => {\n if (!this.source) return;\n\n this.source.props = {\n ...this.source.props,\n playbackMode: value,\n };\n };\n\n onAfterSetEnableSequence: StepSequencerSetterHooks[\"onAfterSetEnableSequence\"] =\n (value) => {\n if (!this.source) return;\n\n this.source.props = {\n ...this.source.props,\n enableSequence: value,\n };\n };\n\n private initializeSource() {\n this.source = new StepSequencerSource(this.engine.transport, {\n onEvent: this.handleStepEvent,\n patterns: this.props.patterns,\n stepsPerPage: this.props.stepsPerPage,\n resolution: this.props.resolution,\n playbackMode: this.props.playbackMode,\n patternSequence: this.props.patternSequence,\n enableSequence: this.props.enableSequence,\n });\n\n this.engine.transport.addSource(this.source);\n }\n\n private handleStepEvent = (event: StepSequencerSourceEvent) => {\n // Update state for UI\n this.state = {\n ...this.state,\n currentStep: event.stepNo,\n };\n\n // Update active page if changed\n if (event.pageNo !== this.props.activePageNo) {\n this.props = {\n ...this.props,\n activePageNo: event.pageNo,\n };\n }\n\n // Update active pattern if changed (for sequence mode)\n if (event.patternNo !== this.props.activePatternNo) {\n this.props = {\n ...this.props,\n activePatternNo: event.patternNo,\n };\n }\n\n // Trigger the step\n this.triggerStep(event.step, event.contextTime);\n\n // Trigger UI update\n this.triggerPropsUpdate();\n };\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi\" });\n }\n\n private triggerStep(step: IStep, contextTime: ContextTime) {\n if (!step.active) return;\n\n // Check if step has notes or CC messages\n if (step.notes.length === 0 && step.ccMessages.length === 0) return;\n\n // Check probability\n if (Math.random() * 100 > step.probability) return;\n\n const bpm = this.engine.bpm;\n\n // Send CC messages immediately\n step.ccMessages.forEach((ccMessage) => {\n this.sendCC(ccMessage, contextTime);\n });\n\n // Send notes\n const microtimeOffsetSeconds =\n (step.microtimeOffset / MICROTIMING_STEP) * (60 / bpm);\n const noteDurationSeconds =\n divisionToMilliseconds(step.duration, bpm) / 1000;\n\n const noteTime = contextTime + microtimeOffsetSeconds;\n\n step.notes.forEach((stepNote) => {\n this.sendNoteOn(stepNote, noteTime);\n if (noteDurationSeconds === Infinity) return;\n\n this.sendNoteOff(stepNote, noteTime + noteDurationSeconds);\n });\n }\n\n private sendNoteOn(stepNote: IStepNote, triggeredAt: ContextTime) {\n const note = new Note(stepNote.note);\n note.velocity = stepNote.velocity / 127; // Normalize to 0-1\n\n const midiEvent = MidiEvent.fromNote(note, true, triggeredAt);\n this.midiOutput.onMidiEvent(midiEvent);\n\n // Track scheduled note\n this.scheduledNotes.set(stepNote.note, triggeredAt);\n }\n\n private sendNoteOff(stepNote: IStepNote, triggeredAt: ContextTime) {\n const midiEvent = MidiEvent.fromNote(stepNote.note, false, triggeredAt);\n this.midiOutput.onMidiEvent(midiEvent);\n\n // Remove from scheduled notes\n this.scheduledNotes.delete(stepNote.note);\n }\n\n private sendCC(stepCC: IStepCC, triggeredAt: ContextTime) {\n const midiEvent = MidiEvent.fromCC(stepCC.cc, stepCC.value, triggeredAt);\n this.midiOutput.onMidiEvent(midiEvent);\n }\n\n // Called when transport starts\n start(contextTime: ContextTime): void {\n super.start(contextTime);\n\n this.state = { isRunning: true };\n this.scheduledNotes.clear();\n\n const ticks = this.engine.transport.getTicksAtContextTime(contextTime);\n this.source!.onStart(ticks);\n\n this.triggerPropsUpdate();\n }\n\n // Called when transport stops\n stop(contextTime: ContextTime): void {\n super.stop(contextTime);\n\n this.state = { isRunning: false };\n\n // Send all note-offs immediately\n this.scheduledNotes.forEach((_offTime, noteName) => {\n const midiEvent = MidiEvent.fromNote(noteName, false, contextTime);\n this.midiOutput.onMidiEvent(midiEvent);\n });\n\n this.scheduledNotes.clear();\n\n // Stop the source\n const ticks = this.engine.transport.getTicksAtContextTime(contextTime);\n this.source!.onStop(ticks);\n this.source!.onJump(0);\n\n // Reset UI indicator\n this.state = { currentStep: 0 };\n this.triggerPropsUpdate();\n }\n\n dispose() {\n if (!this.source) return;\n\n this.engine.transport.removeSource(this.source.id);\n }\n}\n","import { Context } from \"@blibliki/utils\";\nimport { StereoPannerNode } from \"@blibliki/utils/web-audio-api\";\nimport { IModule, Module, ModulePropSchema } from \"@/core\";\nimport { IModuleConstructor, SetterHooks } from \"@/core/module/Module\";\nimport { IPolyModuleConstructor, PolyModule } from \"@/core/module/PolyModule\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IStereoPanner = IModule<ModuleType.StereoPanner>;\nexport type IStereoPannerProps = {\n pan: number;\n};\n\nexport const stereoPannerPropSchema: ModulePropSchema<IStereoPannerProps> = {\n pan: {\n kind: \"number\",\n min: -1,\n max: 1,\n step: 0.01,\n label: \"Pan\",\n },\n};\n\nconst DEFAULT_PROPS: IStereoPannerProps = {\n pan: 0,\n};\n\nexport class MonoStereoPanner\n extends Module<ModuleType.StereoPanner>\n implements Pick<SetterHooks<IStereoPannerProps>, \"onAfterSetPan\">\n{\n declare audioNode: StereoPannerNode;\n\n constructor(\n engineId: string,\n params: ICreateModule<ModuleType.StereoPanner>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const audioNodeConstructor = (context: Context) =>\n new StereoPannerNode(context.audioContext);\n\n super(engineId, {\n ...params,\n audioNodeConstructor,\n props,\n });\n\n this.registerDefaultIOs();\n this.registerAdditionalInputs();\n }\n\n onAfterSetPan: SetterHooks<IStereoPannerProps>[\"onAfterSetPan\"] = (value) => {\n this.audioNode.pan.value = value;\n };\n\n private registerAdditionalInputs() {\n this.registerAudioInput({\n name: \"pan\",\n getAudioNode: () => this.audioNode.pan,\n });\n }\n}\n\nexport default class StereoPanner extends PolyModule<ModuleType.StereoPanner> {\n constructor(\n engineId: string,\n params: IPolyModuleConstructor<ModuleType.StereoPanner>,\n ) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n const monoModuleConstructor = (\n engineId: string,\n params: IModuleConstructor<ModuleType.StereoPanner>,\n ) => Module.create(MonoStereoPanner, engineId, params);\n\n super(engineId, {\n ...params,\n props,\n monoModuleConstructor,\n });\n\n this.registerAdditionalInputs();\n this.registerDefaultIOs();\n }\n\n private registerAdditionalInputs() {\n this.registerAudioInput({ name: \"pan\" });\n }\n}\n","import { ContextTime } from \"@blibliki/transport\";\nimport { Module, IModule, MidiOutput, Note, ModulePropSchema } from \"@/core\";\nimport MidiEvent from \"@/core/midi/MidiEvent\";\nimport { ICreateModule, ModuleType } from \".\";\n\nexport type IVirtualMidi = IModule<ModuleType.VirtualMidi>;\nexport type IVirtualMidiProps = {\n activeNotes: string[];\n};\n\nexport const virtualMidiPropSchema: ModulePropSchema<IVirtualMidiProps> = {\n activeNotes: {\n kind: \"array\",\n label: \"Active notes\",\n },\n};\n\nconst DEFAULT_PROPS: IVirtualMidiProps = { activeNotes: [] };\n\nexport default class VirtualMidi extends Module<ModuleType.VirtualMidi> {\n declare audioNode: undefined;\n midiOutput!: MidiOutput;\n\n constructor(engineId: string, params: ICreateModule<ModuleType.VirtualMidi>) {\n const props = { ...DEFAULT_PROPS, ...params.props };\n\n super(engineId, {\n ...params,\n props,\n });\n\n this.registerInputs();\n this.registerOutputs();\n }\n\n sendMidi(midiEvent: MidiEvent) {\n this.midiOutput.onMidiEvent(midiEvent);\n }\n\n triggerAttack = (note: Note, triggerAttack: ContextTime) => {\n this.props = { activeNotes: [...this.props.activeNotes, note.fullName] };\n this.triggerPropsUpdate();\n this.sendMidi(MidiEvent.fromNote(note, true, triggerAttack));\n };\n\n triggerRelease = (note: Note, triggerAttack: ContextTime) => {\n this.props = {\n activeNotes: this.props.activeNotes.filter(\n (name) => name !== note.fullName,\n ),\n };\n this.triggerPropsUpdate();\n this.sendMidi(MidiEvent.fromNote(note, false, triggerAttack));\n };\n\n private registerInputs() {\n this.registerMidiInput({\n name: \"midi in\",\n onMidiEvent: this.onMidiEvent,\n });\n }\n\n private registerOutputs() {\n this.midiOutput = this.registerMidiOutput({ name: \"midi out\" });\n }\n}\n","export { Engine } from \"./Engine\";\nexport type { ICreateRoute, IUpdateModule, IEngineSerialize } from \"./Engine\";\n\nexport type {\n IRoute,\n IIOSerialize,\n IModule,\n IModuleSerialize,\n IPolyModuleSerialize,\n IAnyModuleSerialize,\n IMidiDevice,\n ModulePropSchema,\n PropSchema,\n StringProp,\n NumberProp,\n EnumProp,\n BooleanProp,\n ArrayProp,\n INote,\n SetterHooks,\n StateSetterHooks,\n} from \"./core\";\nexport {\n MidiDevice,\n MidiInputDevice,\n MidiOutputDevice,\n MidiPortState,\n Note,\n} from \"./core\";\n\nexport { TransportState } from \"@blibliki/transport\";\nexport type { BPM, TimeSignature, Position } from \"@blibliki/transport\";\n\nexport { Context } from \"@blibliki/utils\";\n\nexport {\n ModuleType,\n moduleSchemas,\n OscillatorWave,\n MidiMappingMode,\n LFOWaveform,\n Resolution,\n PlaybackMode,\n stepPropSchema,\n NoiseType,\n DelayTimeMode,\n} from \"./modules\";\nexport { default as StepSequencer } from \"./modules/StepSequencer\";\nexport type {\n IOscillator,\n IGain,\n IMaster,\n IStepSequencerProps,\n IStepSequencerState,\n IStepSequencer,\n IStep,\n IPage,\n IPattern,\n IStepNote,\n IStepCC,\n ModuleTypeToPropsMapping,\n ModuleTypeToStateMapping,\n ICreateModule,\n ModuleParams,\n IMidiMapper,\n IMidiMapperProps,\n MidiMapping,\n ILFO,\n ILFOProps,\n INoise,\n} from \"./modules\";\n"],"mappings":"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","names":["Transport","assertDefined","Context","pick","uuidv4","upperFirst","uuidv4","requestAnimationFrame","assertNever","sortBy","deterministicId","uuidv4","requestAnimationFrame","PolyModule","ModuleClass","engineId","params","instance","id","name","moduleType","voices","monoModuleConstructor","props","uuidv4","InputCollection","OutputCollection","value","m","time","audioModule","from","to","output","input","callback","midiEvent","voiceNo","requestAnimationFrame","moduleByVoice","deterministicId","Engine","AudioParam","deterministicId","Base","module","props","deterministicId","io","plugOther","currentIO","callback","connections","otherIO","IO","PolyAudioInput","IO","io","plugOther","PolyAudioOutput","plugOrUnplug","voice","thisIO","otherIO","isPlug","maxVoices","thisMonoIO","otherMonoIO","AudioInput","IO","module","props","AudioOutput","io","plugOther","PolyAudioInput","input","AudioParam","MidiInput","IO","module","props","MidiOutput","event","input","IOCollection","collectionType","module","props","io","PolyModule","AudioInput","AudioOutput","Module","PolyAudioInput","PolyAudioOutput","MidiInput","MidiOutput","assertNever","callback","id","name","sortBy","InputCollection","OutputCollection","frequencyTable","frequencyTable_default","Notes","MIDI_OCTAVE_SYTSTEM","Note","_Note","frequency","noteName","note","freq","frequencyTable_default","message","dataByte","name","octave","noteOn","statusByte","string","matches","props","Message","data","statusByte","MidiEvent","_MidiEvent","noteName","noteOn","triggeredAt","note","Note","Message","cc","value","message","voiceNo","newEvent","Module","ModuleClass","engineId","params","instance","id","name","moduleType","voiceNo","audioNodeConstructor","props","uuidv4","InputCollection","OutputCollection","value","updatedValue","isFirstSet","key","propValue","result","hookType","hookName","upperFirst","hook","stateValue","audioModule","from","to","output","input","callback","_time","note","_triggeredAt","n","_event","midiEvent","triggeredAt","requestAnimationFrame","updateParams","Engine","uuidv4","Routes","engine","props","id","route","_","sourceIO","destinationIO","source","destination","MidiPortState","BaseMidiDevice","props","MAP_KEYS","Note","computerKeyboardData","ComputerKeyboardInput","context","id","name","state","callback","c","noteOn","event","note","midiEvent","MidiEvent","MidiInputDevice","BaseMidiDevice","input","context","callback","c","event","message","Message","midiEvent","MidiEvent","MidiOutputDevice","BaseMidiDevice","output","data","timestamp","isNode","isNode","NodeMidiInputPort","portIndex","name","input","callback","_deltaTime","message","event","cb","err","NodeMidiOutputPort","output","data","_timestamp","NodeMidiAccess","MidiModule","inputCount","i","portName","id","portInput","port","outputCount","portOutput","_event","_callback","NodeMidiAdapter","midi","midiModule","WebMidiInputPort","input","callback","e","event","cb","WebMidiOutputPort","output","data","timestamp","WebMidiAccess","midiAccess","port","midiPort","WebMidiAdapter","err","createMidiAdapter","isNode","NodeMidiAdapter","WebMidiAdapter","normalizeDeviceName","name","normalized","parts","longest","current","extractCoreTokens","tokens","genericWords","token","levenshteinDistance","str1","str2","len1","len2","matrix","i","j","cost","prevRow","currRow","prevCell","diagCell","prevCellInRow","calculateSimilarity","name1","name2","normalized1","normalized2","tokens1","tokens2","maxLen","intersection","x","union","jaccardScore","stringScore","substringScore","findBestMatch","targetName","candidateNames","threshold","bestMatch","candidateName","score","MidiDeviceManager","createMidiAdapter","context","id","name","threshold","input","output","d","targetName","deviceEntries","candidateNames","match","findBestMatch","device","callback","MidiInputDevice","MidiOutputDevice","err","computerKeyboardDevice","ComputerKeyboardInput","port","listener","assertNever","voiceSchedulerPropSchema","DEFAULT_PROPS","Voice","Module","engineId","params","props","time","range","relevantRanges","currentTime","midiEvent","triggeredAt","note","type","noteName","isFutureEvent","r","VoiceScheduler","PolyModule","monoModuleConstructor","voice","v","targetTime","isRealTime","a","b","aEndTime","bEndTime","stolenNoteName","releaseTime","noteOffEvent","MidiEvent","GainNode","WetDryMixer","context","audioContext","source","mix","dryGain","wetGain","chorusPropSchema","DEFAULT_PROPS","Chorus","Module","engineId","params","props","audioNodeConstructor","context","GainNode","audioContext","WetDryMixer","depthInSeconds","now","phaseOffset","value","cappedValue","ConstantSourceNode","constantPropSchema","DEFAULT_PROPS","Constant","Module","engineId","params","props","audioNodeConstructor","context","ConstantSourceNode","value","time","note","triggeredAt","_","divisionToMilliseconds","DelayTimeMode","NOTE_DIVISIONS","delayPropSchema","DEFAULT_DELAY_PROPS","Delay","Module","engineId","params","props","audioNodeConstructor","context","WetDryMixer","timeInSeconds","bpm","divisionToMilliseconds","_value","value","maxTime","cappedValue","GainNode","distortionPropSchema","DEFAULT_PROPS","MonoDistortion","Module","engineId","params","props","audioNodeConstructor","context","GainNode","audioContext","WetDryMixer","drive","buffer","curve","i","driven","value","PolyDistortion","PolyModule","monoModuleConstructor","GainNode","assertNever","customEnvelopeProcessorURL","CustomEnvelopeProcessor","_inputs","outputs","parameters","output","trigs","atk","dec","sus","rel","atkmax","atkRatio","decRatio","relRatio","i","channel","filterProcessorURL","FilterProcessor","inputs","outputs","parameters","input","output","cutoff","resonance","channelNum","inputChannel","outputChannel","i","s","cutoffHz","clampedHz","normalizedCutoff","c","resonanceValue","mrc","lfoProcessorURL","LFOProcessor","_inputs","outputs","parameters","output","frequencyValues","waveformValues","phaseValues","blockSize","i","frequency","waveformIdx","phaseOffset","currentPhase","sample","channel","phaseIncrement","scaleProcessorURL","ScaleProcessor","inputs","outputs","parameters","input","output","minValues","maxValues","currentValues","modeValues","firstInput","outputChannel","current","channel","inputChannel","i","x","min","max","mode","loadProcessors","context","scaleProcessorURL","filterProcessorURL","lfoProcessorURL","customEnvelopeProcessorURL","newAudioWorklet","worklet","assertNever","DEFAULT_PROPS","customEnvelopePropSchema","MonoCustomEnvelope","Module","engineId","params","props","audioNodeConstructor","context","audioNode","newAudioWorklet","GainNode","value","note","triggeredAt","CustomEnvelope","PolyModule","monoModuleConstructor","BiquadFilterNode","GainNode","gainPropSchema","DEFAULT_PROPS","MonoGain","Module","engineId","params","props","audioNodeConstructor","context","GainNode","value","Gain","PolyModule","monoModuleConstructor","scalePropSchema","DEFAULT_PROPS","MonoScale","Module","engineId","params","props","audioNodeConstructor","context","newAudioWorklet","value","Scale","PolyModule","monoModuleConstructor","MIN_FREQ","MAX_FREQ","DEFAULT_PROPS","filterPropSchema","MonoFilter","Module","engineId","params","props","audioNodeConstructor","context","BiquadFilterNode","MonoGain","MonoScale","value","Filter","PolyModule","monoModuleConstructor","AnalyserNode","inspectorPropSchema","DEFAULT_PROPS","Inspector","Module","engineId","params","props","audioNodeConstructor","context","AnalyserNode","value","divisionToFrequency","ConstantSourceNode","GainNode","LFOWaveform","DIVISIONS","DEFAULT_PROPS","lfoPropSchema","MonoLFO","Module","engineId","params","props","audioNodeConstructor","context","newAudioWorklet","ctx","ConstantSourceNode","GainNode","waveformIndex","bpm","frequency","divisionToFrequency","offset","amplitude","dcOffset","value","LFO","PolyModule","monoModuleConstructor","GainNode","DEFAULT_PROPS","envelopePropSchema","MIN_GAIN","MonoEnvelope","Module","engineId","params","props","audioNodeConstructor","context","audioNode","GainNode","note","triggeredAt","attack","decay","sustain","gain","resetTimeConstant","resetDuration","attackStartTime","attackEndTime","decayEndTime","sustainValue","release","Envelope","PolyModule","monoModuleConstructor","DEFAULT_PROPS","masterPropSchema","Master","Module","engineId","params","props","audioNodeConstructor","context","midiInputPropSchema","DEFAULT_PROPS","MidiInput","Module","engineId","params","props","midiDevice","fuzzyMatch","value","midiEvent","MidiMappingMode","midiMapperPropSchema","DEFAULT_PROPS","getMidiFromMappedValue","value","midiValue","propSchema","mapping","min","max","exp","threshold","mode","curvedValue","newMidiValue","MidiMapper","Module","engineId","params","props","activePage","newPage","now","midiEvent","MidiEvent","event","triggeredAt","matchingMappings","m","updatedGlobalMappings","updatedPageMappings","updatedPages","page","index","_triggeredAt","propName","mappedModule","moduleSchemas","mappedValue","currentValue","normalizedMidi","steps","optionIndex","clampedIndex","hasGlobalAutoAssign","autoAssign","hasPageAutoAssign","midiOutputPropSchema","DEFAULT_PROPS","MidiOutput","Module","engineId","params","props","midiDevice","fuzzyMatch","value","midiEvent","rawData","AudioBufferSourceNode","NoiseType","noisePropSchema","DEFAULT_PROPS","generateWhiteNoise","context","duration","sampleRate","length","buffer","channel","data","i","generatePinkNoise","b0","b1","b2","b3","b4","b5","b6","white","pink","generateBrownNoise","lastOut","generateBlueNoise","lastWhite","Noise","Module","engineId","params","props","audioNodeConstructor","AudioBufferSourceNode","bufferDuration","type","wasStarted","currentTime","time","dbToGain","GainNode","OscillatorNode","LOW_GAIN","OscillatorWave","oscillatorPropSchema","DEFAULT_PROPS","MonoOscillator","Module","engineId","params","props","audioNodeConstructor","context","OscillatorNode","GainNode","dbToGain","value","lowGain","time","note","triggeredAt","lastNote","frequency","coarse","octave","fine","actionAt","Oscillator","PolyModule","monoModuleConstructor","audioModule","reverbPropSchema","DEFAULT_REVERB_PROPS","generateImpulseResponse","context","type","decayTime","sampleRate","effectiveDecayTime","length","buffer","tuning","getRoomTuning","generateEarlyReflections","generateDiffuseTail","normalizeBuffer","reflectionTimes","channel","data","channelOffset","reflectionMs","time","position","burstLength","amplitude","i","noise","envelope","applyLowpass","decay","damping","cutoff","y1","a","sample","max","scale","Reverb","Module","engineId","params","props","audioNodeConstructor","WetDryMixer","value","impulse","divisionToMilliseconds","TPB","StepSequencerSource","Resolution","PlaybackMode","MICROTIMING_STEP","TPB","stepSequencerPropSchema","Resolution","PlaybackMode","NOTE_DIVISIONS","stepPropSchema","createDefaultStep","createDefaultPage","name","createDefaultPattern","DEFAULT_PROPS","DEFAULT_STATE","StepSequencer","Module","engineId","params","props","value","StepSequencerSource","event","step","contextTime","bpm","ccMessage","microtimeOffsetSeconds","noteDurationSeconds","divisionToMilliseconds","noteTime","stepNote","triggeredAt","note","Note","midiEvent","MidiEvent","stepCC","ticks","_offTime","noteName","StereoPannerNode","stereoPannerPropSchema","DEFAULT_PROPS","MonoStereoPanner","Module","engineId","params","props","audioNodeConstructor","context","StereoPannerNode","value","StereoPanner","PolyModule","monoModuleConstructor","virtualMidiPropSchema","DEFAULT_PROPS","VirtualMidi","Module","engineId","params","props","midiEvent","note","triggerAttack","MidiEvent","name","ModuleType","moduleSchemas","oscillatorPropSchema","gainPropSchema","masterPropSchema","midiInputPropSchema","midiOutputPropSchema","envelopePropSchema","customEnvelopePropSchema","filterPropSchema","scalePropSchema","stereoPannerPropSchema","inspectorPropSchema","chorusPropSchema","constantPropSchema","delayPropSchema","distortionPropSchema","midiMapperPropSchema","virtualMidiPropSchema","stepSequencerPropSchema","voiceSchedulerPropSchema","lfoPropSchema","noisePropSchema","reverbPropSchema","createModule","engineId","params","Oscillator","Gain","Master","MidiInput","MidiOutput","Envelope","CustomEnvelope","Filter","Scale","StereoPanner","Inspector","Chorus","Constant","Delay","PolyDistortion","MidiMapper","VirtualMidi","StepSequencer","VoiceScheduler","LFO","Noise","Reverb","assertNever","Engine","_Engine","id","engine","assertDefined","data","bpm","timeSignature","modules","routes","context","Context","m","r","uuidv4","Transport","Routes","MidiDeviceManager","loadProcessors","params","module","createModule","updates","pick","PolyModule","props","source","destination","output","input","actionAt","value","moduleId","ioName","type","name","threshold","callback","noteName","virtualMidi","MidiEvent","ticks","TransportState","Context"]}
|