@blibliki/engine 0.3.10 → 0.4.0

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