@blibliki/engine 0.3.3 → 0.3.4

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{assertDefined as ot,pick as bt,uuidv4 as Nt}from"@blibliki/utils";import{upperFirst as be,uuidv4 as at}from"@blibliki/utils";import{assertNever as nt}from"@blibliki/utils";import{deterministicId as it,uuidv4 as rt}from"@blibliki/utils";var c=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;superInitialized=!1;_voices;_name;constructor(e,t){let{id:o,name:i,moduleType:s,voices:n,monoModuleConstructor:m,props:h}=t;this.audioModules=[],this.monoModuleConstructor=m,this.id=o??rt(),this.engineId=e,this.name=i,this.moduleType=s,this.voices=n||1,this._props={},this.props=h,this.inputs=new x(this),this.outputs=new S(this),this.superInitialized=!0}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),s=e.inputs.findByName(o);i.plug(s)}rePlugAll(e){this.superInitialized&&(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)};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=it(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 T.getById(this.engineId)}get context(){return this.engine.context}};import{deterministicId as st}from"@blibliki/utils";var ce=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}}},M=class extends ce{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var g=class extends M{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof b)&&ie(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof b)&&ie(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},b=class extends M{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof g)&&ie(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof g)&&ie(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function ie(r,e,t){if(e instanceof g||e instanceof b){let o=Math.max(r.module.voices,e.module.voices);for(let i=0;i<o;i++){let s=r.findIOByVoice(i%r.module.voices),n=e.findIOByVoice(i%e.module.voices);t?s.plug(n):s.unPlug(n)}}else for(let o=0;o<r.module.voices;o++){let i=r.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var G=class extends M{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},V=class extends M{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof g)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 g)return;let o=e.getAudioNode();try{o instanceof AudioParam?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var F=class extends M{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},z=class extends M{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof F)}};var R=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 c)throw Error("Not compatible");t=new G(this.module,e);break;case"audioOutput":if(this.module instanceof c)throw Error("Not compatible");t=new V(this.module,e);break;case"polyAudioInput":if(this.module instanceof u)throw Error("Not compatible");t=new g(this.module,e);break;case"polyAudioOutput":if(this.module instanceof u)throw Error("Not compatible");t=new b(this.module,e);break;case"midiInput":t=new F(this.module,e);break;case"midiOutput":t=new z(this.module,e);break;default:nt(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 this.collection.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 R{constructor(e){super("Input",e)}},S=class extends R{constructor(e){super("Output",e)}};import{Message as Se}from"webmidi";var ut=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]]),me=ut;var he=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],pt=2,p=class r{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of me)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new r(t)}static fromEvent(e){let t=he[e.data[1]%12],o=Math.floor(e.data[1]/12)-2;return new r(`${t}${o}`)}static notes(e=3){return he.map(t=>new r(`${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 me.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+pt)*12+this.noteIndex}get noteIndex(){return he.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)}};import{isNumber as xe}from"@blibliki/utils";import{Scheduler as dt}from"@ircam/sc-scheduling";var _=class{transport;internalScheduler;constructor(e){this.transport=e,this.internalScheduler=new dt(y,{currentTimeToProcessorTimeFunction:()=>this.transport.playhead})}start(e,t){this.internalScheduler.add(this.processor,a(e)),this.defer(t,e)}stop(e,t){this.defer(()=>{this.internalScheduler.remove(this.processor),t()},e)}defer(e,t){this.internalScheduler.defer(e,a(t))}processor=(e,t)=>(console.log(`playhead: ${l(t).toNotation()}`),e+.5)};var re=(o=>(o.playing="playing",o.stopped="stopped",o.paused="paused",o))(re||{}),N=class{bpm=120;timeSignature=[4,4];loopStart;loopEnd;state="stopped";offset=0;startTime=0;onStart;onStop;scheduler;constructor(e){this.onStart=e.onStart,this.onStop=e.onStop,this.loopStart=l("0:0:0"),this.scheduler=new _(this)}start({offset:e=this.offset,actionAt:t=y()}){if(this.state!=="playing")return this.validateFutureTime(t),this.scheduler.start(t,()=>{this.state="playing",this.offset=e,this.startTime=l(t).subtrack(this.offset)}),this.onStart?.(t),t}stop({actionAt:e=y()}){if(this.state!=="stopped")return this.validateFutureTime(e),this.scheduler.stop(e,()=>{this.state="stopped",this.offset=0}),this.onStop?.(e),e}pause({actionAt:e=y()}){if(this.state!=="paused")return this.validateFutureTime(e),this.scheduler.stop(e,()=>{this.state="paused",this.offset=l(e).subtrack(this.startTime)}),this.onStop?.(e),e}get playhead(){return this.state==="stopped"?l(0):this.state==="paused"?l(this.offset):l(y()).subtrack(this.startTime)}validateFutureTime(e){if(l(e).isBefore(y()))throw Error("Past time not allowed")}};function y(){return T.current.context.currentTime}function Oe(r){let e=performance.now()/1e3-y();return r/1e3-e}var l=r=>(r??=y(),r instanceof C?r:new C(r)),a=r=>(r??=y(),typeof r=="number"?r:l(r).toNumber()),C=class r{value;_notation;_number;constructor(e){this.value=e instanceof r?e.value:e,xe(this.value)?this._number=this.value:this._notation=this.value}add(e){return l(this.toNumber()+l(e).toNumber())}subtrack(e){return l(this.toNumber()-a(e))}isBefore(e){return this.toNumber()<a(e)}isAfter(e){return this.toNumber()>a(e)}isEqual(e){return this.toNumber()>a(e)}toNotation(){if(this._notation)return this._notation;let[e,t]=this.transport.timeSignature,o=16/t,i=e*o,s=60/this.transport.bpm,n=Math.floor(this.value/s*o),m=Math.floor(n/i),h=Math.floor(n%i/o),I=n%o;return this._notation=`${m}:${h}:${I}`,this._notation}toNumber(){if(xe(this._number))return this._number;let[e,t]=this.transport.timeSignature,o=60/this.transport.bpm,[i,s,n]=this.value.split(":").map(Number),m=i*e+s+n/(t/4);return this._number=m*o,this._number}get transport(){return T.current.transport}};var f=class r{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof p?e:new p(e);return new r(new Se(i.midiData(t)),o)}static fromCC(e,t,o){return new r(new Se(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t??l(),this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}defineNotes(){this.isNote&&(this.note||(this.note=p.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new r(this.message,this.triggeredAt);return t.voiceNo=e,t}};var u=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;superInitialized=!1;activeNotes;constructor(e,t){let{id:o,name:i,moduleType:s,voiceNo:n,audioNodeConstructor:m,props:h}=t;this.id=o??at(),this.engineId=e,this.name=i,this.moduleType=s,this.voiceNo=n??0,this.activeNotes=[],this.audioNode=m?.(this.context),this._props={},this.props=h,this.inputs=new x(this),this.outputs=new S(this),this.superInitialized=!0}get props(){return this._props}set props(e){Object.keys(e).forEach(t=>{let o=`onSet${be(t)}`;this[o]?.(e[t])}),this._props={...this._props,...e},Object.keys(e).forEach(t=>{let o=`onAfterSet${be(t)}`;this[o]?.(e[t])})}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),s=e.inputs.findByName(o);i.plug(s)}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)}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;default:throw Error("This type is not a note")}};triggerPropsUpdate(){this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props})}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 T.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as lt}from"@blibliki/utils";var L=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??lt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){for(let e in this.routes)this.removeRoute(e)}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,s=this.engine.findIO(o.moduleId,o.ioName,"output"),n=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:s,destinationIO:n}}};import{Output as Ne,WebMidi as ne}from"webmidi";var se=(t=>(t.connected="connected",t.disconnected="disconnected",t))(se||{}),O=class{id;name;eventListerCallbacks=[];input;constructor(e){this.id=e.id,this.name=e.name||`Device ${e.id}`,this.input=e,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,Oe(e.timestamp));switch(t.type){case"noteon":case"noteoff":this.eventListerCallbacks.forEach(o=>{o(t)})}}};var ct={a:new p("C3"),s:new p("D3"),d:new p("E3"),f:new p("F3"),g:new p("G3"),h:new p("A3"),j:new p("B3"),k:new p("C4"),l:new p("D4"),w:new p("C#3"),e:new p("D#3"),t:new p("F#3"),y:new p("G#3"),u:new p("A#3"),o:new p("C#4"),p:new p("D#4")},mt=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),k=class{id;name;state;eventListerCallbacks=[];constructor(){let{id:e,name:t,state:o}=mt();this.id=e,this.name=t,this.state=o,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.eventListerCallbacks.forEach(s=>{s(i)})};extractNote(e){if(!e.repeat)return ct[e.key]}};var D=class{devices=new Map;initialized=!1;listeners=[];constructor(){let e=new k;this.devices.set(e.id,e)}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.devices.get(e)}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{await ne.enable(),ne.inputs.forEach(e=>{this.devices.has(e.id)||this.devices.set(e.id,new O(e))})}catch(e){console.error("Error enabling WebMidi:",e)}}listenChanges(){ne.addListener("connected",e=>{let t=e.port;if(t instanceof Ne||this.devices.has(t.id))return;let o=new O(t);this.devices.set(o.id,o),this.listeners.forEach(i=>{i(o)})}),ne.addListener("disconnected",e=>{let t=e.port;if(t instanceof Ne)return;let o=this.devices.get(t.id);o&&(o instanceof k||(o.disconnect(),this.devices.delete(o.id),this.listeners.forEach(i=>{i(o)})))})}};import{assertNever as xt}from"@blibliki/utils";var Ce={},Ee={},Me=class extends u{activeNote=null;triggeredAt=l(0);constructor(e,t){let o={...Ee,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let s=o.fullName;switch(i){case"noteon":this.activeNote=s,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},U=class extends c{midiOutput;constructor(e,t){let o={...Ee,...t.props},i=(s,n)=>new Me(s,n);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)=>a(t.triggeredAt)-a(o.triggeredAt))[0],e}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var we={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},Fe={gain:1},E=class extends u{constructor(e,t){let o={...Fe,...t.props},i=s=>new GainNode(s);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onSetGain(e){this.audioNode.gain.value=e}registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},$=class extends c{constructor(e,t){let o={...Fe,...t.props},i=(s,n)=>new E(s,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var ke=20,Te=2e4,De={cutoff:Te,envelopeAmount:0,type:"lowpass",Q:1},qe={cutoff:{kind:"number",min:ke,max:Te,step:1,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:-100,max:100,step:.1,label:"Q"}},ye=class extends u{scale;amount;constructor(e,t){let o={...De,...t.props},i=s=>new BiquadFilterNode(s,{type:o.type,frequency:o.cutoff,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new E(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=q(e,{name:"scale",moduleType:"Scale",props:{min:ke,max:Te,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()}onSetType(e){this.audioNode.type=e}onSetCutoff(e){this.superInitialized&&(this.scale.props={current:e})}onSetQ(e){this.audioNode.Q.value=e}onSetEnvelopeAmount(e){this.superInitialized&&(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})}},W=class extends c{constructor(e,t){let o={...De,...t.props},i=(s,n)=>new ye(s,n);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 Be={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},ht={value:1},j=class extends u{isStated=!1;constructor(e,t){let o={...ht,...t.props},i=s=>new ConstantSourceNode(s);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onSetValue(e){this.audioNode.offset.value=e}start(e){this.isStated||(this.isStated=!0,this.audioNode.start(a(e)))}stop(e){this.audioNode.stop(a(e)),this.rePlugAll(()=>{this.audioNode=new ConstantSourceNode(this.context,{offset:this.props.value})}),this.isStated=!1}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,a(t)),this.start(t)};triggerRelease=()=>{}};import{createScaleNormalized as Ve}from"@blibliki/utils";var ze={attack:.1,decay:.2,sustain:0,release:.3},Re={attack:{kind:"number",min:1e-4,max:1,step:.01,label:"Attack"},decay:{kind:"number",min:0,max:1,step:.01,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:1,step:.01,label:"Release"}},Ge=Ve({min:.001,max:10}),ft=Ve({min:.001,max:5}),Ie=class extends u{constructor(e,t){let o={...ze,...t.props},i=s=>{let n=new GainNode(s);return n.gain.value=0,n};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let o=this.scaledAttack(),i=this.scaledDecay(),s=this.props.sustain,n=a(t);this.audioNode.gain.cancelAndHoldAtTime(n),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,n),this.audioNode.gain.exponentialRampToValueAtTime(1,n+o),s>0?this.audioNode.gain.exponentialRampToValueAtTime(s,n+o+i):this.audioNode.gain.exponentialRampToValueAtTime(.001,n+o+i)}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let o=this.scaledRelease(),i=a(t);this.audioNode.gain.cancelAndHoldAtTime(i);let s=this.audioNode.gain.value;s>=1e-4&&(this.audioNode.gain.setValueAtTime(s,i),this.audioNode.gain.exponentialRampToValueAtTime(1e-4,i+o-1e-4)),this.audioNode.gain.setValueAtTime(0,i+o)}scaledAttack(){return Ge(this.props.attack)}scaledDecay(){return ft(this.props.decay)}scaledRelease(){return Ge(this.props.release)}},K=class extends c{constructor(e,t){let o={...ze,...t.props},i=(s,n)=>new Ie(s,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{assertNever as Mt}from"@blibliki/utils";var _e=URL.createObjectURL(new Blob(["(",(()=>{class r 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 s=t[0],n=o[0],m=i.cutoff,h=i.resonance;for(let I=0;I<s.length;I++){let P=s[I],w=n[I];for(let A=0;A<P.length;A++){let d=P[A],B=m.length>1?m[A]:m[0],de=Math.max(20,Math.min(2e4,B)),ae=Math.log(de/20)/Math.log(2e4/20),v=Math.pow(.5,(1-ae)/.125),ve=1-Math.pow(.5,((h.length>1?h[A]:h[0])+.125)/.125)*v;this.s0=ve*this.s0-v*this.s1+v*d,this.s1=ve*this.s1+v*this.s0,w[A]=this.s1}}return!0}}registerProcessor("filter-processor",r)}).toString(),")()"],{type:"application/javascript"}));var Le=URL.createObjectURL(new Blob(["(",(()=>{class r extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let s=t[0],n=o[0],m=i.min,h=i.max,I=i.current;if(!s.length||s[0].length===0){for(let P of n){let w=(i.current.length>1,i.current[0]);P.fill(w)}return!0}for(let P=0;P<s.length;P++){let w=s[P],A=n[P];for(let d=0;d<w.length;d++){let B=w[d],de=m.length>1?m[d]:m[0],ae=h.length>1?h[d]:h[0],v=I.length>1?I[d]:I[0];B<0?A[d]=v*Math.pow(de/v,-B):A[d]=v*Math.pow(ae/v,B)}}return!0}}registerProcessor("scale-processor",r)}).toString(),")()"],{type:"application/javascript"}));async function Ue(r){await r.audioWorklet.addModule(Le),await r.audioWorklet.addModule(_e)}function ue(r,e){switch(e){case"ScaleProcessor":return new AudioWorkletNode(r,"scale-processor");case"FilterProcessor":return new AudioWorkletNode(r,"filter-processor");default:Mt(e)}}var $e=20,Pe=22050,We={cutoff:Pe,envelopeAmount:0,resonance:0},Qe={cutoff:{kind:"number",min:$e,max:Pe,step:1e-4,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},resonance:{kind:"number",min:0,max:4,step:.01,label:"resonance"}},ge=class extends u{scale;amount;constructor(e,t){let o={...We,...t.props},i=s=>ue(s,"FilterProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new E(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=q(e,{name:"scale",moduleType:"Scale",props:{min:$e,max:Pe,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.cutoff),this.registerDefaultIOs(),this.registerInputs()}get cutoff(){return this.audioNode.parameters.get("cutoff")}get resonance(){return this.audioNode.parameters.get("resonance")}onSetCutoff(e){this.superInitialized&&(this.scale.props={current:e})}onSetResonance(e){this.resonance.value=e}onSetEnvelopeAmount(e){this.superInitialized&&(this.amount.props={gain:e})}registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.scale.audioNode}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.resonance})}},H=class extends c{constructor(e,t){let o={...We,...t.props},i=(s,n)=>new ge(s,n);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 je={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},Tt={fftSize:512},X=class extends u{_buffer;constructor(e,t){let o={...Tt,...t.props},i=s=>new AnalyserNode(s);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onSetFftSize(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 It={},Ke={},Y=class extends u{constructor(e,t){let o={...It,...t.props},i=s=>s.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var He={selectedId:{kind:"string",label:"Midi device ID"}},gt={selectedId:void 0},J=class extends u{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...gt,...t.props};super(e,{...t,props:o}),this.addEventListener(this.props.selectedId),this.registerOutputs()}onSetSelectedId(e){this.superInitialized&&(this.removeEventListener(),this.addEventListener(e))}get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){if(!e)return;this.engine.findMidiDevice(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 Pt}from"@blibliki/utils";var Xe=-18,pe=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(pe||{}),Ye={wave:{kind:"enum",options:Object.values(pe),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-4,max:4,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${Xe}db Gain`}},Je={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},Ae=class extends u{isStated=!1;lowOutputGain;detuneGain;constructor(e,t){let o={...Je,...t.props},i=s=>new OscillatorNode(s);super(e,{...t,props:o,audioNodeConstructor:i}),this.lowOutputGain=new GainNode(this.context,{gain:Pt(Xe)}),this.applyOutputGain(),this.initializeGainDetune(),this.registerInputs(),this.registerOutputs()}onAfterSetWave(e){this.audioNode.type=e}onAfterSetFrequency(){this.updateFrequency()}onAfterSetFine(){this.updateFrequency()}onAfterSetCoarse(){this.updateFrequency()}onAfterSetOctave(){this.updateFrequency()}onAfterSetLowGain(){this.superInitialized&&this.rePlugAll()}start(e){this.isStated||(this.isStated=!0,this.audioNode.start(a(e)))}stop(e){this.audioNode.stop(a(e)),this.rePlugAll(()=>{this.audioNode=new OscillatorNode(this.context,{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 this.superInitialized?e*Math.pow(2,t/12+o+i/12):void 0}updateFrequency(e){this.finalFrequency!==void 0&&(e?this.audioNode.frequency.setValueAtTime(this.finalFrequency,a(e)):this.audioNode.frequency.value=this.finalFrequency)}applyOutputGain(){this.audioNode.connect(this.lowOutputGain)}initializeGainDetune(){this.detuneGain=new GainNode(this.context,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.props.lowGain?this.lowOutputGain:this.audioNode})}},Z=class extends c{constructor(e,t){let o={...Je,...t.props},i=(s,n)=>new Ae(s,n);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"})}};var Ze={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"}},At={min:0,max:1,current:.5},ee=class extends u{constructor(e,t){let o={...At,...t.props},i=s=>ue(s,"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")}onSetMin(e){this.min.value=e}onSetMax(e){this.max.value=e}onSetCurrent(e){this.current.value=e}};var et={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},te=class extends u{midiOutput;constructor(e,t){let o={...vt,...t.props};super(e,{...t,props:o})}};var tt={activeNotes:{kind:"array",label:"Active notes"}},Ot={activeNotes:[]},oe=class extends u{midiOutput;constructor(e,t){let o={...Ot,...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 Q=(d=>(d.Master="Master",d.Oscillator="Oscillator",d.Gain="Gain",d.MidiSelector="MidiSelector",d.Envelope="Envelope",d.Filter="Filter",d.BiquadFilter="BiquadFilter",d.Scale="Scale",d.Inspector="Inspector",d.Constant="Constant",d.VirtualMidi="VirtualMidi",d.StepSequencer="StepSequencer",d.VoiceScheduler="VoiceScheduler",d))(Q||{}),St={Oscillator:Ye,Gain:we,Master:Ke,MidiSelector:He,Envelope:Re,Filter:Qe,BiquadFilter:qe,Scale:Ze,Inspector:je,Constant:Be,VirtualMidi:tt,StepSequencer:et,VoiceScheduler:Ce};function q(r,e){switch(e.moduleType){case"Oscillator":return new Z(r,e);case"Gain":return new $(r,e);case"Master":return new Y(r,e);case"MidiSelector":return new J(r,e);case"Envelope":return new K(r,e);case"Filter":return new H(r,e);case"BiquadFilter":return new W(r,e);case"Scale":return new ee(r,e);case"Inspector":return new X(r,e);case"Constant":return new j(r,e);case"VirtualMidi":return new oe(r,e);case"StepSequencer":return new te(r,e);case"VoiceScheduler":return new U(r,e);default:xt(e)}}var T=class r{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=r._engines.get(e);return ot(t),t}static get current(){return ot(this._currentId),this.getById(this._currentId)}constructor(e){this.id=Nt(),this.context=e,this.transport=new N({onStart:this.onStart,onStop:this.onStop}),this.routes=new L(this),this.modules=new Map,this.midiDeviceManager=new D,r._engines.set(this.id,this),r._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=q(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 c&&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"),s=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&s.isMidi()||i.isAudio()&&s.isAudio()}async start(e={}){await this.resume(),this.transport.start(e)}stop(e={}){this.transport.stop(e)}pause(e={}){this.transport.pause(e)}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}get playhead(){return this.transport.playhead}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear(),r._engines.delete(this.id),r._currentId=r._engines.keys().next().value}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)}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"))}onStart=e=>{this.modules.forEach(t=>{t.start(e)})};onStop=e=>{this.modules.forEach(t=>{t.stop(e)})}};export{T as Engine,O as MidiDevice,se as MidiPortState,Q as ModuleType,p as Note,pe as OscillatorWave,re as TransportState,St as moduleSchemas};
1
+ import{Transport as yt}from"@blibliki/transport";import{assertDefined as Qe,pick as It,uuidv4 as gt}from"@blibliki/utils";import{upperFirst as Ie,uuidv4 as et}from"@blibliki/utils";import{assertNever as Ye}from"@blibliki/utils";import{deterministicId as je,uuidv4 as He}from"@blibliki/utils";var l=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;superInitialized=!1;_voices;_name;constructor(e,t){let{id:o,name:i,moduleType:s,voices:u,monoModuleConstructor:m,props:h}=t;this.audioModules=[],this.monoModuleConstructor=m,this.id=o??He(),this.engineId=e,this.name=i,this.moduleType=s,this.voices=u||1,this._props={},this.props=h,this.inputs=new P(this),this.outputs=new v(this),this.superInitialized=!0}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),s=e.inputs.findByName(o);i.plug(s)}rePlugAll(e){this.superInitialized&&(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)};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=je(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 x.getById(this.engineId)}get context(){return this.engine.context}};import{deterministicId as Xe}from"@blibliki/utils";var se=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=Xe(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}}},c=class extends se{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var M=class extends c{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof A)&&Y(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof A)&&Y(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},A=class extends c{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof M)&&Y(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof M)&&Y(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function Y(r,e,t){if(e instanceof M||e instanceof A){let o=Math.max(r.module.voices,e.module.voices);for(let i=0;i<o;i++){let s=r.findIOByVoice(i%r.module.voices),u=e.findIOByVoice(i%e.module.voices);t?s.plug(u):s.unPlug(u)}}else for(let o=0;o<r.module.voices;o++){let i=r.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var F=class extends c{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},D=class extends c{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof M)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 M)return;let o=e.getAudioNode();try{o instanceof AudioParam?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var S=class extends c{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},k=class extends c{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof S)}};var q=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 l)throw Error("Not compatible");t=new F(this.module,e);break;case"audioOutput":if(this.module instanceof l)throw Error("Not compatible");t=new D(this.module,e);break;case"polyAudioInput":if(this.module instanceof n)throw Error("Not compatible");t=new M(this.module,e);break;case"polyAudioOutput":if(this.module instanceof n)throw Error("Not compatible");t=new A(this.module,e);break;case"midiInput":t=new S(this.module,e);break;case"midiOutput":t=new k(this.module,e);break;default:Ye(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 this.collection.map(e=>e.serialize())}validateUniqName(e){if(this.collection.some(t=>t.name===e))throw Error(`An io with name ${e} is already exists`)}},P=class extends q{constructor(e){super("Input",e)}},v=class extends q{constructor(e){super("Output",e)}};import{Message as ye}from"webmidi";var Je=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]]),ne=Je;var ue=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],Ze=2,p=class r{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of ne)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new r(t)}static fromEvent(e){let t=ue[e.data[1]%12],o=Math.floor(e.data[1]/12)-2;return new r(`${t}${o}`)}static notes(e=3){return ue.map(t=>new r(`${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 ne.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+Ze)*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 a=class r{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof p?e:new p(e);return new r(new ye(i.midiData(t)),o)}static fromCC(e,t,o){return new r(new ye(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"}defineNotes(){this.isNote&&(this.note||(this.note=p.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new r(this.message,this.triggeredAt);return t.voiceNo=e,t}};var n=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;superInitialized=!1;activeNotes;constructor(e,t){let{id:o,name:i,moduleType:s,voiceNo:u,audioNodeConstructor:m,props:h}=t;this.id=o??et(),this.engineId=e,this.name=i,this.moduleType=s,this.voiceNo=u??0,this.activeNotes=[],this.audioNode=m?.(this.context),this._props={},this.props=h,this.inputs=new P(this),this.outputs=new v(this),this.superInitialized=!0}get props(){return this._props}set props(e){Object.keys(e).forEach(t=>{let o=`onSet${Ie(t)}`;this[o]?.(e[t])}),this._props={...this._props,...e},Object.keys(e).forEach(t=>{let o=`onAfterSet${Ie(t)}`;this[o]?.(e[t])})}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),s=e.inputs.findByName(o);i.plug(s)}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)}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;default:throw Error("This type is not a note")}};triggerPropsUpdate(){this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props})}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 x.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as tt}from"@blibliki/utils";var G=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??tt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){for(let e in this.routes)this.removeRoute(e)}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,s=this.engine.findIO(o.moduleId,o.ioName,"output"),u=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:s,destinationIO:u}}};import{Output as ge,WebMidi as Z}from"webmidi";var J=(t=>(t.connected="connected",t.disconnected="disconnected",t))(J||{}),T=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 a(e.message,this.context.browserToContextTime(e.timestamp));switch(t.type){case"noteon":case"noteoff":this.eventListerCallbacks.forEach(o=>{o(t)})}}};var ot={a:new p("C3"),s:new p("D3"),d:new p("E3"),f:new p("F3"),g:new p("G3"),h:new p("A3"),j:new p("B3"),k:new p("C4"),l:new p("D4"),w:new p("C#3"),e:new p("D#3"),t:new p("F#3"),y:new p("G#3"),u:new p("A#3"),o:new p("C#4"),p:new p("D#4")},it=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),b=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=it();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=a.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(s=>{s(i)})};extractNote(e){if(!e.repeat)return ot[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)}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 T(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 ge||this.devices.has(t.id))return;let o=new T(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 ge)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 Mt}from"@blibliki/utils";var Te={},Pe={},de=class extends n{activeNote=null;triggeredAt=0;constructor(e,t){let o={...Pe,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let s=o.fullName;switch(i){case"noteon":this.activeNote=s,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},V=class extends l{midiOutput;constructor(e,t){let o={...Pe,...t.props},i=(s,u)=>new de(s,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 ve={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},xe={gain:1},O=class extends n{constructor(e,t){let o={...xe,...t.props},i=s=>new GainNode(s.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onSetGain(e){this.audioNode.gain.value=e}registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},B=class extends l{constructor(e,t){let o={...xe,...t.props},i=(s,u)=>new O(s,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var Ae=20,ae=2e4,Oe={cutoff:ae,envelopeAmount:0,type:"lowpass",Q:1},Ce={cutoff:{kind:"number",min:Ae,max:ae,step:1,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:-100,max:100,step:.1,label:"Q"}},le=class extends n{scale;amount;constructor(e,t){let o={...Oe,...t.props},i=s=>new BiquadFilterNode(s.audioContext,{type:o.type,frequency:o.cutoff,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new O(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=E(e,{name:"scale",moduleType:"Scale",props:{min:Ae,max:ae,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()}onSetType(e){this.audioNode.type=e}onSetCutoff(e){this.superInitialized&&(this.scale.props={current:e})}onSetQ(e){this.audioNode.Q.value=e}onSetEnvelopeAmount(e){this.superInitialized&&(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})}},R=class extends l{constructor(e,t){let o={...Oe,...t.props},i=(s,u)=>new le(s,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 Se={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},rt={value:1},L=class extends n{isStated=!1;constructor(e,t){let o={...rt,...t.props},i=s=>new ConstantSourceNode(s.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onSetValue(e){this.audioNode.offset.value=e}start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){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{createScaleNormalized as Ne}from"@blibliki/utils";var Ee={attack:.1,decay:.2,sustain:0,release:.3},we={attack:{kind:"number",min:1e-4,max:1,step:.01,label:"Attack"},decay:{kind:"number",min:0,max:1,step:.01,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:1,step:.01,label:"Release"}},be=Ne({min:.001,max:10}),st=Ne({min:.001,max:5}),ce=class extends n{constructor(e,t){let o={...Ee,...t.props},i=s=>{let u=new GainNode(s.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.scaledAttack(),i=this.scaledDecay(),s=this.props.sustain;this.audioNode.gain.cancelAndHoldAtTime(t),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,t),this.audioNode.gain.exponentialRampToValueAtTime(1,t+o),s>0?this.audioNode.gain.exponentialRampToValueAtTime(s,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.scaledRelease();this.audioNode.gain.cancelAndHoldAtTime(t);let i=this.audioNode.gain.value;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)}scaledAttack(){return be(this.props.attack)}scaledDecay(){return st(this.props.decay)}scaledRelease(){return be(this.props.release)}},_=class extends l{constructor(e,t){let o={...Ee,...t.props},i=(s,u)=>new ce(s,u);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{assertNever as nt}from"@blibliki/utils";var Fe=URL.createObjectURL(new Blob(["(",(()=>{class r 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 s=t[0],u=o[0],m=i.cutoff,h=i.resonance;for(let g=0;g<s.length;g++){let f=s[g],C=u[g];for(let y=0;y<f.length;y++){let d=f[y],w=m.length>1?m[y]:m[0],oe=Math.max(20,Math.min(2e4,w)),ie=Math.log(oe/20)/Math.log(2e4/20),I=Math.pow(.5,(1-ie)/.125),fe=1-Math.pow(.5,((h.length>1?h[y]:h[0])+.125)/.125)*I;this.s0=fe*this.s0-I*this.s1+I*d,this.s1=fe*this.s1+I*this.s0,C[y]=this.s1}}return!0}}registerProcessor("filter-processor",r)}).toString(),")()"],{type:"application/javascript"}));var De=URL.createObjectURL(new Blob(["(",(()=>{class r extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let s=t[0],u=o[0],m=i.min,h=i.max,g=i.current;if(!s.length||s[0].length===0){for(let f of u){let C=(i.current.length>1,i.current[0]);f.fill(C)}return!0}for(let f=0;f<s.length;f++){let C=s[f],y=u[f];for(let d=0;d<C.length;d++){let w=C[d],oe=m.length>1?m[d]:m[0],ie=h.length>1?h[d]:h[0],I=g.length>1?g[d]:g[0];w<0?y[d]=I*Math.pow(oe/I,-w):y[d]=I*Math.pow(ie/I,w)}}return!0}}registerProcessor("scale-processor",r)}).toString(),")()"],{type:"application/javascript"}));async function ke(r){await r.addModule(De),await r.addModule(Fe)}function ee(r,e){switch(e){case"ScaleProcessor":return r.newAudioWorklet("scale-processor");case"FilterProcessor":return r.newAudioWorklet("filter-processor");default:nt(e)}}var qe=20,he=22050,Ge={cutoff:he,envelopeAmount:0,resonance:0},Ve={cutoff:{kind:"number",min:qe,max:he,step:1e-4,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},resonance:{kind:"number",min:0,max:4,step:.01,label:"resonance"}},me=class extends n{scale;amount;constructor(e,t){let o={...Ge,...t.props},i=s=>ee(s,"FilterProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new O(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=E(e,{name:"scale",moduleType:"Scale",props:{min:qe,max:he,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.cutoff),this.registerDefaultIOs(),this.registerInputs()}get cutoff(){return this.audioNode.parameters.get("cutoff")}get resonance(){return this.audioNode.parameters.get("resonance")}onSetCutoff(e){this.superInitialized&&(this.scale.props={current:e})}onSetResonance(e){this.resonance.value=e}onSetEnvelopeAmount(e){this.superInitialized&&(this.amount.props={gain:e})}registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.scale.audioNode}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.resonance})}},U=class extends l{constructor(e,t){let o={...Ge,...t.props},i=(s,u)=>new me(s,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 Be={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},pt={fftSize:512},$=class extends n{_buffer;constructor(e,t){let o={...pt,...t.props},i=s=>new AnalyserNode(s.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onSetFftSize(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={},Re={},K=class extends n{constructor(e,t){let o={...dt,...t.props},i=s=>s.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var ze={selectedId:{kind:"string",label:"Midi device ID"}},lt={selectedId:void 0},W=class extends n{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...lt,...t.props};super(e,{...t,props:o}),this.addEventListener(this.props.selectedId),this.registerOutputs()}onSetSelectedId(e){this.superInitialized&&(this.removeEventListener(),this.addEventListener(e))}get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){if(!e)return;this.engine.findMidiDevice(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 at}from"@blibliki/utils";var Le=-18,te=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(te||{}),_e={wave:{kind:"enum",options:Object.values(te),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:-4,max:4,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${Le}db Gain`}},Ue={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},Me=class extends n{isStated=!1;lowOutputGain;detuneGain;constructor(e,t){let o={...Ue,...t.props},i=s=>new OscillatorNode(s.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.lowOutputGain=new GainNode(this.context.audioContext,{gain:at(Le)}),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(){this.superInitialized&&this.rePlugAll()}start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){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 this.superInitialized?e*Math.pow(2,t/12+o+i/12):void 0}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.lowOutputGain)}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.props.lowGain?this.lowOutputGain:this.audioNode})}},Q=class extends l{constructor(e,t){let o={...Ue,...t.props},i=(s,u)=>new Me(s,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"})}};var $e={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"}},ct={min:0,max:1,current:.5},j=class extends n{constructor(e,t){let o={...ct,...t.props},i=s=>ee(s,"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")}onSetMin(e){this.min.value=e}onSetMax(e){this.max.value=e}onSetCurrent(e){this.current.value=e}};var Ke={steps:{kind:"number",min:1,max:16,step:1,label:"Steps"},bars:{kind:"number",min:1,max:16,step:1,label:"Steps"}},mt={sequences:[],steps:16,bars:1},H=class extends n{midiOutput;constructor(e,t){let o={...mt,...t.props};super(e,{...t,props:o})}};var We={activeNotes:{kind:"array",label:"Active notes"}},ht={activeNotes:[]},X=class extends n{midiOutput;constructor(e,t){let o={...ht,...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(a.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(a.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var z=(d=>(d.Master="Master",d.Oscillator="Oscillator",d.Gain="Gain",d.MidiSelector="MidiSelector",d.Envelope="Envelope",d.Filter="Filter",d.BiquadFilter="BiquadFilter",d.Scale="Scale",d.Inspector="Inspector",d.Constant="Constant",d.VirtualMidi="VirtualMidi",d.StepSequencer="StepSequencer",d.VoiceScheduler="VoiceScheduler",d))(z||{}),ft={Oscillator:_e,Gain:ve,Master:Re,MidiSelector:ze,Envelope:we,Filter:Ve,BiquadFilter:Ce,Scale:$e,Inspector:Be,Constant:Se,VirtualMidi:We,StepSequencer:Ke,VoiceScheduler:Te};function E(r,e){switch(e.moduleType){case"Oscillator":return new Q(r,e);case"Gain":return new B(r,e);case"Master":return new K(r,e);case"MidiSelector":return new W(r,e);case"Envelope":return new _(r,e);case"Filter":return new U(r,e);case"BiquadFilter":return new R(r,e);case"Scale":return new j(r,e);case"Inspector":return new $(r,e);case"Constant":return new L(r,e);case"VirtualMidi":return new X(r,e);case"StepSequencer":return new H(r,e);case"VoiceScheduler":return new V(r,e);default:Mt(e)}}var x=class r{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=r._engines.get(e);return Qe(t),t}static get current(){return Qe(this._currentId),this.getById(this._currentId)}constructor(e){this.id=gt(),this.context=e,this.transport=new yt(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),r._engines.set(this.id,this),r._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await ke(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=E(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=It(e.changes,["name","props"]);return Object.assign(t,o),t instanceof l&&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"),s=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&s.isMidi()||i.isAudio()&&s.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()}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)}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(a.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 As}from"@blibliki/transport";export{x as Engine,T as MidiDevice,J as MidiPortState,z as ModuleType,p as Note,te as OscillatorWave,As as TransportState,ft as moduleSchemas};
2
2
  //# sourceMappingURL=index.js.map