@blibliki/engine 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/core/Note/index.ts +1 -1
- package/src/core/midi/Message.ts +46 -0
- package/src/core/midi/MidiDevice.ts +17 -10
- package/src/core/midi/MidiDeviceManager.ts +43 -33
- package/src/core/midi/MidiEvent.ts +1 -1
- package/src/core/midi/adapters/NodeMidiAdapter.ts +174 -0
- package/src/core/midi/adapters/WebMidiAdapter.ts +120 -0
- package/src/core/midi/adapters/index.ts +23 -0
- package/src/core/midi/adapters/types.ts +32 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import * as _blibliki_utils from '@blibliki/utils';
|
|
2
2
|
import { Context, Optional, EmptyObject } from '@blibliki/utils';
|
|
3
3
|
export { Context } from '@blibliki/utils';
|
|
4
|
-
import { Message, Input } from 'webmidi';
|
|
5
4
|
import * as _blibliki_transport from '@blibliki/transport';
|
|
6
5
|
import { Seconds, ContextTime, Transport, TransportEvent, BPM, TimeSignature } from '@blibliki/transport';
|
|
7
6
|
export { Position, TimeSignature, TransportState } from '@blibliki/transport';
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Simple wrapper around MIDI message data (Uint8Array)
|
|
10
|
+
* Replaces the webmidi Message class with native Web MIDI API data
|
|
11
|
+
*/
|
|
12
|
+
declare class Message {
|
|
13
|
+
readonly data: Uint8Array;
|
|
14
|
+
constructor(data: Uint8Array);
|
|
15
|
+
/**
|
|
16
|
+
* Returns the data bytes (excluding the status byte)
|
|
17
|
+
*/
|
|
18
|
+
get dataBytes(): number[];
|
|
19
|
+
/**
|
|
20
|
+
* Returns the MIDI message type based on the status byte
|
|
21
|
+
*/
|
|
22
|
+
get type(): string;
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
type INote = {
|
|
10
26
|
name: string;
|
|
11
27
|
octave: number;
|
|
@@ -58,6 +74,23 @@ declare class MidiEvent {
|
|
|
58
74
|
clone(voiceNo?: number): MidiEvent;
|
|
59
75
|
}
|
|
60
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Platform-agnostic MIDI interfaces
|
|
79
|
+
* Allows switching between Web MIDI API and node-midi without refactoring
|
|
80
|
+
*/
|
|
81
|
+
interface IMidiMessageEvent {
|
|
82
|
+
data: Uint8Array;
|
|
83
|
+
timeStamp: number;
|
|
84
|
+
}
|
|
85
|
+
type MidiMessageCallback = (event: IMidiMessageEvent) => void;
|
|
86
|
+
interface IMidiInputPort {
|
|
87
|
+
readonly id: string;
|
|
88
|
+
readonly name: string;
|
|
89
|
+
readonly state: "connected" | "disconnected";
|
|
90
|
+
addEventListener(callback: MidiMessageCallback): void;
|
|
91
|
+
removeEventListener(callback: MidiMessageCallback): void;
|
|
92
|
+
}
|
|
93
|
+
|
|
61
94
|
declare enum MidiPortState {
|
|
62
95
|
connected = "connected",
|
|
63
96
|
disconnected = "disconnected"
|
|
@@ -77,7 +110,8 @@ declare class MidiDevice implements IMidiDevice {
|
|
|
77
110
|
eventListerCallbacks: EventListerCallback[];
|
|
78
111
|
private context;
|
|
79
112
|
private input;
|
|
80
|
-
|
|
113
|
+
private messageHandler;
|
|
114
|
+
constructor(input: IMidiInputPort, context: Context);
|
|
81
115
|
get state(): MidiPortState;
|
|
82
116
|
connect(): void;
|
|
83
117
|
disconnect(): void;
|
|
@@ -753,6 +787,8 @@ declare class MidiDeviceManager {
|
|
|
753
787
|
private initialized;
|
|
754
788
|
private listeners;
|
|
755
789
|
private context;
|
|
790
|
+
private midiAccess;
|
|
791
|
+
private adapter;
|
|
756
792
|
constructor(context: Context);
|
|
757
793
|
initialize(): Promise<void>;
|
|
758
794
|
find(id: string): MidiDevice | ComputerKeyboardInput | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import * as _blibliki_utils from '@blibliki/utils';
|
|
2
2
|
import { Context, Optional, EmptyObject } from '@blibliki/utils';
|
|
3
3
|
export { Context } from '@blibliki/utils';
|
|
4
|
-
import { Message, Input } from 'webmidi';
|
|
5
4
|
import * as _blibliki_transport from '@blibliki/transport';
|
|
6
5
|
import { Seconds, ContextTime, Transport, TransportEvent, BPM, TimeSignature } from '@blibliki/transport';
|
|
7
6
|
export { Position, TimeSignature, TransportState } from '@blibliki/transport';
|
|
8
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Simple wrapper around MIDI message data (Uint8Array)
|
|
10
|
+
* Replaces the webmidi Message class with native Web MIDI API data
|
|
11
|
+
*/
|
|
12
|
+
declare class Message {
|
|
13
|
+
readonly data: Uint8Array;
|
|
14
|
+
constructor(data: Uint8Array);
|
|
15
|
+
/**
|
|
16
|
+
* Returns the data bytes (excluding the status byte)
|
|
17
|
+
*/
|
|
18
|
+
get dataBytes(): number[];
|
|
19
|
+
/**
|
|
20
|
+
* Returns the MIDI message type based on the status byte
|
|
21
|
+
*/
|
|
22
|
+
get type(): string;
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
type INote = {
|
|
10
26
|
name: string;
|
|
11
27
|
octave: number;
|
|
@@ -58,6 +74,23 @@ declare class MidiEvent {
|
|
|
58
74
|
clone(voiceNo?: number): MidiEvent;
|
|
59
75
|
}
|
|
60
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Platform-agnostic MIDI interfaces
|
|
79
|
+
* Allows switching between Web MIDI API and node-midi without refactoring
|
|
80
|
+
*/
|
|
81
|
+
interface IMidiMessageEvent {
|
|
82
|
+
data: Uint8Array;
|
|
83
|
+
timeStamp: number;
|
|
84
|
+
}
|
|
85
|
+
type MidiMessageCallback = (event: IMidiMessageEvent) => void;
|
|
86
|
+
interface IMidiInputPort {
|
|
87
|
+
readonly id: string;
|
|
88
|
+
readonly name: string;
|
|
89
|
+
readonly state: "connected" | "disconnected";
|
|
90
|
+
addEventListener(callback: MidiMessageCallback): void;
|
|
91
|
+
removeEventListener(callback: MidiMessageCallback): void;
|
|
92
|
+
}
|
|
93
|
+
|
|
61
94
|
declare enum MidiPortState {
|
|
62
95
|
connected = "connected",
|
|
63
96
|
disconnected = "disconnected"
|
|
@@ -77,7 +110,8 @@ declare class MidiDevice implements IMidiDevice {
|
|
|
77
110
|
eventListerCallbacks: EventListerCallback[];
|
|
78
111
|
private context;
|
|
79
112
|
private input;
|
|
80
|
-
|
|
113
|
+
private messageHandler;
|
|
114
|
+
constructor(input: IMidiInputPort, context: Context);
|
|
81
115
|
get state(): MidiPortState;
|
|
82
116
|
connect(): void;
|
|
83
117
|
disconnect(): void;
|
|
@@ -753,6 +787,8 @@ declare class MidiDeviceManager {
|
|
|
753
787
|
private initialized;
|
|
754
788
|
private listeners;
|
|
755
789
|
private context;
|
|
790
|
+
private midiAccess;
|
|
791
|
+
private adapter;
|
|
756
792
|
constructor(context: Context);
|
|
757
793
|
initialize(): Promise<void>;
|
|
758
794
|
find(id: string): MidiDevice | ComputerKeyboardInput | undefined;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Transport as gt}from"@blibliki/transport";import{assertDefined as je,Context as It,pick as Pt,uuidv4 as Tt}from"@blibliki/utils";import{upperFirst as ot,uuidv4 as it}from"@blibliki/utils";import{assertNever as Xe}from"@blibliki/utils";import{sortBy as Ze}from"es-toolkit";import{deterministicId as Qe,uuidv4 as Ye}from"@blibliki/utils";var m=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;_voices;_name;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voices:u,monoModuleConstructor:d,props:s}=t;this.audioModules=[],this.monoModuleConstructor=d,this.id=o??Ye(),this.engineId=e,this.name=i,this.moduleType=r,this._props=s,this.inputs=new S(this),this.outputs=new x(this),queueMicrotask(()=>{this.voices=u||1,this.props=s})}get name(){return this._name}set name(e){this._name=e,this.audioModules.forEach(t=>t.name=e)}get props(){return this._props}set props(e){this._props={...this._props,...e},this.audioModules.forEach(t=>t.props=e)}get voices(){return this._voices}set voices(e){this._voices=e,this.adjustNumberOfModules(),this.rePlugAll()}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voices:this.voices,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll(),this.audioModules.forEach(e=>{e.dispose()})}onMidiEvent=e=>{let t=e.voiceNo??0;this.findVoice(t).onMidiEvent(e)};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){requestAnimationFrame(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voices:this.voices,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}findVoice(e){let t=this.audioModules.find(o=>o.voiceNo===e);if(!t)throw Error(`Voice ${e} on module ${this.name} not found`);return t}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),(e==="in"||e==="both")&&this.registerAudioInput({name:"in"}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out"})}registerAudioInput(e){return this.inputs.add({...e,ioType:"polyAudioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"polyAudioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}adjustNumberOfModules(){if(this.audioModules.length!==this.voices){if(this.audioModules.length>this.voices)this.audioModules.pop()?.dispose();else{let e=this.audioModules.length,t=Qe(this.id,e.toString()),o=this.monoModuleConstructor(this.engineId,{id:t,name:this.name,moduleType:this.moduleType,voiceNo:e,props:{...this.props}});this.audioModules.push(o)}this.adjustNumberOfModules()}}get engine(){return A.getById(this.engineId)}get context(){return this.engine.context}};import{deterministicId as Je}from"@blibliki/utils";var ne=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=Je(this.module.id,this.name),this.connections=[]}plug(e,t=!0){this.connections.push(e),t&&e.plug(this,!1)}unPlug(e,t=!0){this.connections=this.connections.filter(o=>o.id!==e.id),t&&e.unPlug(this,!1)}rePlugAll(e){let t=this.connections;this.unPlugAll(),e&&e(),t.forEach(o=>{this.plug(o)})}unPlugAll(){this.connections.forEach(e=>{this.unPlug(e)})}isAudio(){return this.ioType==="audioInput"||this.ioType==="audioOutput"||this.ioType==="polyAudioInput"||this.ioType==="polyAudioOutput"}isMidi(){return this.ioType==="midiInput"||this.ioType==="midiOutput"}serialize(){return{id:this.id,name:this.name,ioType:this.ioType,moduleId:this.module.id}}},g=class extends ne{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var P=class extends g{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof O)&&J(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof O)&&J(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},O=class extends g{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof P)&&J(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof P)&&J(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function J(n,e,t){if(e instanceof P||e instanceof O){let o=Math.max(n.module.voices,e.module.voices);for(let i=0;i<o;i++){let r=n.findIOByVoice(i%n.module.voices),u=e.findIOByVoice(i%e.module.voices);t?r.plug(u):r.unPlug(u)}}else for(let o=0;o<n.module.voices;o++){let i=n.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var k=class extends g{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},w=class extends g{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof P)return;let o=e.getAudioNode();o instanceof AudioParam?this.getAudioNode().connect(o):this.getAudioNode().connect(o)}unPlug(e,t=!0){if(super.unPlug(e,t),e instanceof P)return;let o=e.getAudioNode();try{o instanceof AudioParam?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var C=class extends g{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},D=class extends g{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof C)}};var F=class{module;collection=[];collectionType;constructor(e,t){this.collectionType=e,this.module=t}add(e){let t;switch(this.validateUniqName(e.name),e.ioType){case"audioInput":if(this.module instanceof m)throw Error("Not compatible");t=new k(this.module,e);break;case"audioOutput":if(this.module instanceof m)throw Error("Not compatible");t=new w(this.module,e);break;case"polyAudioInput":if(this.module instanceof p)throw Error("Not compatible");t=new P(this.module,e);break;case"polyAudioOutput":if(this.module instanceof p)throw Error("Not compatible");t=new O(this.module,e);break;case"midiInput":t=new C(this.module,e);break;case"midiOutput":t=new D(this.module,e);break;default:Xe(e)}return this.collection.push(t),t}unPlugAll(){this.collection.forEach(e=>{e.unPlugAll()})}rePlugAll(e){this.collection.forEach(t=>{t.rePlugAll(e)})}find(e){let t=this.collection.find(o=>o.id===e);if(!t)throw Error(`The io with id ${e} is not exists`);return t}findByName(e){let t=this.collection.find(o=>o.name===e);if(!t)throw Error(`The io with name ${e} is not exists`);return t}serialize(){return Ze(this.collection,[e=>e.isMidi()?-1:1]).map(e=>e.serialize())}validateUniqName(e){if(this.collection.some(t=>t.name===e))throw Error(`An io with name ${e} is already exists`)}},S=class extends F{constructor(e){super("Input",e)}},x=class extends F{constructor(e){super("Output",e)}};import{Message as Ie}from"webmidi";var et=new Map([["C0",16.35],["C#0",17.32],["Db0",17.32],["D0",18.35],["D#0",19.45],["Eb0",19.45],["E0",20.6],["F0",21.83],["F#0",23.12],["Gb0",23.12],["G0",24.5],["G#0",25.96],["Ab0",25.96],["A0",27.5],["A#0",29.14],["Bb0",29.14],["B0",30.87],["C1",32.7],["C#1",34.65],["Db1",34.65],["D1",36.71],["D#1",38.89],["Eb1",38.89],["E1",41.2],["F1",43.65],["F#1",46.25],["Gb1",46.25],["G1",49],["G#1",51.91],["Ab1",51.91],["A1",55],["A#1",58.27],["Bb1",58.27],["B1",61.74],["C2",65.41],["C#2",69.3],["Db2",69.3],["D2",73.42],["D#2",77.78],["Eb2",77.78],["E2",82.41],["F2",87.31],["F#2",92.5],["Gb2",92.5],["G2",98],["G#2",103.83],["Ab2",103.83],["A2",110],["A#2",116.54],["Bb2",116.54],["B2",123.47],["C3",130.81],["C#3",138.59],["Db3",138.59],["D3",146.83],["D#3",155.56],["Eb3",155.56],["E3",164.81],["F3",174.61],["F#3",185],["Gb3",185],["G3",196],["G#3",207.65],["Ab3",207.65],["A3",220],["A#3",233.08],["Bb3",233.08],["B3",246.94],["C4",261.63],["C#4",277.18],["Db4",277.18],["D4",293.66],["D#4",311.13],["Eb4",311.13],["E4",329.63],["F4",349.23],["F#4",369.99],["Gb4",369.99],["G4",392],["G#4",415.3],["Ab4",415.3],["A4",440],["A#4",466.16],["Bb4",466.16],["B4",493.88],["C5",523.25],["C#5",554.37],["Db5",554.37],["D5",587.33],["D#5",622.25],["Eb5",622.25],["E5",659.26],["F5",698.46],["F#5",739.99],["Gb5",739.99],["G5",783.99],["G#5",830.61],["Ab5",830.61],["A5",880],["A#5",932.33],["Bb5",932.33],["B5",987.77],["C6",1046.5],["C#6",1108.73],["Db6",1108.73],["D6",1174.66],["D#6",1244.51],["Eb6",1244.51],["E6",1318.51],["F6",1396.91],["F#6",1479.98],["Gb6",1479.98],["G6",1567.98],["G#6",1661.22],["Ab6",1661.22],["A6",1760],["A#6",1864.66],["Bb6",1864.66],["B6",1975.53],["C7",2093],["C#7",2217.46],["Db7",2217.46],["D7",2349.32],["D#7",2489.02],["Eb7",2489.02],["E7",2637.02],["F7",2793.83],["F#7",2959.96],["Gb7",2959.96],["G7",3135.96],["G#7",3322.44],["Ab7",3322.44],["A7",3520],["A#7",3729.31],["Bb7",3729.31],["B7",3951.07],["C8",4186.01],["C#8",4434.92],["Db8",4434.92],["D8",4698.64],["D#8",4978.03],["Eb8",4978.03]]),se=et;var ue=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],tt=2,a=class n{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of se)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new n(t)}static fromEvent(e){let t=ue[e.data[1]%12],o=Math.floor(e.data[1]/12)-2;return new n(`${t}${o}`)}static notes(e=3){return ue.map(t=>new n(`${t}${e}`))}constructor(e){typeof e=="string"?this.fromString(e):this.fromProps(e)}get isSemi(){return this.name.endsWith("#")}get fullName(){return`${this.name}${this.octave}`}get frequency(){return se.get(`${this.name}${this.octave}`)}midiData(e=!0){let t=e?144:128;return new Uint8Array([t,this.midiNumber,this.velocity*100])}get midiNumber(){return(this.octave+tt)*12+this.noteIndex}get noteIndex(){return ue.indexOf(this.name)}valueOf(){return this.fullName}serialize(){return{name:this.name,octave:this.octave,frequency:this.frequency,velocity:this.velocity,duration:this.duration}}fromString(e){let t=/(\w#?)(\d)?/.exec(e)??[];this.name=t[1],this.octave=t[2]?parseInt(t[2]):1}fromProps(e){Object.assign(this,e)}};var f=class n{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof a?e:new a(e);return new n(new Ie(i.midiData(t)),o)}static fromCC(e,t,o){return new n(new Ie(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t,this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}get isCC(){return this.type==="controlchange"}get cc(){if(this.isCC)return this.message.dataBytes[0]}get ccValue(){if(this.isCC)return this.message.dataBytes[1]}defineNotes(){this.isNote&&(this.note||(this.note=a.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new n(this.message,this.triggeredAt);return t.voiceNo=e,t}};var p=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;activeNotes;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voiceNo:u,audioNodeConstructor:d,props:s}=t;this.id=o??it(),this.engineId=e,this.name=i,this.moduleType=r,this.voiceNo=u??0,this.activeNotes=[],this.audioNode=d?.(this.context),this._props=s,this.inputs=new S(this),this.outputs=new x(this),queueMicrotask(()=>{this.props=s})}get props(){return this._props}set props(e){let t={...e};Object.keys(e).forEach(o=>{let i=e[o];if(i!==void 0){let r=this.callPropHook("onSet",o,i);r!==void 0&&(t[o]=r)}}),this._props={...this._props,...t},Object.keys(t).forEach(o=>{let i=t[o];i!==void 0&&this.callPropHook("onAfterSet",o,i)})}callPropHook(e,t,o){let i=`${e}${ot(t)}`,r=this[i];if(typeof r=="function")return r.call(this,o)}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voiceNo:this.voiceNo,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}start(e){}stop(e){}triggerAttack(e,t){this.activeNotes.some(o=>o.fullName===e.fullName)||this.activeNotes.push(e)}triggerRelease(e,t){this.activeNotes=this.activeNotes.filter(o=>o.fullName!==e.fullName)}handleCC(e,t){}onMidiEvent=e=>{let{note:t,triggeredAt:o}=e;switch(e.type){case"noteon":{this.triggerAttack(t,o);break}case"noteoff":this.triggerRelease(t,o);break;case"controlchange":this.handleCC(e,o);break;default:throw Error("This type is not a note")}};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){requestAnimationFrame(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),this.audioNode&&((e==="in"||e==="both")&&this.registerAudioInput({name:"in",getAudioNode:()=>this.audioNode}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out",getAudioNode:()=>this.audioNode}))}registerAudioInput(e){return this.inputs.add({...e,ioType:"audioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"audioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}get engine(){return A.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as rt}from"@blibliki/utils";var G=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??rt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){this.routes.forEach((e,t)=>{this.removeRoute(t)})}replug(){this.routes.forEach((e,t)=>{let{sourceIO:o,destinationIO:i}=this.getIOs(t);o.rePlugAll(),i.rePlugAll()})}serialize(){return Array.from(this.routes.values())}plug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.plug(o)}unPlug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.unPlug(o)}find(e){let t=this.routes.get(e);if(!t)throw Error(`Route with id ${e} not found`);return t}getIOs(e){let t=this.find(e),{source:o,destination:i}=t,r=this.engine.findIO(o.moduleId,o.ioName,"output"),u=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:r,destinationIO:u}}};import{Output as Pe,WebMidi as Z}from"webmidi";var X=(t=>(t.connected="connected",t.disconnected="disconnected",t))(X||{}),v=class{id;name;eventListerCallbacks=[];context;input;constructor(e,t){this.id=e.id,this.name=e.name||`Device ${e.id}`,this.input=e,this.context=t,this.connect()}get state(){return this.input.state}connect(){this.input.addListener("midimessage",e=>{this.processEvent(e)})}disconnect(){this.input.removeListener()}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}processEvent(e){let t=new f(e.message,this.context.browserToContextTime(e.timestamp));switch(t.type){case"noteon":case"noteoff":case"controlchange":this.eventListerCallbacks.forEach(o=>{o(t)})}}};var nt={a:new a("C3"),s:new a("D3"),d:new a("E3"),f:new a("F3"),g:new a("G3"),h:new a("A3"),j:new a("B3"),k:new a("C4"),l:new a("D4"),w:new a("C#3"),e:new a("D#3"),t:new a("F#3"),y:new a("G#3"),u:new a("A#3"),o:new a("C#4"),p:new a("D#4")},st=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),b=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=st();this.id=t,this.name=o,this.state=i,this.context=e,document.addEventListener("keydown",this.onKeyTrigger(!0)),document.addEventListener("keyup",this.onKeyTrigger(!1))}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}onKeyTrigger=e=>t=>{let o=this.extractNote(t);if(!o)return;let i=f.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(r=>{r(i)})};extractNote(e){if(!e.repeat)return nt[e.key]}};var N=class{devices=new Map;initialized=!1;listeners=[];context;constructor(e){this.context=e,this.addComputerKeyboard()}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.devices.get(e)}findByName(e){return Array.from(this.devices.values()).find(t=>t.name===e)}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{await Z.enable(),Z.inputs.forEach(e=>{this.devices.has(e.id)||this.devices.set(e.id,new v(e,this.context))})}catch(e){console.error("Error enabling WebMidi:",e)}}addComputerKeyboard(){if(typeof document>"u")return;let e=new b(this.context);this.devices.set(e.id,e)}listenChanges(){Z.addListener("connected",e=>{let t=e.port;if(t instanceof Pe||this.devices.has(t.id))return;let o=new v(t,this.context);this.devices.set(o.id,o),this.listeners.forEach(i=>{i(o)})}),Z.addListener("disconnected",e=>{let t=e.port;if(t instanceof Pe)return;let o=this.devices.get(t.id);o&&(o instanceof b||(o.disconnect(),this.devices.delete(o.id),this.listeners.forEach(i=>{i(o)})))})}};import{assertNever as yt}from"@blibliki/utils";var Te={},ve={},de=class extends p{activeNote=null;triggeredAt=0;constructor(e,t){let o={...ve,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let r=o.fullName;switch(i){case"noteon":this.activeNote=r,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},V=class extends m{midiOutput;constructor(e,t){let o={...ve,...t.props},i=(r,u)=>new de(r,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerOutputs()}onMidiEvent=e=>{let t;switch(e.type){case"noteon":t=this.findFreeVoice();break;case"noteoff":t=this.audioModules.find(o=>o.activeNote===e.note.fullName);break;default:throw Error("This type is not a note")}t&&(t.midiTriggered(e),e.voiceNo=t.voiceNo,this.midiOutput.onMidiEvent(e))};findFreeVoice(){let e=this.audioModules.find(t=>!t.activeNote);return e??=this.audioModules.sort((t,o)=>t.triggeredAt-o.triggeredAt)[0],e}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var Se={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},ut={value:1},q=class extends p{isStated=!1;constructor(e,t){let o={...ut,...t.props},i=r=>new ConstantSourceNode(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onAfterSetValue=e=>{this.audioNode.offset.value=e};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new ConstantSourceNode(this.context.audioContext,{offset:this.props.value})}),this.isStated=!1)}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,t),this.start(t)};triggerRelease=()=>{}};import{cancelAndHoldAtTime as xe}from"@blibliki/utils";var Ae={attack:.01,decay:0,sustain:1,release:0},Oe={attack:{kind:"number",min:1e-4,max:20,step:.01,exp:3,label:"Attack"},decay:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Release"}},ae=class extends p{constructor(e,t){let o={...Ae,...t.props},i=r=>{let u=new GainNode(r.audioContext);return u.gain.value=0,u};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let o=this.props.attack,i=this.props.decay,r=this.props.sustain;xe(this.audioNode.gain,t),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,t),this.audioNode.gain.exponentialRampToValueAtTime(1,t+o),r>0?this.audioNode.gain.exponentialRampToValueAtTime(r,t+o+i):this.audioNode.gain.exponentialRampToValueAtTime(.001,t+o+i)}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let o=this.props.release,i=xe(this.audioNode.gain,t);i>=1e-4&&(this.audioNode.gain.setValueAtTime(i,t),this.audioNode.gain.exponentialRampToValueAtTime(1e-4,t+o-1e-4)),this.audioNode.gain.setValueAtTime(0,t+o)}},R=class extends m{constructor(e,t){let o={...Ae,...t.props},i=(r,u)=>new ae(r,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};var Ce={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},be={gain:1},z=class extends p{constructor(e,t){let o={...be,...t.props},i=r=>new GainNode(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetGain=e=>{this.audioNode.gain.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},L=class extends m{constructor(e,t){let o={...be,...t.props},i=(r,u)=>new z(r,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var Ne=20,ce=2e4,Ee={cutoff:ce,envelopeAmount:0,type:"lowpass",Q:1},ke={cutoff:{kind:"number",min:Ne,max:ce,step:1,exp:5,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},type:{kind:"enum",options:["lowpass","highpass","bandpass"],label:"Type"},Q:{kind:"number",min:1e-4,max:1e3,step:.1,exp:5,label:"Q"}},le=class extends p{scale;amount;constructor(e,t){let o={...Ee,...t.props},i=r=>new BiquadFilterNode(r.audioContext,{type:o.type,frequency:0,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new z(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=te(e,{name:"scale",moduleType:"Scale",props:{min:Ne,max:ce,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.audioNode.frequency),this.registerDefaultIOs(),this.registerInputs()}onAfterSetType=e=>{this.audioNode.type=e};onAfterSetCutoff=e=>{this.scale.props={current:e}};onAfterSetQ=e=>{this.audioNode.Q.value=e};onAfterSetEnvelopeAmount=e=>{this.amount.props={gain:e}};registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.audioNode.frequency}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.audioNode.Q})}},U=class extends m{constructor(e,t){let o={...Ee,...t.props},i=(r,u)=>new le(r,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs()}registerInputs(){this.registerAudioInput({name:"cutoff"}),this.registerAudioInput({name:"cutoffMod"}),this.registerAudioInput({name:"Q"})}};var we={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},pt={fftSize:512},_=class extends p{_buffer;constructor(e,t){let o={...pt,...t.props},i=r=>new AnalyserNode(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onAfterSetFftSize=e=>{this._buffer=new Float32Array(e)};get buffer(){return this._buffer?this._buffer:(this._buffer=new Float32Array(this.props.fftSize),this._buffer)}getValue(){return this.getValues()[0]}getValues(){return this.audioNode.getFloatTimeDomainData(this.buffer),this.buffer}};var dt={},De={},B=class extends p{constructor(e,t){let o={...dt,...t.props},i=r=>r.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var me=(u=>(u.direct="direct",u.directRev="directRev",u.toggleInc="toggleInc",u.toggleDec="toggleDec",u.incDec="incDec",u.incDecRev="incDecRev",u))(me||{}),Fe={pages:{kind:"array",label:"Midi mapping pages"},activePage:{kind:"number",label:"Active page",min:0,max:100,step:1},globalMappings:{kind:"array",label:"Global midi mappings"}},at={pages:[{name:"Page 1",mappings:[{}]}],activePage:0,globalMappings:[{}]};function lt({value:n,midiValue:e,propSchema:t,mapping:o}){let i=t.min??0,r=t.max??1,u=t.exp??1,{threshold:d=64,mode:s}=o,c=(n-i)/(r-i),M=Math.pow(c,1/u)*127;return M=e>=d&&s==="incDec"||e<=d&&s==="incDecRev"?M+1:M-1,Math.round(Math.max(0,Math.min(127,M)))}var H=class extends p{constructor(e,t){let o={...at,...t.props};super(e,{...t,props:o}),this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}onSetActivePage=e=>Math.max(Math.min(e,this.props.pages.length-1),0);handleCC=(e,t)=>{this.checkAutoAssign(e);let o=this.props.pages[this.props.activePage];[...this.props.globalMappings.filter(i=>i.cc===e.cc),...o.mappings.filter(i=>i.cc===e.cc)].forEach(i=>{this.forwardMapping(e,i,t)})};forwardMapping=(e,t,o)=>{if(t.moduleId===void 0||t.moduleType===void 0||t.propName===void 0)return;let i=t.propName,r=e.ccValue;if(r===void 0)return;let u=t.mode??"direct";if((u==="toggleInc"||u==="toggleDec")&&r!==127)return;let d=this.engine.findModule(t.moduleId),s=Me[d.moduleType][i],c;switch(s.kind){case"number":{let h=d.props[i];if(u==="incDec"||u==="incDecRev"?r=lt({value:h,propSchema:s,mapping:t,midiValue:r}):u==="directRev"&&(r=127-r),u==="toggleInc")c=h+(s.step??1);else if(u==="toggleDec")c=h-(s.step??1);else{let M=s.min??0,I=s.max??1,y=r/127,l=Math.pow(y,s.exp??1);if(c=M+l*(I-M),s.step!==void 0&&(!s.exp||s.exp===1)){let E=Math.round((c-M)/s.step);c=M+E*s.step}}break}case"enum":{let h=Math.floor(r/127*s.options.length),M=Math.min(h,s.options.length-1);c=s.options[M];break}case"boolean":c=r>=64;break;case"string":throw Error("MidiMapper not support string type of values");case"array":throw Error("MidiMapper not support array type of values");default:throw Error("MidiMapper unknown type")}d.props={[i]:c},d.triggerPropsUpdate()};checkAutoAssign(e){if(e.cc===void 0)return;let t=this.props.pages[this.props.activePage],o=this.props.globalMappings.some(({autoAssign:s})=>s),i=t.mappings.some(({autoAssign:s})=>s);if(!o&&!i)return;let r=o?this.props.globalMappings.map(s=>s.autoAssign?{...s,cc:e.cc,autoAssign:!1}:s):this.props.globalMappings,u=i?t.mappings.map(s=>s.autoAssign?{...s,cc:e.cc,autoAssign:!1}:s):t.mappings,d=this.props.pages.map((s,c)=>c===this.props.activePage?{...s,mappings:u}:s);this.props={pages:d,globalMappings:r},this.triggerPropsUpdate()}};var Ge={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},ct={selectedId:void 0,selectedName:void 0},K=class extends p{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...ct,...t.props};super(e,{...t,props:o});let i=(this.props.selectedId&&this.engine.findMidiDevice(this.props.selectedId))??(this.props.selectedName&&this.engine.findMidiDeviceByName(this.props.selectedName));i&&this.addEventListener(i),this.registerOutputs()}onSetSelectedId=e=>{if(this.removeEventListener(),!e)return e;let t=this.engine.findMidiDevice(e);return t&&(this.props={selectedName:t.name},this.addEventListener(t)),e};get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){e.addEventListener(this.forwardMidiEvent)}removeEventListener(){if(!this.props.selectedId)return;this.engine.findMidiDevice(this.props.selectedId)?.removeEventListener(this.forwardMidiEvent)}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{dbToGain as Ve}from"@blibliki/utils";var he=-18,oe=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(oe||{}),qe={wave:{kind:"enum",options:Object.values(oe),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-1,max:2,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${he}db Gain`}},Re={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},fe=class extends p{isStated=!1;outputGain;detuneGain;constructor(e,t){let o={...Re,...t.props},i=r=>new OscillatorNode(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.outputGain=new GainNode(this.context.audioContext,{gain:Ve(he)}),this.applyOutputGain(),this.initializeGainDetune(),this.registerInputs(),this.registerOutputs()}onAfterSetWave=e=>{this.audioNode.type=e};onAfterSetFrequency=()=>{this.updateFrequency()};onAfterSetFine=()=>{this.updateFrequency()};onAfterSetCoarse=()=>{this.updateFrequency()};onAfterSetOctave=()=>{this.updateFrequency()};onAfterSetLowGain=e=>{this.outputGain.gain.value=e?Ve(he):1};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new OscillatorNode(this.context.audioContext,{type:this.props.wave,frequency:this.finalFrequency}),this.applyOutputGain(),this.detuneGain.connect(this.audioNode.detune)}),this.isStated=!1)}triggerAttack=(e,t)=>{super.triggerAttack(e,t),this.props={frequency:e.frequency},this.updateFrequency(t),this.start(t)};triggerRelease(e,t){super.triggerRelease(e,t);let o=this.activeNotes.length?this.activeNotes[this.activeNotes.length-1]:null;o&&(this.props={frequency:o.frequency},this.updateFrequency(t))}get finalFrequency(){let{frequency:e,coarse:t,octave:o,fine:i}=this.props;return e*Math.pow(2,t/12+o+i/12)}updateFrequency(e){this.finalFrequency!==void 0&&(e?this.audioNode.frequency.setValueAtTime(this.finalFrequency,e):this.audioNode.frequency.value=this.finalFrequency)}applyOutputGain(){this.audioNode.connect(this.outputGain)}initializeGainDetune(){this.detuneGain=new GainNode(this.context.audioContext,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputGain})}},$=class extends m{constructor(e,t){let o={...Re,...t.props},i=(r,u)=>new fe(r,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs("out")}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}registerInputs(){this.registerAudioInput({name:"detune"})}};import{assertNever as mt}from"@blibliki/utils";var ze=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{s0;s1;constructor(){super(),this.s0=0,this.s1=0}static get parameterDescriptors(){return[{name:"cutoff",defaultValue:1e3,minValue:20,maxValue:2e4},{name:"resonance",defaultValue:0,minValue:0,maxValue:4}]}process(t,o,i){let r=t[0],u=o[0],d=i.cutoff,s=i.resonance;for(let c=0;c<r.length;c++){let h=r[c],M=u[c];for(let I=0;I<h.length;I++){let y=h[I],l=d.length>1?d[I]:d[0],E=Math.max(20,Math.min(2e4,l)),ie=Math.log(E/20)/Math.log(2e4/20),T=Math.pow(.5,(1-ie)/.125),ge=1-Math.pow(.5,((s.length>1?s[I]:s[0])+.125)/.125)*T;this.s0=ge*this.s0-T*this.s1+T*y,this.s1=ge*this.s1+T*this.s0,M[I]=this.s1}}return!0}}registerProcessor("filter-processor",n)}).toString(),")()"],{type:"application/javascript"}));var Le=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let r=t[0],u=o[0],d=i.min,s=i.max,c=i.current;if(!r.length||r[0].length===0){for(let h of u){let M=(i.current.length>1,i.current[0]);h.fill(M)}return!0}for(let h=0;h<r.length;h++){let M=r[h],I=u[h];for(let y=0;y<M.length;y++){let l=M[y],E=d.length>1?d[y]:d[0],ie=s.length>1?s[y]:s[0],T=c.length>1?c[y]:c[0];l<0?I[y]=T*Math.pow(E/T,-l):I[y]=T*Math.pow(ie/T,l)}}return!0}}registerProcessor("scale-processor",n)}).toString(),")()"],{type:"application/javascript"}));async function Ue(n){await n.addModule(Le),await n.addModule(ze)}function _e(n,e){switch(e){case"ScaleProcessor":return n.newAudioWorklet("scale-processor");case"FilterProcessor":return n.newAudioWorklet("filter-processor");default:mt(e)}}var Be={min:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Min"},max:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Max"},current:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Current"}},Mt={min:0,max:1,current:.5},W=class extends p{constructor(e,t){let o={...Mt,...t.props},i=r=>_e(r,"ScaleProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}get current(){return this.audioNode.parameters.get("current")}get min(){return this.audioNode.parameters.get("min")}get max(){return this.audioNode.parameters.get("max")}onAfterSetMin=e=>{this.min.value=e};onAfterSetMax=e=>{this.max.value=e};onAfterSetCurrent=e=>{this.current.value=e}};var He={steps:{kind:"number",min:1,max:16,step:1,label:"Steps"},bars:{kind:"number",min:1,max:16,step:1,label:"Steps"}},ht={sequences:[],steps:16,bars:1},j=class extends p{midiOutput;constructor(e,t){let o={...ht,...t.props};super(e,{...t,props:o})}};var Ke={pan:{kind:"number",min:-1,max:1,step:.01,label:"Pan"}},$e={pan:0},ye=class extends p{constructor(e,t){let o={...$e,...t.props},i=r=>new StereoPannerNode(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetPan=e=>{this.audioNode.pan.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"pan",getAudioNode:()=>this.audioNode.pan})}},Q=class extends m{constructor(e,t){let o={...$e,...t.props},i=(r,u)=>new ye(r,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"pan"})}};var We={activeNotes:{kind:"array",label:"Active notes"}},ft={activeNotes:[]},Y=class extends p{midiOutput;constructor(e,t){let o={...ft,...t.props};super(e,{...t,props:o}),this.registerInputs(),this.registerOutputs()}sendMidi(e){this.midiOutput.onMidiEvent(e)}triggerAttack=(e,t)=>{this.props={activeNotes:[...this.props.activeNotes,e.fullName]},this.triggerPropsUpdate(),this.sendMidi(f.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(f.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var ee=(l=>(l.Master="Master",l.Oscillator="Oscillator",l.Gain="Gain",l.MidiSelector="MidiSelector",l.Envelope="Envelope",l.Filter="Filter",l.Scale="Scale",l.StereoPanner="StereoPanner",l.Inspector="Inspector",l.Constant="Constant",l.MidiMapper="MidiMapper",l.VirtualMidi="VirtualMidi",l.StepSequencer="StepSequencer",l.VoiceScheduler="VoiceScheduler",l))(ee||{}),Me={Oscillator:qe,Gain:Ce,Master:De,MidiSelector:Ge,Envelope:Oe,Filter:ke,Scale:Be,StereoPanner:Ke,Inspector:we,Constant:Se,MidiMapper:Fe,VirtualMidi:We,StepSequencer:He,VoiceScheduler:Te};function te(n,e){switch(e.moduleType){case"Oscillator":return new $(n,e);case"Gain":return new L(n,e);case"Master":return new B(n,e);case"MidiSelector":return new K(n,e);case"Envelope":return new R(n,e);case"Filter":return new U(n,e);case"Scale":return new W(n,e);case"StereoPanner":return new Q(n,e);case"Inspector":return new _(n,e);case"Constant":return new q(n,e);case"MidiMapper":return new H(n,e);case"VirtualMidi":return new Y(n,e);case"StepSequencer":return new j(n,e);case"VoiceScheduler":return new V(n,e);default:yt(e)}}var A=class n{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=n._engines.get(e);return je(t),t}static get current(){return je(this._currentId),this.getById(this._currentId)}static async load(e){let{bpm:t,timeSignature:o,modules:i,routes:r}=e,u=new It,d=new n(u);return await d.initialize(),d.timeSignature=o,d.bpm=t,i.forEach(s=>{d.addModule(s)}),r.forEach(s=>{d.addRoute(s)}),d}constructor(e){this.id=Tt(),this.context=e,this.transport=new gt(this.context,{generator:(t,o)=>[],consumer:t=>{},onJump:t=>{},onStart:this.onStart,onStop:this.onStop,silence:t=>{}}),this.routes=new G(this),this.modules=new Map,this.midiDeviceManager=new N(this.context),n._engines.set(this.id,this),n._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await Ue(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=te(this.id,e);return this.modules.set(t.id,t),t.serialize()}updateModule(e){let t=this.findModule(e.id);if(t.moduleType!==e.moduleType)throw Error(`The module id ${e.id} isn't moduleType ${e.moduleType}`);let o=Pt(e.changes,["name","props"]);return Object.assign(t,o),t instanceof m&&e.changes.voices!==void 0&&(t.voices=e.changes.voices),t.serialize()}removeModule(e){this.modules.delete(e)}addRoute(e){return this.routes.addRoute(e)}removeRoute(e){this.routes.removeRoute(e)}validRoute(e){let{source:t,destination:o}=e,i=this.findIO(t.moduleId,t.ioName,"output"),r=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&r.isMidi()||i.isAudio()&&r.isAudio()}async start(){await this.resume(),this.transport.start()}stop(){this.transport.stop(),this.transport.reset()}pause(){this.transport.stop()}get bpm(){return this.transport.bpm}set bpm(e){this.transport.bpm=e}get timeSignature(){return this.transport.timeSignature}set timeSignature(e){this.transport.timeSignature=e}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear()}serialize(){return{bpm:this.bpm,timeSignature:this.timeSignature,modules:Array.from(this.modules.values()).map(e=>e.serialize()),routes:this.routes.serialize()}}findModule(e){let t=this.modules.get(e);if(!t)throw Error(`The module with id ${e} is not exists`);return t}findIO(e,t,o){return this.findModule(e)[`${o}s`].findByName(t)}findMidiDevice(e){return this.midiDeviceManager.find(e)}findMidiDeviceByName(e){return this.midiDeviceManager.findByName(e)}onPropsUpdate(e){this.propsUpdateCallbacks.push(e)}_triggerPropsUpdate(e){this.propsUpdateCallbacks.forEach(t=>{t(e)})}triggerVirtualMidi(e,t,o){let i=this.findModule(e);if(i.moduleType!=="VirtualMidi")throw Error("This is not a virtual mid");i.sendMidi(f.fromNote(t,o==="noteOn",this.context.currentTime))}onStart=e=>{this.modules.forEach(t=>{t.start(e)})};onStop=e=>{this.modules.forEach(t=>{t.stop(e)})}};import{TransportState as Jn}from"@blibliki/transport";import{Context as Zn}from"@blibliki/utils";export{Zn as Context,A as Engine,v as MidiDevice,me as MidiMappingMode,X as MidiPortState,ee as ModuleType,a as Note,oe as OscillatorWave,Jn as TransportState,Me as moduleSchemas};
|
|
1
|
+
import{Transport as xt}from"@blibliki/transport";import{assertDefined as Ze,Context as Ot,pick as bt,uuidv4 as Ct}from"@blibliki/utils";import{upperFirst as ut,uuidv4 as pt}from"@blibliki/utils";import{assertNever as it}from"@blibliki/utils";import{sortBy as rt}from"es-toolkit";import{deterministicId as et,uuidv4 as tt}from"@blibliki/utils";var m=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;_voices;_name;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voices:s,monoModuleConstructor:d,props:u}=t;this.audioModules=[],this.monoModuleConstructor=d,this.id=o??tt(),this.engineId=e,this.name=i,this.moduleType=r,this._props=u,this.inputs=new S(this),this.outputs=new x(this),queueMicrotask(()=>{this.voices=s||1,this.props=u})}get name(){return this._name}set name(e){this._name=e,this.audioModules.forEach(t=>t.name=e)}get props(){return this._props}set props(e){this._props={...this._props,...e},this.audioModules.forEach(t=>t.props=e)}get voices(){return this._voices}set voices(e){this._voices=e,this.adjustNumberOfModules(),this.rePlugAll()}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voices:this.voices,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll(),this.audioModules.forEach(e=>{e.dispose()})}onMidiEvent=e=>{let t=e.voiceNo??0;this.findVoice(t).onMidiEvent(e)};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){requestAnimationFrame(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voices:this.voices,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}findVoice(e){let t=this.audioModules.find(o=>o.voiceNo===e);if(!t)throw Error(`Voice ${e} on module ${this.name} not found`);return t}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),(e==="in"||e==="both")&&this.registerAudioInput({name:"in"}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out"})}registerAudioInput(e){return this.inputs.add({...e,ioType:"polyAudioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"polyAudioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}adjustNumberOfModules(){if(this.audioModules.length!==this.voices){if(this.audioModules.length>this.voices)this.audioModules.pop()?.dispose();else{let e=this.audioModules.length,t=et(this.id,e.toString()),o=this.monoModuleConstructor(this.engineId,{id:t,name:this.name,moduleType:this.moduleType,voiceNo:e,props:{...this.props}});this.audioModules.push(o)}this.adjustNumberOfModules()}}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{deterministicId as ot}from"@blibliki/utils";var pe=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=ot(this.module.id,this.name),this.connections=[]}plug(e,t=!0){this.connections.push(e),t&&e.plug(this,!1)}unPlug(e,t=!0){this.connections=this.connections.filter(o=>o.id!==e.id),t&&e.unPlug(this,!1)}rePlugAll(e){let t=this.connections;this.unPlugAll(),e&&e(),t.forEach(o=>{this.plug(o)})}unPlugAll(){this.connections.forEach(e=>{this.unPlug(e)})}isAudio(){return this.ioType==="audioInput"||this.ioType==="audioOutput"||this.ioType==="polyAudioInput"||this.ioType==="polyAudioOutput"}isMidi(){return this.ioType==="midiInput"||this.ioType==="midiOutput"}serialize(){return{id:this.id,name:this.name,ioType:this.ioType,moduleId:this.module.id}}},g=class extends pe{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var P=class extends g{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof b)&&ee(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof b)&&ee(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},b=class extends g{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof P)&&ee(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof P)&&ee(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function ee(n,e,t){if(e instanceof P||e instanceof b){let o=Math.max(n.module.voices,e.module.voices);for(let i=0;i<o;i++){let r=n.findIOByVoice(i%n.module.voices),s=e.findIOByVoice(i%e.module.voices);t?r.plug(s):r.unPlug(s)}}else for(let o=0;o<n.module.voices;o++){let i=n.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var w=class extends g{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},D=class extends g{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof P)return;let o=e.getAudioNode();o instanceof AudioParam?this.getAudioNode().connect(o):this.getAudioNode().connect(o)}unPlug(e,t=!0){if(super.unPlug(e,t),e instanceof P)return;let o=e.getAudioNode();try{o instanceof AudioParam?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var C=class extends g{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},F=class extends g{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof C)}};var G=class{module;collection=[];collectionType;constructor(e,t){this.collectionType=e,this.module=t}add(e){let t;switch(this.validateUniqName(e.name),e.ioType){case"audioInput":if(this.module instanceof m)throw Error("Not compatible");t=new w(this.module,e);break;case"audioOutput":if(this.module instanceof m)throw Error("Not compatible");t=new D(this.module,e);break;case"polyAudioInput":if(this.module instanceof p)throw Error("Not compatible");t=new P(this.module,e);break;case"polyAudioOutput":if(this.module instanceof p)throw Error("Not compatible");t=new b(this.module,e);break;case"midiInput":t=new C(this.module,e);break;case"midiOutput":t=new F(this.module,e);break;default:it(e)}return this.collection.push(t),t}unPlugAll(){this.collection.forEach(e=>{e.unPlugAll()})}rePlugAll(e){this.collection.forEach(t=>{t.rePlugAll(e)})}find(e){let t=this.collection.find(o=>o.id===e);if(!t)throw Error(`The io with id ${e} is not exists`);return t}findByName(e){let t=this.collection.find(o=>o.name===e);if(!t)throw Error(`The io with name ${e} is not exists`);return t}serialize(){return rt(this.collection,[e=>e.isMidi()?-1:1]).map(e=>e.serialize())}validateUniqName(e){if(this.collection.some(t=>t.name===e))throw Error(`An io with name ${e} is already exists`)}},S=class extends G{constructor(e){super("Input",e)}},x=class extends G{constructor(e){super("Output",e)}};var nt=new Map([["C0",16.35],["C#0",17.32],["Db0",17.32],["D0",18.35],["D#0",19.45],["Eb0",19.45],["E0",20.6],["F0",21.83],["F#0",23.12],["Gb0",23.12],["G0",24.5],["G#0",25.96],["Ab0",25.96],["A0",27.5],["A#0",29.14],["Bb0",29.14],["B0",30.87],["C1",32.7],["C#1",34.65],["Db1",34.65],["D1",36.71],["D#1",38.89],["Eb1",38.89],["E1",41.2],["F1",43.65],["F#1",46.25],["Gb1",46.25],["G1",49],["G#1",51.91],["Ab1",51.91],["A1",55],["A#1",58.27],["Bb1",58.27],["B1",61.74],["C2",65.41],["C#2",69.3],["Db2",69.3],["D2",73.42],["D#2",77.78],["Eb2",77.78],["E2",82.41],["F2",87.31],["F#2",92.5],["Gb2",92.5],["G2",98],["G#2",103.83],["Ab2",103.83],["A2",110],["A#2",116.54],["Bb2",116.54],["B2",123.47],["C3",130.81],["C#3",138.59],["Db3",138.59],["D3",146.83],["D#3",155.56],["Eb3",155.56],["E3",164.81],["F3",174.61],["F#3",185],["Gb3",185],["G3",196],["G#3",207.65],["Ab3",207.65],["A3",220],["A#3",233.08],["Bb3",233.08],["B3",246.94],["C4",261.63],["C#4",277.18],["Db4",277.18],["D4",293.66],["D#4",311.13],["Eb4",311.13],["E4",329.63],["F4",349.23],["F#4",369.99],["Gb4",369.99],["G4",392],["G#4",415.3],["Ab4",415.3],["A4",440],["A#4",466.16],["Bb4",466.16],["B4",493.88],["C5",523.25],["C#5",554.37],["Db5",554.37],["D5",587.33],["D#5",622.25],["Eb5",622.25],["E5",659.26],["F5",698.46],["F#5",739.99],["Gb5",739.99],["G5",783.99],["G#5",830.61],["Ab5",830.61],["A5",880],["A#5",932.33],["Bb5",932.33],["B5",987.77],["C6",1046.5],["C#6",1108.73],["Db6",1108.73],["D6",1174.66],["D#6",1244.51],["Eb6",1244.51],["E6",1318.51],["F6",1396.91],["F#6",1479.98],["Gb6",1479.98],["G6",1567.98],["G#6",1661.22],["Ab6",1661.22],["A6",1760],["A#6",1864.66],["Bb6",1864.66],["B6",1975.53],["C7",2093],["C#7",2217.46],["Db7",2217.46],["D7",2349.32],["D#7",2489.02],["Eb7",2489.02],["E7",2637.02],["F7",2793.83],["F#7",2959.96],["Gb7",2959.96],["G7",3135.96],["G#7",3322.44],["Ab7",3322.44],["A7",3520],["A#7",3729.31],["Bb7",3729.31],["B7",3951.07],["C8",4186.01],["C#8",4434.92],["Db8",4434.92],["D8",4698.64],["D#8",4978.03],["Eb8",4978.03]]),de=nt;var ae=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],st=2,a=class n{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of de)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new n(t)}static fromEvent(e){let t=ae[e.data[1]%12],o=Math.floor(e.data[1]/12)-2;return new n(`${t}${o}`)}static notes(e=3){return ae.map(t=>new n(`${t}${e}`))}constructor(e){typeof e=="string"?this.fromString(e):this.fromProps(e)}get isSemi(){return this.name.endsWith("#")}get fullName(){return`${this.name}${this.octave}`}get frequency(){return de.get(`${this.name}${this.octave}`)}midiData(e=!0){let t=e?144:128;return new Uint8Array([t,this.midiNumber,this.velocity*100])}get midiNumber(){return(this.octave+st)*12+this.noteIndex}get noteIndex(){return ae.indexOf(this.name)}valueOf(){return this.fullName}serialize(){return{name:this.name,octave:this.octave,frequency:this.frequency,velocity:this.velocity,duration:this.duration}}fromString(e){let t=/(\w#?)(\d)?/.exec(e)??[];this.name=t[1],this.octave=t[2]?parseInt(t[2]):1}fromProps(e){Object.assign(this,e)}};var v=class{data;constructor(e){this.data=e}get dataBytes(){return Array.from(this.data.slice(1))}get type(){switch(this.data[0]&240){case 144:return this.data[2]===0?"noteoff":"noteon";case 128:return"noteoff";case 176:return"controlchange";case 224:return"pitchbend";case 208:return"channelaftertouch";case 160:return"keyaftertouch";case 192:return"programchange";default:return"unknown"}}};var f=class n{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof a?e:new a(e);return new n(new v(i.midiData(t)),o)}static fromCC(e,t,o){return new n(new v(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t,this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}get isCC(){return this.type==="controlchange"}get cc(){if(this.isCC)return this.message.dataBytes[0]}get ccValue(){if(this.isCC)return this.message.dataBytes[1]}defineNotes(){this.isNote&&(this.note||(this.note=a.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new n(this.message,this.triggeredAt);return t.voiceNo=e,t}};var p=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;activeNotes;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voiceNo:s,audioNodeConstructor:d,props:u}=t;this.id=o??pt(),this.engineId=e,this.name=i,this.moduleType=r,this.voiceNo=s??0,this.activeNotes=[],this.audioNode=d?.(this.context),this._props=u,this.inputs=new S(this),this.outputs=new x(this),queueMicrotask(()=>{this.props=u})}get props(){return this._props}set props(e){let t={...e};Object.keys(e).forEach(o=>{let i=e[o];if(i!==void 0){let r=this.callPropHook("onSet",o,i);r!==void 0&&(t[o]=r)}}),this._props={...this._props,...t},Object.keys(t).forEach(o=>{let i=t[o];i!==void 0&&this.callPropHook("onAfterSet",o,i)})}callPropHook(e,t,o){let i=`${e}${ut(t)}`,r=this[i];if(typeof r=="function")return r.call(this,o)}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voiceNo:this.voiceNo,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}start(e){}stop(e){}triggerAttack(e,t){this.activeNotes.some(o=>o.fullName===e.fullName)||this.activeNotes.push(e)}triggerRelease(e,t){this.activeNotes=this.activeNotes.filter(o=>o.fullName!==e.fullName)}handleCC(e,t){}onMidiEvent=e=>{let{note:t,triggeredAt:o}=e;switch(e.type){case"noteon":{this.triggerAttack(t,o);break}case"noteoff":this.triggerRelease(t,o);break;case"controlchange":this.handleCC(e,o);break;default:throw Error("This type is not a note")}};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){requestAnimationFrame(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),this.audioNode&&((e==="in"||e==="both")&&this.registerAudioInput({name:"in",getAudioNode:()=>this.audioNode}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out",getAudioNode:()=>this.audioNode}))}registerAudioInput(e){return this.inputs.add({...e,ioType:"audioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"audioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as dt}from"@blibliki/utils";var q=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??dt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){this.routes.forEach((e,t)=>{this.removeRoute(t)})}replug(){this.routes.forEach((e,t)=>{let{sourceIO:o,destinationIO:i}=this.getIOs(t);o.rePlugAll(),i.rePlugAll()})}serialize(){return Array.from(this.routes.values())}plug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.plug(o)}unPlug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.unPlug(o)}find(e){let t=this.routes.get(e);if(!t)throw Error(`Route with id ${e} not found`);return t}getIOs(e){let t=this.find(e),{source:o,destination:i}=t,r=this.engine.findIO(o.moduleId,o.ioName,"output"),s=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:r,destinationIO:s}}};var te=(t=>(t.connected="connected",t.disconnected="disconnected",t))(te||{}),A=class{id;name;eventListerCallbacks=[];context;input;messageHandler=null;constructor(e,t){this.id=e.id,this.name=e.name,this.input=e,this.context=t,this.connect()}get state(){return this.input.state}connect(){this.messageHandler=e=>{this.processEvent(e)},this.input.addEventListener(this.messageHandler)}disconnect(){this.messageHandler&&(this.input.removeEventListener(this.messageHandler),this.messageHandler=null)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}processEvent(e){let t=new v(e.data),o=new f(t,this.context.browserToContextTime(e.timeStamp));switch(o.type){case"noteon":case"noteoff":case"controlchange":this.eventListerCallbacks.forEach(i=>{i(o)})}}};var at={a:new a("C3"),s:new a("D3"),d:new a("E3"),f:new a("F3"),g:new a("G3"),h:new a("A3"),j:new a("B3"),k:new a("C4"),l:new a("D4"),w:new a("C#3"),e:new a("D#3"),t:new a("F#3"),y:new a("G#3"),u:new a("A#3"),o:new a("C#4"),p:new a("D#4")},lt=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),N=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=lt();this.id=t,this.name=o,this.state=i,this.context=e,document.addEventListener("keydown",this.onKeyTrigger(!0)),document.addEventListener("keyup",this.onKeyTrigger(!1))}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}onKeyTrigger=e=>t=>{let o=this.extractNote(t);if(!o)return;let i=f.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(r=>{r(i)})};extractNote(e){if(!e.repeat)return at[e.key]}};import{isNode as mt}from"es-toolkit";import{isNode as ct}from"es-toolkit";var ce=class{id;name;portIndex;input;callbacks=new Set;handler=null;_state="disconnected";constructor(e,t,o){this.portIndex=e,this.id=`node-midi-${e}`,this.name=t,this.input=o}get state(){return this._state}setState(e){this._state=e}addEventListener(e){if(this.callbacks.size===0){this.handler=(t,o)=>{let i={data:new Uint8Array(o),timeStamp:performance.now()};this.callbacks.forEach(r=>{r(i)})};try{this.input.isPortOpen()||(this.input.openPort(this.portIndex),this._state="connected"),this.input.on("message",this.handler)}catch(t){console.error(`Error opening MIDI port ${this.portIndex}:`,t)}}this.callbacks.add(e)}removeEventListener(e){if(this.callbacks.delete(e),this.callbacks.size===0&&this.handler){try{this.input.off("message",this.handler),this.input.isPortOpen()&&(this.input.closePort(),this._state="disconnected")}catch(t){console.error(`Error closing MIDI port ${this.portIndex}:`,t)}this.handler=null}}},me=class{ports=new Map;MidiModule;constructor(e){this.MidiModule=e,this.scanPorts()}scanPorts(){try{let e=new this.MidiModule.Input,t=e.getPortCount();for(let o=0;o<t;o++){let i=e.getPortName(o),r=`node-midi-${o}`;if(!this.ports.has(r)){let s=new this.MidiModule.Input,d=new ce(o,i,s);this.ports.set(r,d)}}e.isPortOpen()&&e.closePort()}catch(e){console.error("Error scanning MIDI ports:",e)}}*inputs(){for(let[,e]of this.ports)yield e}addEventListener(e,t){console.warn("Hot-plug detection not supported with node-midi adapter. Restart required for new devices.")}},V=class{async requestMIDIAccess(){try{let e=await import("@julusian/midi"),t="default"in e?e.default:e;return new me(t)}catch(e){return console.error("Error loading node-midi:",e),null}}isSupported(){return ct()}};var oe=class{input;callbacks=new Set;handler=null;constructor(e){this.input=e}get id(){return this.input.id}get name(){return this.input.name??`Device ${this.input.id}`}get state(){return this.input.state}addEventListener(e){this.callbacks.size===0&&(this.handler=t=>{if(!t.data)return;let o={data:t.data,timeStamp:t.timeStamp};this.callbacks.forEach(i=>{i(o)})},this.input.addEventListener("midimessage",this.handler)),this.callbacks.add(e)}removeEventListener(e){this.callbacks.delete(e),this.callbacks.size===0&&this.handler&&(this.input.removeEventListener("midimessage",this.handler),this.handler=null)}},Me=class{midiAccess;portCache=new Map;constructor(e){this.midiAccess=e}*inputs(){for(let[,e]of this.midiAccess.inputs)this.portCache.has(e.id)||this.portCache.set(e.id,new oe(e)),yield this.portCache.get(e.id)}addEventListener(e,t){this.midiAccess.addEventListener(e,o=>{let i=o.port;if(!i||i.type!=="input")return;let r=i;this.portCache.has(r.id)||this.portCache.set(r.id,new oe(r)),t(this.portCache.get(r.id))})}},z=class{async requestMIDIAccess(){try{if(typeof navigator>"u"||typeof navigator.requestMIDIAccess!="function")return null;let e=await navigator.requestMIDIAccess();return new Me(e)}catch(e){return console.error("Error enabling Web MIDI API:",e),null}}isSupported(){return typeof navigator<"u"&&typeof navigator.requestMIDIAccess=="function"}};function xe(){return mt()?new V:new z}var E=class{devices=new Map;initialized=!1;listeners=[];context;midiAccess=null;adapter=xe();constructor(e){this.context=e,this.addComputerKeyboard()}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.devices.get(e)}findByName(e){return Array.from(this.devices.values()).find(t=>t.name===e)}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{if(!this.adapter.isSupported()){console.warn("MIDI is not supported on this platform");return}if(this.midiAccess=await this.adapter.requestMIDIAccess(),!this.midiAccess){console.error("Failed to get MIDI access");return}for(let e of this.midiAccess.inputs())this.devices.has(e.id)||this.devices.set(e.id,new A(e,this.context))}catch(e){console.error("Error enabling MIDI:",e)}}addComputerKeyboard(){if(typeof document>"u")return;let e=new N(this.context);this.devices.set(e.id,e)}listenChanges(){this.midiAccess&&this.midiAccess.addEventListener("statechange",e=>{if(e.state==="connected"){if(this.devices.has(e.id))return;let t=new A(e,this.context);this.devices.set(t.id,t),this.listeners.forEach(o=>{o(t)})}else{let t=this.devices.get(e.id);if(!t||t instanceof N)return;t.disconnect(),this.devices.delete(t.id),this.listeners.forEach(o=>{o(t)})}})}};import{assertNever as St}from"@blibliki/utils";var Oe={},be={},he=class extends p{activeNote=null;triggeredAt=0;constructor(e,t){let o={...be,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let r=o.fullName;switch(i){case"noteon":this.activeNote=r,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},R=class extends m{midiOutput;constructor(e,t){let o={...be,...t.props},i=(r,s)=>new he(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerOutputs()}onMidiEvent=e=>{let t;switch(e.type){case"noteon":t=this.findFreeVoice();break;case"noteoff":t=this.audioModules.find(o=>o.activeNote===e.note.fullName);break;default:throw Error("This type is not a note")}t&&(t.midiTriggered(e),e.voiceNo=t.voiceNo,this.midiOutput.onMidiEvent(e))};findFreeVoice(){let e=this.audioModules.find(t=>!t.activeNote);return e??=this.audioModules.sort((t,o)=>t.triggeredAt-o.triggeredAt)[0],e}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var Ce={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},Mt={value:1},L=class extends p{isStated=!1;constructor(e,t){let o={...Mt,...t.props},i=r=>new ConstantSourceNode(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onAfterSetValue=e=>{this.audioNode.offset.value=e};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new ConstantSourceNode(this.context.audioContext,{offset:this.props.value})}),this.isStated=!1)}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,t),this.start(t)};triggerRelease=()=>{}};import{cancelAndHoldAtTime as Ne}from"@blibliki/utils";var Ee={attack:.01,decay:0,sustain:1,release:0},ke={attack:{kind:"number",min:1e-4,max:20,step:.01,exp:3,label:"Attack"},decay:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Release"}},fe=class extends p{constructor(e,t){let o={...Ee,...t.props},i=r=>{let s=new GainNode(r.audioContext);return s.gain.value=0,s};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let o=this.props.attack,i=this.props.decay,r=this.props.sustain;Ne(this.audioNode.gain,t),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,t),this.audioNode.gain.exponentialRampToValueAtTime(1,t+o),r>0?this.audioNode.gain.exponentialRampToValueAtTime(r,t+o+i):this.audioNode.gain.exponentialRampToValueAtTime(.001,t+o+i)}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let o=this.props.release,i=Ne(this.audioNode.gain,t);i>=1e-4&&(this.audioNode.gain.setValueAtTime(i,t),this.audioNode.gain.exponentialRampToValueAtTime(1e-4,t+o-1e-4)),this.audioNode.gain.setValueAtTime(0,t+o)}},_=class extends m{constructor(e,t){let o={...Ee,...t.props},i=(r,s)=>new fe(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};var we={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},De={gain:1},U=class extends p{constructor(e,t){let o={...De,...t.props},i=r=>new GainNode(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetGain=e=>{this.audioNode.gain.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},B=class extends m{constructor(e,t){let o={...De,...t.props},i=(r,s)=>new U(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var Fe=20,ge=2e4,Ge={cutoff:ge,envelopeAmount:0,type:"lowpass",Q:1},qe={cutoff:{kind:"number",min:Fe,max:ge,step:1,exp:5,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},type:{kind:"enum",options:["lowpass","highpass","bandpass"],label:"Type"},Q:{kind:"number",min:1e-4,max:1e3,step:.1,exp:5,label:"Q"}},ye=class extends p{scale;amount;constructor(e,t){let o={...Ge,...t.props},i=r=>new BiquadFilterNode(r.audioContext,{type:o.type,frequency:0,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new U(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=re(e,{name:"scale",moduleType:"Scale",props:{min:Fe,max:ge,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.audioNode.frequency),this.registerDefaultIOs(),this.registerInputs()}onAfterSetType=e=>{this.audioNode.type=e};onAfterSetCutoff=e=>{this.scale.props={current:e}};onAfterSetQ=e=>{this.audioNode.Q.value=e};onAfterSetEnvelopeAmount=e=>{this.amount.props={gain:e}};registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.audioNode.frequency}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.audioNode.Q})}},H=class extends m{constructor(e,t){let o={...Ge,...t.props},i=(r,s)=>new ye(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs()}registerInputs(){this.registerAudioInput({name:"cutoff"}),this.registerAudioInput({name:"cutoffMod"}),this.registerAudioInput({name:"Q"})}};var Ve={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},ht={fftSize:512},K=class extends p{_buffer;constructor(e,t){let o={...ht,...t.props},i=r=>new AnalyserNode(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onAfterSetFftSize=e=>{this._buffer=new Float32Array(e)};get buffer(){return this._buffer?this._buffer:(this._buffer=new Float32Array(this.props.fftSize),this._buffer)}getValue(){return this.getValues()[0]}getValues(){return this.audioNode.getFloatTimeDomainData(this.buffer),this.buffer}};var ft={},ze={},$=class extends p{constructor(e,t){let o={...ft,...t.props},i=r=>r.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var Ie=(s=>(s.direct="direct",s.directRev="directRev",s.toggleInc="toggleInc",s.toggleDec="toggleDec",s.incDec="incDec",s.incDecRev="incDecRev",s))(Ie||{}),Re={pages:{kind:"array",label:"Midi mapping pages"},activePage:{kind:"number",label:"Active page",min:0,max:100,step:1},globalMappings:{kind:"array",label:"Global midi mappings"}},yt={pages:[{name:"Page 1",mappings:[{}]}],activePage:0,globalMappings:[{}]};function gt({value:n,midiValue:e,propSchema:t,mapping:o}){let i=t.min??0,r=t.max??1,s=t.exp??1,{threshold:d=64,mode:u}=o,c=(n-i)/(r-i),M=Math.pow(c,1/s)*127;return M=e>=d&&u==="incDec"||e<=d&&u==="incDecRev"?M+1:M-1,Math.round(Math.max(0,Math.min(127,M)))}var j=class extends p{constructor(e,t){let o={...yt,...t.props};super(e,{...t,props:o}),this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}onSetActivePage=e=>Math.max(Math.min(e,this.props.pages.length-1),0);handleCC=(e,t)=>{this.checkAutoAssign(e);let o=this.props.pages[this.props.activePage];[...this.props.globalMappings.filter(i=>i.cc===e.cc),...o.mappings.filter(i=>i.cc===e.cc)].forEach(i=>{this.forwardMapping(e,i,t)})};forwardMapping=(e,t,o)=>{if(t.moduleId===void 0||t.moduleType===void 0||t.propName===void 0)return;let i=t.propName,r=e.ccValue;if(r===void 0)return;let s=t.mode??"direct";if((s==="toggleInc"||s==="toggleDec")&&r!==127)return;let d=this.engine.findModule(t.moduleId),u=Pe[d.moduleType][i],c;switch(u.kind){case"number":{let h=d.props[i];if(s==="incDec"||s==="incDecRev"?r=gt({value:h,propSchema:u,mapping:t,midiValue:r}):s==="directRev"&&(r=127-r),s==="toggleInc")c=h+(u.step??1);else if(s==="toggleDec")c=h-(u.step??1);else{let M=u.min??0,I=u.max??1,y=r/127,l=Math.pow(y,u.exp??1);if(c=M+l*(I-M),u.step!==void 0&&(!u.exp||u.exp===1)){let k=Math.round((c-M)/u.step);c=M+k*u.step}}break}case"enum":{let h=Math.floor(r/127*u.options.length),M=Math.min(h,u.options.length-1);c=u.options[M];break}case"boolean":c=r>=64;break;case"string":throw Error("MidiMapper not support string type of values");case"array":throw Error("MidiMapper not support array type of values");default:throw Error("MidiMapper unknown type")}d.props={[i]:c},d.triggerPropsUpdate()};checkAutoAssign(e){if(e.cc===void 0)return;let t=this.props.pages[this.props.activePage],o=this.props.globalMappings.some(({autoAssign:u})=>u),i=t.mappings.some(({autoAssign:u})=>u);if(!o&&!i)return;let r=o?this.props.globalMappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):this.props.globalMappings,s=i?t.mappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):t.mappings,d=this.props.pages.map((u,c)=>c===this.props.activePage?{...u,mappings:s}:u);this.props={pages:d,globalMappings:r},this.triggerPropsUpdate()}};var Le={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},It={selectedId:void 0,selectedName:void 0},W=class extends p{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...It,...t.props};super(e,{...t,props:o});let i=(this.props.selectedId&&this.engine.findMidiDevice(this.props.selectedId))??(this.props.selectedName&&this.engine.findMidiDeviceByName(this.props.selectedName));i&&this.addEventListener(i),this.registerOutputs()}onSetSelectedId=e=>{if(this.removeEventListener(),!e)return e;let t=this.engine.findMidiDevice(e);return t&&(this.props={selectedName:t.name},this.addEventListener(t)),e};get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){e.addEventListener(this.forwardMidiEvent)}removeEventListener(){if(!this.props.selectedId)return;this.engine.findMidiDevice(this.props.selectedId)?.removeEventListener(this.forwardMidiEvent)}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{dbToGain as _e}from"@blibliki/utils";var Te=-18,ne=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(ne||{}),Ue={wave:{kind:"enum",options:Object.values(ne),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-1,max:2,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${Te}db Gain`}},Be={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},ve=class extends p{isStated=!1;outputGain;detuneGain;constructor(e,t){let o={...Be,...t.props},i=r=>new OscillatorNode(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.outputGain=new GainNode(this.context.audioContext,{gain:_e(Te)}),this.applyOutputGain(),this.initializeGainDetune(),this.registerInputs(),this.registerOutputs()}onAfterSetWave=e=>{this.audioNode.type=e};onAfterSetFrequency=()=>{this.updateFrequency()};onAfterSetFine=()=>{this.updateFrequency()};onAfterSetCoarse=()=>{this.updateFrequency()};onAfterSetOctave=()=>{this.updateFrequency()};onAfterSetLowGain=e=>{this.outputGain.gain.value=e?_e(Te):1};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new OscillatorNode(this.context.audioContext,{type:this.props.wave,frequency:this.finalFrequency}),this.applyOutputGain(),this.detuneGain.connect(this.audioNode.detune)}),this.isStated=!1)}triggerAttack=(e,t)=>{super.triggerAttack(e,t),this.props={frequency:e.frequency},this.updateFrequency(t),this.start(t)};triggerRelease(e,t){super.triggerRelease(e,t);let o=this.activeNotes.length?this.activeNotes[this.activeNotes.length-1]:null;o&&(this.props={frequency:o.frequency},this.updateFrequency(t))}get finalFrequency(){let{frequency:e,coarse:t,octave:o,fine:i}=this.props;return e*Math.pow(2,t/12+o+i/12)}updateFrequency(e){this.finalFrequency!==void 0&&(e?this.audioNode.frequency.setValueAtTime(this.finalFrequency,e):this.audioNode.frequency.value=this.finalFrequency)}applyOutputGain(){this.audioNode.connect(this.outputGain)}initializeGainDetune(){this.detuneGain=new GainNode(this.context.audioContext,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputGain})}},Q=class extends m{constructor(e,t){let o={...Be,...t.props},i=(r,s)=>new ve(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs("out")}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}registerInputs(){this.registerAudioInput({name:"detune"})}};import{assertNever as Pt}from"@blibliki/utils";var He=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{s0;s1;constructor(){super(),this.s0=0,this.s1=0}static get parameterDescriptors(){return[{name:"cutoff",defaultValue:1e3,minValue:20,maxValue:2e4},{name:"resonance",defaultValue:0,minValue:0,maxValue:4}]}process(t,o,i){let r=t[0],s=o[0],d=i.cutoff,u=i.resonance;for(let c=0;c<r.length;c++){let h=r[c],M=s[c];for(let I=0;I<h.length;I++){let y=h[I],l=d.length>1?d[I]:d[0],k=Math.max(20,Math.min(2e4,l)),se=Math.log(k/20)/Math.log(2e4/20),T=Math.pow(.5,(1-se)/.125),Se=1-Math.pow(.5,((u.length>1?u[I]:u[0])+.125)/.125)*T;this.s0=Se*this.s0-T*this.s1+T*y,this.s1=Se*this.s1+T*this.s0,M[I]=this.s1}}return!0}}registerProcessor("filter-processor",n)}).toString(),")()"],{type:"application/javascript"}));var Ke=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let r=t[0],s=o[0],d=i.min,u=i.max,c=i.current;if(!r.length||r[0].length===0){for(let h of s){let M=(i.current.length>1,i.current[0]);h.fill(M)}return!0}for(let h=0;h<r.length;h++){let M=r[h],I=s[h];for(let y=0;y<M.length;y++){let l=M[y],k=d.length>1?d[y]:d[0],se=u.length>1?u[y]:u[0],T=c.length>1?c[y]:c[0];l<0?I[y]=T*Math.pow(k/T,-l):I[y]=T*Math.pow(se/T,l)}}return!0}}registerProcessor("scale-processor",n)}).toString(),")()"],{type:"application/javascript"}));async function $e(n){await n.addModule(Ke),await n.addModule(He)}function je(n,e){switch(e){case"ScaleProcessor":return n.newAudioWorklet("scale-processor");case"FilterProcessor":return n.newAudioWorklet("filter-processor");default:Pt(e)}}var We={min:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Min"},max:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Max"},current:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Current"}},Tt={min:0,max:1,current:.5},Y=class extends p{constructor(e,t){let o={...Tt,...t.props},i=r=>je(r,"ScaleProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}get current(){return this.audioNode.parameters.get("current")}get min(){return this.audioNode.parameters.get("min")}get max(){return this.audioNode.parameters.get("max")}onAfterSetMin=e=>{this.min.value=e};onAfterSetMax=e=>{this.max.value=e};onAfterSetCurrent=e=>{this.current.value=e}};var Qe={steps:{kind:"number",min:1,max:16,step:1,label:"Steps"},bars:{kind:"number",min:1,max:16,step:1,label:"Steps"}},vt={sequences:[],steps:16,bars:1},J=class extends p{midiOutput;constructor(e,t){let o={...vt,...t.props};super(e,{...t,props:o})}};var Ye={pan:{kind:"number",min:-1,max:1,step:.01,label:"Pan"}},Je={pan:0},Ae=class extends p{constructor(e,t){let o={...Je,...t.props},i=r=>new StereoPannerNode(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetPan=e=>{this.audioNode.pan.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"pan",getAudioNode:()=>this.audioNode.pan})}},X=class extends m{constructor(e,t){let o={...Je,...t.props},i=(r,s)=>new Ae(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"pan"})}};var Xe={activeNotes:{kind:"array",label:"Active notes"}},At={activeNotes:[]},Z=class extends p{midiOutput;constructor(e,t){let o={...At,...t.props};super(e,{...t,props:o}),this.registerInputs(),this.registerOutputs()}sendMidi(e){this.midiOutput.onMidiEvent(e)}triggerAttack=(e,t)=>{this.props={activeNotes:[...this.props.activeNotes,e.fullName]},this.triggerPropsUpdate(),this.sendMidi(f.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(f.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var ie=(l=>(l.Master="Master",l.Oscillator="Oscillator",l.Gain="Gain",l.MidiSelector="MidiSelector",l.Envelope="Envelope",l.Filter="Filter",l.Scale="Scale",l.StereoPanner="StereoPanner",l.Inspector="Inspector",l.Constant="Constant",l.MidiMapper="MidiMapper",l.VirtualMidi="VirtualMidi",l.StepSequencer="StepSequencer",l.VoiceScheduler="VoiceScheduler",l))(ie||{}),Pe={Oscillator:Ue,Gain:we,Master:ze,MidiSelector:Le,Envelope:ke,Filter:qe,Scale:We,StereoPanner:Ye,Inspector:Ve,Constant:Ce,MidiMapper:Re,VirtualMidi:Xe,StepSequencer:Qe,VoiceScheduler:Oe};function re(n,e){switch(e.moduleType){case"Oscillator":return new Q(n,e);case"Gain":return new B(n,e);case"Master":return new $(n,e);case"MidiSelector":return new W(n,e);case"Envelope":return new _(n,e);case"Filter":return new H(n,e);case"Scale":return new Y(n,e);case"StereoPanner":return new X(n,e);case"Inspector":return new K(n,e);case"Constant":return new L(n,e);case"MidiMapper":return new j(n,e);case"VirtualMidi":return new Z(n,e);case"StepSequencer":return new J(n,e);case"VoiceScheduler":return new R(n,e);default:St(e)}}var O=class n{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=n._engines.get(e);return Ze(t),t}static get current(){return Ze(this._currentId),this.getById(this._currentId)}static async load(e){let{bpm:t,timeSignature:o,modules:i,routes:r}=e,s=new Ot,d=new n(s);return await d.initialize(),d.timeSignature=o,d.bpm=t,i.forEach(u=>{d.addModule(u)}),r.forEach(u=>{d.addRoute(u)}),d}constructor(e){this.id=Ct(),this.context=e,this.transport=new xt(this.context,{generator:(t,o)=>[],consumer:t=>{},onJump:t=>{},onStart:this.onStart,onStop:this.onStop,silence:t=>{}}),this.routes=new q(this),this.modules=new Map,this.midiDeviceManager=new E(this.context),n._engines.set(this.id,this),n._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await $e(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=re(this.id,e);return this.modules.set(t.id,t),t.serialize()}updateModule(e){let t=this.findModule(e.id);if(t.moduleType!==e.moduleType)throw Error(`The module id ${e.id} isn't moduleType ${e.moduleType}`);let o=bt(e.changes,["name","props"]);return Object.assign(t,o),t instanceof m&&e.changes.voices!==void 0&&(t.voices=e.changes.voices),t.serialize()}removeModule(e){this.modules.delete(e)}addRoute(e){return this.routes.addRoute(e)}removeRoute(e){this.routes.removeRoute(e)}validRoute(e){let{source:t,destination:o}=e,i=this.findIO(t.moduleId,t.ioName,"output"),r=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&r.isMidi()||i.isAudio()&&r.isAudio()}async start(){await this.resume(),this.transport.start()}stop(){this.transport.stop(),this.transport.reset()}pause(){this.transport.stop()}get bpm(){return this.transport.bpm}set bpm(e){this.transport.bpm=e}get timeSignature(){return this.transport.timeSignature}set timeSignature(e){this.transport.timeSignature=e}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear()}serialize(){return{bpm:this.bpm,timeSignature:this.timeSignature,modules:Array.from(this.modules.values()).map(e=>e.serialize()),routes:this.routes.serialize()}}findModule(e){let t=this.modules.get(e);if(!t)throw Error(`The module with id ${e} is not exists`);return t}findIO(e,t,o){return this.findModule(e)[`${o}s`].findByName(t)}findMidiDevice(e){return this.midiDeviceManager.find(e)}findMidiDeviceByName(e){return this.midiDeviceManager.findByName(e)}onPropsUpdate(e){this.propsUpdateCallbacks.push(e)}_triggerPropsUpdate(e){this.propsUpdateCallbacks.forEach(t=>{t(e)})}triggerVirtualMidi(e,t,o){let i=this.findModule(e);if(i.moduleType!=="VirtualMidi")throw Error("This is not a virtual mid");i.sendMidi(f.fromNote(t,o==="noteOn",this.context.currentTime))}onStart=e=>{this.modules.forEach(t=>{t.start(e)})};onStop=e=>{this.modules.forEach(t=>{t.stop(e)})}};import{TransportState as ms}from"@blibliki/transport";import{Context as hs}from"@blibliki/utils";export{hs as Context,O as Engine,A as MidiDevice,Ie as MidiMappingMode,te as MidiPortState,ie as ModuleType,a as Note,ne as OscillatorWave,ms as TransportState,Pe as moduleSchemas};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|