@blibliki/engine 0.5.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -2
- package/dist/index.d.ts +501 -107
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/Engine.ts +46 -29
- package/src/core/index.ts +11 -2
- package/src/core/midi/BaseMidiDevice.ts +47 -0
- package/src/core/midi/ComputerKeyboardDevice.ts +2 -1
- package/src/core/midi/MidiDeviceManager.ts +125 -31
- package/src/core/midi/{MidiDevice.ts → MidiInputDevice.ts} +6 -30
- package/src/core/midi/MidiOutputDevice.ts +23 -0
- package/src/core/midi/adapters/NodeMidiAdapter.ts +99 -13
- package/src/core/midi/adapters/WebMidiAdapter.ts +68 -10
- package/src/core/midi/adapters/types.ts +13 -4
- package/src/core/midi/controllers/BaseController.ts +14 -0
- package/src/core/module/Module.ts +121 -13
- package/src/core/module/PolyModule.ts +36 -0
- package/src/core/module/VoiceScheduler.ts +150 -10
- package/src/core/module/index.ts +9 -4
- package/src/index.ts +27 -3
- package/src/modules/Chorus.ts +222 -0
- package/src/modules/Constant.ts +2 -2
- package/src/modules/Delay.ts +347 -0
- package/src/modules/Distortion.ts +182 -0
- package/src/modules/Envelope.ts +158 -92
- package/src/modules/Filter.ts +7 -7
- package/src/modules/Gain.ts +2 -2
- package/src/modules/LFO.ts +287 -0
- package/src/modules/LegacyEnvelope.ts +146 -0
- package/src/modules/{MidiSelector.ts → MidiInput.ts} +26 -19
- package/src/modules/MidiMapper.ts +59 -4
- package/src/modules/MidiOutput.ts +121 -0
- package/src/modules/Noise.ts +259 -0
- package/src/modules/Oscillator.ts +9 -3
- package/src/modules/Reverb.ts +379 -0
- package/src/modules/Scale.ts +49 -4
- package/src/modules/StepSequencer.ts +410 -22
- package/src/modules/StereoPanner.ts +1 -1
- package/src/modules/index.ts +142 -29
- package/src/processors/custom-envelope-processor.ts +125 -0
- package/src/processors/index.ts +10 -0
- package/src/processors/lfo-processor.ts +123 -0
- package/src/processors/scale-processor.ts +42 -5
- package/src/utils/WetDryMixer.ts +123 -0
- package/src/utils/expandPatternSequence.ts +18 -0
- package/src/utils/index.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Transport as Ut}from"@blibliki/transport";import{assertDefined as dt,Context as _t,pick as Ht,uuidv4 as $t}from"@blibliki/utils";import{upperFirst as gt,uuidv4 as It,requestAnimationFrame as Pt}from"@blibliki/utils";import{assertNever as Mt}from"@blibliki/utils";import{sortBy as ht}from"es-toolkit";import{deterministicId as at,uuidv4 as lt,requestAnimationFrame as ct}from"@blibliki/utils";var h=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;_voices;_name;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voices:s,monoModuleConstructor:p,props:u}=t;this.audioModules=[],this.monoModuleConstructor=p,this.id=o??lt(),this.engineId=e,this.name=i,this.moduleType=r,this._props=u,this.inputs=new b(this),this.outputs=new C(this),queueMicrotask(()=>{this.voices=s||1,this.props=u,this.triggerPropsUpdate()})}get name(){return this._name}set name(e){this._name=e,this.audioModules.forEach(t=>t.name=e)}get props(){return this._props}set props(e){this._props={...this._props,...e},this.audioModules.forEach(t=>t.props=e)}get voices(){return this._voices}set voices(e){this._voices=e,this.adjustNumberOfModules(),this.rePlugAll()}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voices:this.voices,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll(),this.audioModules.forEach(e=>{e.dispose()})}onMidiEvent=e=>{let t=e.voiceNo??0;this.findVoice(t).onMidiEvent(e)};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){ct(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voices:this.voices,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}findVoice(e){let t=this.audioModules.find(o=>o.voiceNo===e);if(!t)throw Error(`Voice ${e} on module ${this.name} not found`);return t}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),(e==="in"||e==="both")&&this.registerAudioInput({name:"in"}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out"})}registerAudioInput(e){return this.inputs.add({...e,ioType:"polyAudioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"polyAudioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}adjustNumberOfModules(){if(this.audioModules.length!==this.voices){if(this.audioModules.length>this.voices)this.audioModules.pop()?.dispose();else{let e=this.audioModules.length,t=at(this.id,e.toString()),o=this.monoModuleConstructor(this.engineId,{id:t,name:this.name,moduleType:this.moduleType,voiceNo:e,props:{...this.props}});this.audioModules.push(o)}this.adjustNumberOfModules()}}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{AudioParam as Ne}from"@blibliki/utils/web-audio-api";import{deterministicId as mt}from"@blibliki/utils";var ae=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=mt(this.module.id,this.name),this.connections=[]}plug(e,t=!0){this.connections.push(e),t&&e.plug(this,!1)}unPlug(e,t=!0){this.connections=this.connections.filter(o=>o.id!==e.id),t&&e.unPlug(this,!1)}rePlugAll(e){let t=this.connections;this.unPlugAll(),e&&e(),t.forEach(o=>{this.plug(o)})}unPlugAll(){this.connections.forEach(e=>{this.unPlug(e)})}isAudio(){return this.ioType==="audioInput"||this.ioType==="audioOutput"||this.ioType==="polyAudioInput"||this.ioType==="polyAudioOutput"}isMidi(){return this.ioType==="midiInput"||this.ioType==="midiOutput"}serialize(){return{id:this.id,name:this.name,ioType:this.ioType,moduleId:this.module.id}}},I=class extends ae{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var P=class extends I{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof N)&&oe(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof N)&&oe(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},N=class extends I{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof P)&&oe(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof P)&&oe(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function oe(n,e,t){if(e instanceof P||e instanceof N){let o=Math.max(n.module.voices,e.module.voices);for(let i=0;i<o;i++){let r=n.findIOByVoice(i%n.module.voices),s=e.findIOByVoice(i%e.module.voices);t?r.plug(s):r.unPlug(s)}}else for(let o=0;o<n.module.voices;o++){let i=n.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var D=class extends I{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},F=class extends I{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof P)return;let o=e.getAudioNode();o instanceof Ne?this.getAudioNode().connect(o):this.getAudioNode().connect(o)}unPlug(e,t=!0){if(super.unPlug(e,t),e instanceof P)return;let o=e.getAudioNode();try{o instanceof Ne?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var E=class extends I{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},z=class extends I{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof E)}};var G=class{module;collection=[];collectionType;constructor(e,t){this.collectionType=e,this.module=t}add(e){let t;switch(this.validateUniqName(e.name),e.ioType){case"audioInput":if(this.module instanceof h)throw Error("Not compatible");t=new D(this.module,e);break;case"audioOutput":if(this.module instanceof h)throw Error("Not compatible");t=new F(this.module,e);break;case"polyAudioInput":if(this.module instanceof d)throw Error("Not compatible");t=new P(this.module,e);break;case"polyAudioOutput":if(this.module instanceof d)throw Error("Not compatible");t=new N(this.module,e);break;case"midiInput":t=new E(this.module,e);break;case"midiOutput":t=new z(this.module,e);break;default:Mt(e)}return this.collection.push(t),t}unPlugAll(){this.collection.forEach(e=>{e.unPlugAll()})}rePlugAll(e){this.collection.forEach(t=>{t.rePlugAll(e)})}find(e){let t=this.collection.find(o=>o.id===e);if(!t)throw Error(`The io with id ${e} is not exists`);return t}findByName(e){let t=this.collection.find(o=>o.name===e);if(!t)throw Error(`The io with name ${e} is not exists`);return t}serialize(){return ht(this.collection,[e=>e.isMidi()?-1:1]).map(e=>e.serialize())}validateUniqName(e){if(this.collection.some(t=>t.name===e))throw Error(`An io with name ${e} is already exists`)}},b=class extends G{constructor(e){super("Input",e)}},C=class extends G{constructor(e){super("Output",e)}};var ft=new Map([["C0",16.35],["C#0",17.32],["Db0",17.32],["D0",18.35],["D#0",19.45],["Eb0",19.45],["E0",20.6],["F0",21.83],["F#0",23.12],["Gb0",23.12],["G0",24.5],["G#0",25.96],["Ab0",25.96],["A0",27.5],["A#0",29.14],["Bb0",29.14],["B0",30.87],["C1",32.7],["C#1",34.65],["Db1",34.65],["D1",36.71],["D#1",38.89],["Eb1",38.89],["E1",41.2],["F1",43.65],["F#1",46.25],["Gb1",46.25],["G1",49],["G#1",51.91],["Ab1",51.91],["A1",55],["A#1",58.27],["Bb1",58.27],["B1",61.74],["C2",65.41],["C#2",69.3],["Db2",69.3],["D2",73.42],["D#2",77.78],["Eb2",77.78],["E2",82.41],["F2",87.31],["F#2",92.5],["Gb2",92.5],["G2",98],["G#2",103.83],["Ab2",103.83],["A2",110],["A#2",116.54],["Bb2",116.54],["B2",123.47],["C3",130.81],["C#3",138.59],["Db3",138.59],["D3",146.83],["D#3",155.56],["Eb3",155.56],["E3",164.81],["F3",174.61],["F#3",185],["Gb3",185],["G3",196],["G#3",207.65],["Ab3",207.65],["A3",220],["A#3",233.08],["Bb3",233.08],["B3",246.94],["C4",261.63],["C#4",277.18],["Db4",277.18],["D4",293.66],["D#4",311.13],["Eb4",311.13],["E4",329.63],["F4",349.23],["F#4",369.99],["Gb4",369.99],["G4",392],["G#4",415.3],["Ab4",415.3],["A4",440],["A#4",466.16],["Bb4",466.16],["B4",493.88],["C5",523.25],["C#5",554.37],["Db5",554.37],["D5",587.33],["D#5",622.25],["Eb5",622.25],["E5",659.26],["F5",698.46],["F#5",739.99],["Gb5",739.99],["G5",783.99],["G#5",830.61],["Ab5",830.61],["A5",880],["A#5",932.33],["Bb5",932.33],["B5",987.77],["C6",1046.5],["C#6",1108.73],["Db6",1108.73],["D6",1174.66],["D#6",1244.51],["Eb6",1244.51],["E6",1318.51],["F6",1396.91],["F#6",1479.98],["Gb6",1479.98],["G6",1567.98],["G#6",1661.22],["Ab6",1661.22],["A6",1760],["A#6",1864.66],["Bb6",1864.66],["B6",1975.53],["C7",2093],["C#7",2217.46],["Db7",2217.46],["D7",2349.32],["D#7",2489.02],["Eb7",2489.02],["E7",2637.02],["F7",2793.83],["F#7",2959.96],["Gb7",2959.96],["G7",3135.96],["G#7",3322.44],["Ab7",3322.44],["A7",3520],["A#7",3729.31],["Bb7",3729.31],["B7",3951.07],["C8",4186.01],["C#8",4434.92],["Db8",4434.92],["D8",4698.64],["D#8",4978.03],["Eb8",4978.03]]),le=ft;var ce=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],yt=2,m=class n{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of le)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new n(t)}static fromEvent(e){let t=e.data[1];if(t===void 0)throw new Error("Invalid MIDI message: missing data byte");let o=ce[t%12];if(!o)throw new Error(`Invalid MIDI note number: ${t}`);let i=Math.floor(t/12)-2;return new n(`${o}${i}`)}static notes(e=3){return ce.map(t=>new n(`${t}${e}`))}constructor(e){typeof e=="string"?this.fromString(e):this.fromProps(e)}get isSemi(){return this.name.endsWith("#")}get fullName(){return`${this.name}${this.octave}`}get frequency(){return le.get(`${this.name}${this.octave}`)}midiData(e=!0){let t=e?144:128;return new Uint8Array([t,this.midiNumber,this.velocity*100])}get midiNumber(){return(this.octave+yt)*12+this.noteIndex}get noteIndex(){return ce.indexOf(this.name)}valueOf(){return this.fullName}serialize(){return{name:this.name,octave:this.octave,frequency:this.frequency,velocity:this.velocity,duration:this.duration}}fromString(e){let t=/(\w#?)(\d)?/.exec(e);if(!t)throw new Error(`Invalid note string: ${e}`);let o=t[1];if(!o)throw new Error(`Invalid note name in: ${e}`);this.name=o,this.octave=t[2]?parseInt(t[2]):1}fromProps(e){Object.assign(this,e)}};var S=class{data;constructor(e){this.data=e}get dataBytes(){return Array.from(this.data.slice(1))}get type(){let e=this.data[0];if(e===void 0)return"unknown";switch(e&240){case 144:return this.data[2]===0?"noteoff":"noteon";case 128:return"noteoff";case 176:return"controlchange";case 224:return"pitchbend";case 208:return"channelaftertouch";case 160:return"keyaftertouch";case 192:return"programchange";default:return"unknown"}}};var y=class n{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof m?e:new m(e);return new n(new S(i.midiData(t)),o)}static fromCC(e,t,o){return new n(new S(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t,this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}get isCC(){return this.type==="controlchange"}get cc(){if(this.isCC)return this.message.dataBytes[0]}get ccValue(){if(this.isCC)return this.message.dataBytes[1]}defineNotes(){this.isNote&&(this.note||(this.note=m.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new n(this.message,this.triggeredAt);return t.voiceNo=e,t}};var d=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;activeNotes;pendingUIUpdates=!1;constructor(e,t){let{id:o,name:i,moduleType:r,voiceNo:s,audioNodeConstructor:p,props:u}=t;this.id=o??It(),this.engineId=e,this.name=i,this.moduleType=r,this.voiceNo=s??0,this.activeNotes=[],this.audioNode=p?.(this.context),this._props=u,this.inputs=new b(this),this.outputs=new C(this),queueMicrotask(()=>{this.props=u})}get props(){return this._props}set props(e){let t={...e};Object.keys(e).forEach(o=>{let i=e[o];if(i!==void 0){let r=this.callPropHook("onSet",o,i);r!==void 0&&(t[o]=r)}}),this._props={...this._props,...t},Object.keys(t).forEach(o=>{let i=t[o];i!==void 0&&this.callPropHook("onAfterSet",o,i)})}callPropHook(e,t,o){let i=`${e}${gt(t)}`,r=this[i];if(typeof r=="function")return r.call(this,o)}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voiceNo:this.voiceNo,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}start(e){}stop(e){}triggerAttack(e,t){this.activeNotes.some(o=>o.fullName===e.fullName)||this.activeNotes.push(e)}triggerRelease(e,t){this.activeNotes=this.activeNotes.filter(o=>o.fullName!==e.fullName)}handleCC(e,t){}onMidiEvent=e=>{let{note:t,triggeredAt:o}=e;switch(e.type){case"noteon":{this.triggerAttack(t,o);break}case"noteoff":this.triggerRelease(t,o);break;case"controlchange":this.handleCC(e,o);break;default:throw Error("This type is not a note")}};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){Pt(()=>{this.engine._triggerPropsUpdate({id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props}),this.pendingUIUpdates=!1})}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}registerDefaultIOs(e="both"){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),this.audioNode&&((e==="in"||e==="both")&&this.registerAudioInput({name:"in",getAudioNode:()=>this.audioNode}),(e==="out"||e==="both")&&this.registerAudioOutput({name:"out",getAudioNode:()=>this.audioNode}))}registerAudioInput(e){return this.inputs.add({...e,ioType:"audioInput"})}registerAudioOutput(e){return this.outputs.add({...e,ioType:"audioOutput"})}registerMidiInput(e){return this.inputs.add({...e,ioType:"midiInput"})}registerMidiOutput(e){return this.outputs.add({...e,ioType:"midiOutput"})}get engine(){return O.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as Tt}from"@blibliki/utils";var V=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??Tt(),o={...e,id:t};return this.routes.set(t,o),this.plug(t),o}removeRoute(e){this.unPlug(e),this.routes.delete(e)}clear(){this.routes.forEach((e,t)=>{this.removeRoute(t)})}replug(){this.routes.forEach((e,t)=>{let{sourceIO:o,destinationIO:i}=this.getIOs(t);o.rePlugAll(),i.rePlugAll()})}serialize(){return Array.from(this.routes.values())}plug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.plug(o)}unPlug(e){let{sourceIO:t,destinationIO:o}=this.getIOs(e);t.unPlug(o)}find(e){let t=this.routes.get(e);if(!t)throw Error(`Route with id ${e} not found`);return t}getIOs(e){let t=this.find(e),{source:o,destination:i}=t,r=this.engine.findIO(o.moduleId,o.ioName,"output"),s=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:r,destinationIO:s}}};var ie=(t=>(t.connected="connected",t.disconnected="disconnected",t))(ie||{}),A=class{id;name;eventListerCallbacks=[];context;input;messageHandler=null;constructor(e,t){this.id=e.id,this.name=e.name,this.input=e,this.context=t,this.connect()}get state(){return this.input.state}connect(){this.messageHandler=e=>{this.processEvent(e)},this.input.addEventListener(this.messageHandler)}disconnect(){this.messageHandler&&(this.input.removeEventListener(this.messageHandler),this.messageHandler=null)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}processEvent(e){let t=new S(e.data),o=new y(t,this.context.browserToContextTime(e.timeStamp));switch(o.type){case"noteon":case"noteoff":case"controlchange":this.eventListerCallbacks.forEach(i=>{i(o)})}}};var vt={a:new m("C3"),s:new m("D3"),d:new m("E3"),f:new m("F3"),g:new m("G3"),h:new m("A3"),j:new m("B3"),k:new m("C4"),l:new m("D4"),w:new m("C#3"),e:new m("D#3"),t:new m("F#3"),y:new m("G#3"),u:new m("A#3"),o:new m("C#4"),p:new m("D#4")},St=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),w=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=St();this.id=t,this.name=o,this.state=i,this.context=e,document.addEventListener("keydown",this.onKeyTrigger(!0)),document.addEventListener("keyup",this.onKeyTrigger(!1))}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}serialize(){let{id:e,name:t,state:o}=this;return{id:e,name:t,state:o}}onKeyTrigger=e=>t=>{let o=this.extractNote(t);if(!o)return;let i=y.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(r=>{r(i)})};extractNote(e){if(!e.repeat)return vt[e.key]}};import{isNode as xt}from"es-toolkit";import{isNode as At}from"es-toolkit";var Me=class{id;name;portIndex;input;callbacks=new Set;handler=null;_state="disconnected";constructor(e,t,o){this.portIndex=e,this.id=`node-midi-${e}`,this.name=t,this.input=o}get state(){return this._state}setState(e){this._state=e}addEventListener(e){if(this.callbacks.size===0){this.handler=(t,o)=>{let i={data:new Uint8Array(o),timeStamp:performance.now()};this.callbacks.forEach(r=>{r(i)})};try{this.input.isPortOpen()||(this.input.openPort(this.portIndex),this._state="connected"),this.input.on("message",this.handler)}catch(t){console.error(`Error opening MIDI port ${this.portIndex}:`,t)}}this.callbacks.add(e)}removeEventListener(e){if(this.callbacks.delete(e),this.callbacks.size===0&&this.handler){try{this.input.off("message",this.handler),this.input.isPortOpen()&&(this.input.closePort(),this._state="disconnected")}catch(t){console.error(`Error closing MIDI port ${this.portIndex}:`,t)}this.handler=null}}},he=class{ports=new Map;MidiModule;constructor(e){this.MidiModule=e,this.scanPorts()}scanPorts(){try{let e=new this.MidiModule.Input,t=e.getPortCount();for(let o=0;o<t;o++){let i=e.getPortName(o),r=`node-midi-${o}`;if(!this.ports.has(r)){let s=new this.MidiModule.Input,p=new Me(o,i,s);this.ports.set(r,p)}}e.isPortOpen()&&e.closePort()}catch(e){console.error("Error scanning MIDI ports:",e)}}*inputs(){for(let[,e]of this.ports)yield e}addEventListener(e,t){console.warn("Hot-plug detection not supported with node-midi adapter. Restart required for new devices.")}},q=class{async requestMIDIAccess(){try{let e=await import("@julusian/midi"),t="default"in e?e.default:e;return new he(t)}catch(e){return console.error("Error loading node-midi:",e),null}}isSupported(){return At()}};var re=class{input;callbacks=new Set;handler=null;constructor(e){this.input=e}get id(){return this.input.id}get name(){return this.input.name??`Device ${this.input.id}`}get state(){return this.input.state}addEventListener(e){this.callbacks.size===0&&(this.handler=t=>{if(!t.data)return;let o={data:t.data,timeStamp:t.timeStamp};this.callbacks.forEach(i=>{i(o)})},this.input.addEventListener("midimessage",this.handler)),this.callbacks.add(e)}removeEventListener(e){this.callbacks.delete(e),this.callbacks.size===0&&this.handler&&(this.input.removeEventListener("midimessage",this.handler),this.handler=null)}},fe=class{midiAccess;portCache=new Map;constructor(e){this.midiAccess=e}*inputs(){for(let[,e]of this.midiAccess.inputs)this.portCache.has(e.id)||this.portCache.set(e.id,new re(e)),yield this.portCache.get(e.id)}addEventListener(e,t){this.midiAccess.addEventListener(e,o=>{let i=o.port;if(i?.type!=="input")return;let r=i;this.portCache.has(r.id)||this.portCache.set(r.id,new re(r)),t(this.portCache.get(r.id))})}},R=class{async requestMIDIAccess(){try{if(typeof navigator>"u"||typeof navigator.requestMIDIAccess!="function")return null;let e=await navigator.requestMIDIAccess();return new fe(e)}catch(e){return console.error("Error enabling Web MIDI API:",e),null}}isSupported(){return typeof navigator<"u"&&typeof navigator.requestMIDIAccess=="function"}};function Ee(){return xt()?new q:new R}function ne(n){let e=n.toLowerCase();e=e.replace(/\s+\d+:\d+\s*$/g,"");let t=e.split(":");return t.length>1&&(e=t.reduce((o,i)=>i.length>o.length?i:o)),e=e.replace(/\s+(midi|input|output|port)(\s+\d+)?$/gi,""),e=e.replace(/^device\s+/gi,""),e=e.replace(/\s+/g," ").trim(),e}function ye(n){let t=ne(n).split(/[\s\-_:]+/),o=new Set(["midi","input","output","port","device","in","out"]);return t.filter(i=>i.length>1&&!o.has(i))}function we(n,e){let t=n.length,o=e.length,i=[];for(let s=0;s<=t;s++)i[s]=[s];for(let s=0;s<=o;s++)i[0]&&(i[0][s]=s);for(let s=1;s<=t;s++)for(let p=1;p<=o;p++){let u=n[s-1]===e[p-1]?0:1,l=i[s-1],f=i[s],c=f?.[p-1],M=l?.[p-1],g=l?.[p];f&&g!==void 0&&c!==void 0&&M!==void 0&&(f[p]=Math.min(g+1,c+1,M+u))}return i[t]?.[o]??0}function ke(n,e){let t=ne(n),o=ne(e);if(t===o)return 1;let i=new Set(ye(n)),r=new Set(ye(e));if(i.size===0||r.size===0){let a=Math.max(t.length,o.length);return a===0?0:1-we(t,o)/a}let s=new Set([...i].filter(a=>r.has(a))),p=new Set([...i,...r]),u=s.size/p.size,l=Math.max(t.length,o.length),c=1-we(t,o)/l,M=t.includes(o)||o.includes(t)?.8:0;return u*.5+c*.3+M*.2}function ge(n,e,t=.6){let o=null;for(let i of e){let r=ke(n,i);r>=t&&(!o||r>o.score)&&(o={name:i,score:r})}return o}var k=class{devices=new Map;initialized=!1;listeners=[];context;midiAccess=null;adapter=Ee();constructor(e){this.context=e,this.addComputerKeyboard()}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.devices.get(e)}findByName(e){return Array.from(this.devices.values()).find(t=>t.name===e)}findByFuzzyName(e,t=.6){let o=Array.from(this.devices.values()),i=o.map(p=>p.name),r=ge(e,i,t);if(!r)return null;let s=o.find(p=>p.name===r.name);return s?{device:s,score:r.score}:null}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{if(!this.adapter.isSupported()){console.warn("MIDI is not supported on this platform");return}if(this.midiAccess=await this.adapter.requestMIDIAccess(),!this.midiAccess){console.error("Failed to get MIDI access");return}for(let e of this.midiAccess.inputs())this.devices.has(e.id)||this.devices.set(e.id,new A(e,this.context))}catch(e){console.error("Error enabling MIDI:",e)}}addComputerKeyboard(){if(typeof document>"u")return;let e=new w(this.context);this.devices.set(e.id,e)}listenChanges(){this.midiAccess&&this.midiAccess.addEventListener("statechange",e=>{if(e.state==="connected"){if(this.devices.has(e.id))return;let t=new A(e,this.context);this.devices.set(t.id,t),this.listeners.forEach(o=>{o(t)})}else{let t=this.devices.get(e.id);if(!t||t instanceof w)return;t.disconnect(),this.devices.delete(t.id),this.listeners.forEach(o=>{o(t)})}})}};import{assertNever as Bt}from"@blibliki/utils";var De={},Fe={},Ie=class extends d{activeNote=null;triggeredAt=0;constructor(e,t){let o={...Fe,...t.props};super(e,{...t,props:o})}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let r=o.fullName;switch(i){case"noteon":this.activeNote=r,this.triggeredAt=t;break;case"noteoff":this.activeNote=null;break;default:throw Error("This type is not a note")}}},L=class extends h{midiOutput;constructor(e,t){let o={...Fe,...t.props},i=(r,s)=>new Ie(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerOutputs()}onMidiEvent=e=>{let t;switch(e.type){case"noteon":t=this.findFreeVoice();break;case"noteoff":t=this.audioModules.find(o=>o.activeNote===e.note.fullName);break;default:throw Error("This type is not a note")}t&&(t.midiTriggered(e),e.voiceNo=t.voiceNo,this.midiOutput.onMidiEvent(e))};findFreeVoice(){let e=this.audioModules.find(t=>!t.activeNote);if(!e&&(e=this.audioModules.sort((o,i)=>o.triggeredAt-i.triggeredAt)[0],!e))throw new Error("No voices available in voice scheduler");return e}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{ConstantSourceNode as ze}from"@blibliki/utils/web-audio-api";var Ge={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},bt={value:1},B=class extends d{isStated=!1;constructor(e,t){let o={...bt,...t.props},i=r=>new ze(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("out")}onAfterSetValue=e=>{this.audioNode.offset.value=e};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new ze(this.context.audioContext,{offset:this.props.value})}),this.isStated=!1)}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,t),this.start(t)};triggerRelease=()=>{}};import{cancelAndHoldAtTime as Ve}from"@blibliki/utils";import{GainNode as Ct}from"@blibliki/utils/web-audio-api";var qe={attack:.01,decay:0,sustain:1,release:0},Re={attack:{kind:"number",min:1e-4,max:20,step:.01,exp:3,label:"Attack"},decay:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:20,step:.01,exp:3,label:"Release"}},Pe=class extends d{constructor(e,t){let o={...qe,...t.props},i=r=>{let s=new Ct(r.audioContext);return s.gain.value=0,s};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let o=this.props.attack,i=this.props.decay,r=this.props.sustain;Ve(this.audioNode.gain,t),this.audioNode.gain.value===0&&this.audioNode.gain.setValueAtTime(.001,t),this.audioNode.gain.exponentialRampToValueAtTime(1,t+o),r>0?this.audioNode.gain.exponentialRampToValueAtTime(r,t+o+i):this.audioNode.gain.exponentialRampToValueAtTime(.001,t+o+i)}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let o=this.props.release,i=Ve(this.audioNode.gain,t);i>=1e-4&&(this.audioNode.gain.setValueAtTime(i,t),this.audioNode.gain.exponentialRampToValueAtTime(1e-4,t+o-1e-4)),this.audioNode.gain.setValueAtTime(0,t+o)}},U=class extends h{constructor(e,t){let o={...qe,...t.props},i=(r,s)=>new Pe(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{BiquadFilterNode as Nt}from"@blibliki/utils/web-audio-api";import{GainNode as Ot}from"@blibliki/utils/web-audio-api";var Le={gain:{kind:"number",min:0,max:1/0,step:.01,label:"Gain"}},Be={gain:1},_=class extends d{constructor(e,t){let o={...Be,...t.props},i=r=>new Ot(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetGain=e=>{this.audioNode.gain.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"gain",getAudioNode:()=>this.audioNode.gain})}},H=class extends h{constructor(e,t){let o={...Be,...t.props},i=(r,s)=>new _(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var Ue=20,ve=2e4,_e={cutoff:ve,envelopeAmount:0,type:"lowpass",Q:1},He={cutoff:{kind:"number",min:Ue,max:ve,step:1,exp:5,label:"Cutoff"},envelopeAmount:{kind:"number",min:-1,max:1,step:.01,label:"Envelope Amount"},type:{kind:"enum",options:["lowpass","highpass","bandpass"],label:"Type"},Q:{kind:"number",min:1e-4,max:1e3,step:.1,exp:5,label:"Q"}},Te=class extends d{scale;amount;constructor(e,t){let o={..._e,...t.props},i=r=>new Nt(r.audioContext,{type:o.type,frequency:0,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=new _(e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=ue(e,{name:"scale",moduleType:"Scale",props:{min:Ue,max:ve,current:this.props.cutoff}}),this.amount.plug({audioModule:this.scale,from:"out",to:"in"}),this.scale.audioNode.connect(this.audioNode.frequency),this.registerDefaultIOs(),this.registerInputs()}onAfterSetType=e=>{this.audioNode.type=e};onAfterSetCutoff=e=>{this.scale.props={current:e}};onAfterSetQ=e=>{this.audioNode.Q.value=e};onAfterSetEnvelopeAmount=e=>{this.amount.props={gain:e}};registerInputs(){this.registerAudioInput({name:"cutoff",getAudioNode:()=>this.audioNode.frequency}),this.registerAudioInput({name:"cutoffMod",getAudioNode:()=>this.amount.audioNode}),this.registerAudioInput({name:"Q",getAudioNode:()=>this.audioNode.Q})}},$=class extends h{constructor(e,t){let o={..._e,...t.props},i=(r,s)=>new Te(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs()}registerInputs(){this.registerAudioInput({name:"cutoff"}),this.registerAudioInput({name:"cutoffMod"}),this.registerAudioInput({name:"Q"})}};import{AnalyserNode as Et}from"@blibliki/utils/web-audio-api";var $e={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},wt={fftSize:512},K=class extends d{_buffer;constructor(e,t){let o={...wt,...t.props},i=r=>new Et(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onAfterSetFftSize=e=>{this._buffer=new Float32Array(e)};get buffer(){return this._buffer?this._buffer:(this._buffer=new Float32Array(this.props.fftSize),this._buffer)}getValue(){return this.getValues()[0]??0}getValues(){return this.audioNode.getFloatTimeDomainData(this.buffer),this.buffer}};var kt={},Ke={},j=class extends d{constructor(e,t){let o={...kt,...t.props},i=r=>r.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var Se=(s=>(s.direct="direct",s.directRev="directRev",s.toggleInc="toggleInc",s.toggleDec="toggleDec",s.incDec="incDec",s.incDecRev="incDecRev",s))(Se||{}),je={pages:{kind:"array",label:"Midi mapping pages"},activePage:{kind:"number",label:"Active page",min:0,max:100,step:1},globalMappings:{kind:"array",label:"Global midi mappings"}},Dt={pages:[{name:"Page 1",mappings:[{}]}],activePage:0,globalMappings:[{}]};function Ft({value:n,midiValue:e,propSchema:t,mapping:o}){let i=t.min??0,r=t.max??1,s=t.exp??1,{threshold:p=64,mode:u}=o,l=(n-i)/(r-i),c=Math.pow(l,1/s)*127;return c=e>=p&&u==="incDec"||e<=p&&u==="incDecRev"?c+1:c-1,Math.round(Math.max(0,Math.min(127,c)))}var W=class extends d{constructor(e,t){let o={...Dt,...t.props};super(e,{...t,props:o}),this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}onSetActivePage=e=>Math.max(Math.min(e,this.props.pages.length-1),0);handleCC=(e,t)=>{this.checkAutoAssign(e);let o=this.props.pages[this.props.activePage];o&&[...this.props.globalMappings.filter(i=>i.cc===e.cc),...o.mappings.filter(i=>i.cc===e.cc)].forEach(i=>{this.forwardMapping(e,i,t)})};forwardMapping=(e,t,o)=>{if(t.moduleId===void 0||t.moduleType===void 0||t.propName===void 0)return;let i=t.propName,r=e.ccValue;if(r===void 0)return;let s=t.mode??"direct";if((s==="toggleInc"||s==="toggleDec")&&r!==127)return;let p=this.engine.findModule(t.moduleId),u=Ae[p.moduleType][i],l;switch(u.kind){case"number":{let f=p.props[i];if(s==="incDec"||s==="incDecRev"?r=Ft({value:f,propSchema:u,mapping:t,midiValue:r}):s==="directRev"&&(r=127-r),s==="toggleInc")l=f+(u.step??1);else if(s==="toggleDec")l=f-(u.step??1);else{let c=u.min??0,M=u.max??1,g=r/127,a=Math.pow(g,u.exp??1);if(l=c+a*(M-c),u.step!==void 0&&(!u.exp||u.exp===1)){let T=Math.round((l-c)/u.step);l=c+T*u.step}}break}case"enum":{let f=Math.floor(r/127*u.options.length),c=Math.min(f,u.options.length-1);l=u.options[c];break}case"boolean":l=r>=64;break;case"string":throw Error("MidiMapper not support string type of values");case"array":throw Error("MidiMapper not support array type of values");default:throw Error("MidiMapper unknown type")}p.props={[i]:l},p.triggerPropsUpdate()};checkAutoAssign(e){if(e.cc===void 0)return;let t=this.props.pages[this.props.activePage];if(!t)return;let o=this.props.globalMappings.some(({autoAssign:u})=>u),i=t.mappings.some(({autoAssign:u})=>u);if(!o&&!i)return;let r=o?this.props.globalMappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):this.props.globalMappings,s=i?t.mappings.map(u=>u.autoAssign?{...u,cc:e.cc,autoAssign:!1}:u):t.mappings,p=this.props.pages.map((u,l)=>l===this.props.activePage?{...u,mappings:s}:u);this.props={pages:p,globalMappings:r},this.triggerPropsUpdate()}};var We={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},zt={selectedId:void 0,selectedName:void 0},Q=class extends d{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...zt,...t.props};super(e,{...t,props:o});let i=this.props.selectedId&&this.engine.findMidiDevice(this.props.selectedId);if(!i&&this.props.selectedName&&(i=this.engine.findMidiDeviceByName(this.props.selectedName),!i)){let r=this.engine.findMidiDeviceByFuzzyName(this.props.selectedName,.6);r&&(i=r.device,console.log(`MIDI device fuzzy matched: "${this.props.selectedName}" -> "${i.name}" (confidence: ${Math.round(r.score*100)}%)`))}i&&this.addEventListener(i),this.registerOutputs()}onSetSelectedId=e=>{if(this.removeEventListener(),!e)return e;let t=this.engine.findMidiDevice(e);return t&&(this.props.selectedName!==t.name&&(this.props={selectedName:t.name},this.triggerPropsUpdate()),this.addEventListener(t)),e};get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){e.addEventListener(this.forwardMidiEvent)}removeEventListener(){if(!this.props.selectedId)return;this.engine.findMidiDevice(this.props.selectedId)?.removeEventListener(this.forwardMidiEvent)}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{dbToGain as Qe}from"@blibliki/utils";import{GainNode as Ye,OscillatorNode as Je}from"@blibliki/utils/web-audio-api";var xe=-18,pe=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(pe||{}),Xe={wave:{kind:"enum",options:Object.values(pe),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-1,max:2,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${xe}db Gain`}},Ze={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},be=class extends d{isStated=!1;outputGain;detuneGain;constructor(e,t){let o={...Ze,...t.props},i=r=>new Je(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.outputGain=new Ye(this.context.audioContext,{gain:Qe(xe)}),this.applyOutputGain(),this.initializeGainDetune(),this.registerInputs(),this.registerOutputs()}onAfterSetWave=e=>{this.audioNode.type=e};onAfterSetFrequency=()=>{this.updateFrequency()};onAfterSetFine=()=>{this.updateFrequency()};onAfterSetCoarse=()=>{this.updateFrequency()};onAfterSetOctave=()=>{this.updateFrequency()};onAfterSetLowGain=e=>{this.outputGain.gain.value=e?Qe(xe):1};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new Je(this.context.audioContext,{type:this.props.wave,frequency:this.finalFrequency}),this.applyOutputGain(),this.detuneGain.connect(this.audioNode.detune)}),this.isStated=!1)}triggerAttack=(e,t)=>{super.triggerAttack(e,t),this.props={frequency:e.frequency},this.updateFrequency(t),this.start(t)};triggerRelease(e,t){super.triggerRelease(e,t);let o=this.activeNotes.length?this.activeNotes[this.activeNotes.length-1]:null;o&&(this.props={frequency:o.frequency},this.updateFrequency(t))}get finalFrequency(){let{frequency:e,coarse:t,octave:o,fine:i}=this.props;return e*Math.pow(2,t/12+o+i/12)}updateFrequency(e){this.finalFrequency!==void 0&&(e?this.audioNode.frequency.setValueAtTime(this.finalFrequency,e):this.audioNode.frequency.value=this.finalFrequency)}applyOutputGain(){this.audioNode.connect(this.outputGain)}initializeGainDetune(){this.detuneGain=new Ye(this.context.audioContext,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputGain})}},Y=class extends h{constructor(e,t){let o={...Ze,...t.props},i=(r,s)=>new be(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerDefaultIOs("out")}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}registerInputs(){this.registerAudioInput({name:"detune"})}};import{assertNever as Gt}from"@blibliki/utils";var et=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{s0;s1;constructor(){super(),this.s0=0,this.s1=0}static get parameterDescriptors(){return[{name:"cutoff",defaultValue:1e3,minValue:20,maxValue:2e4},{name:"resonance",defaultValue:0,minValue:0,maxValue:4}]}process(t,o,i){let r=t[0],s=o[0];if(!r||!s)return!0;let p=i.cutoff,u=i.resonance;if(!p||!u)return!0;for(let l=0;l<r.length;l++){let f=r[l],c=s[l];if(!(!f||!c))for(let M=0;M<f.length;M++){let g=f[M];if(g===void 0)continue;let a=p.length>1?p[M]??p[0]:p[0];if(a===void 0)continue;let T=Math.max(20,Math.min(2e4,a)),te=Math.log(T/20)/Math.log(2e4/20),x=Math.pow(.5,(1-te)/.125),v=u.length>1?u[M]??u[0]:u[0];if(v===void 0)continue;let Oe=1-Math.pow(.5,(v+.125)/.125)*x;this.s0=Oe*this.s0-x*this.s1+x*g,this.s1=Oe*this.s1+x*this.s0,c[M]=this.s1}}return!0}}registerProcessor("filter-processor",n)}).toString(),")()"],{type:"application/javascript"}));var tt=URL.createObjectURL(new Blob(["(",(()=>{class n extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5}]}process(t,o,i){let r=t[0],s=o[0];if(!r||!s)return!0;let p=i.min,u=i.max,l=i.current;if(!p||!u||!l)return!0;let f=r[0];if(!f||f.length===0){for(let c of s){let M=(l.length>1,l[0]??.5);c.fill(M)}return!0}for(let c=0;c<r.length;c++){let M=r[c],g=s[c];if(!(!M||!g))for(let a=0;a<M.length;a++){let T=M[a];if(T===void 0)continue;let te=p.length>1?p[a]??p[0]:p[0],x=u.length>1?u[a]??u[0]:u[0],v=l.length>1?l[a]??l[0]:l[0];te===void 0||x===void 0||v===void 0||(T<0?g[a]=v*Math.pow(te/v,-T):g[a]=v*Math.pow(x/v,T))}}return!0}}registerProcessor("scale-processor",n)}).toString(),")()"],{type:"application/javascript"}));async function ot(n){await n.addModule(tt),await n.addModule(et)}function it(n,e){switch(e){case"ScaleProcessor":return n.newAudioWorklet("scale-processor");case"FilterProcessor":return n.newAudioWorklet("filter-processor");default:Gt(e)}}var rt={min:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Min"},max:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Max"},current:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Current"}},Vt={min:0,max:1,current:.5},J=class extends d{constructor(e,t){let o={...Vt,...t.props},i=r=>it(r,"ScaleProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}get current(){return this.audioNode.parameters.get("current")}get min(){return this.audioNode.parameters.get("min")}get max(){return this.audioNode.parameters.get("max")}onAfterSetMin=e=>{this.min.value=e};onAfterSetMax=e=>{this.max.value=e};onAfterSetCurrent=e=>{this.current.value=e}};var nt={steps:{kind:"number",min:1,max:16,step:1,label:"Steps"},bars:{kind:"number",min:1,max:16,step:1,label:"Steps"}},qt={sequences:[],steps:16,bars:1},X=class extends d{midiOutput;constructor(e,t){let o={...qt,...t.props};super(e,{...t,props:o})}};import{StereoPannerNode as Rt}from"@blibliki/utils/web-audio-api";var st={pan:{kind:"number",min:-1,max:1,step:.01,label:"Pan"}},ut={pan:0},Ce=class extends d{constructor(e,t){let o={...ut,...t.props},i=r=>new Rt(r.audioContext);super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs(),this.registerAdditionalInputs()}onAfterSetPan=e=>{this.audioNode.pan.value=e};registerAdditionalInputs(){this.registerAudioInput({name:"pan",getAudioNode:()=>this.audioNode.pan})}},Z=class extends h{constructor(e,t){let o={...ut,...t.props},i=(r,s)=>new Ce(r,s);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"pan"})}};var pt={activeNotes:{kind:"array",label:"Active notes"}},Lt={activeNotes:[]},ee=class extends d{midiOutput;constructor(e,t){let o={...Lt,...t.props};super(e,{...t,props:o}),this.registerInputs(),this.registerOutputs()}sendMidi(e){this.midiOutput.onMidiEvent(e)}triggerAttack=(e,t)=>{this.props={activeNotes:[...this.props.activeNotes,e.fullName]},this.triggerPropsUpdate(),this.sendMidi(y.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(y.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var se=(a=>(a.Master="Master",a.Oscillator="Oscillator",a.Gain="Gain",a.MidiSelector="MidiSelector",a.Envelope="Envelope",a.Filter="Filter",a.Scale="Scale",a.StereoPanner="StereoPanner",a.Inspector="Inspector",a.Constant="Constant",a.MidiMapper="MidiMapper",a.VirtualMidi="VirtualMidi",a.StepSequencer="StepSequencer",a.VoiceScheduler="VoiceScheduler",a))(se||{}),Ae={Oscillator:Xe,Gain:Le,Master:Ke,MidiSelector:We,Envelope:Re,Filter:He,Scale:rt,StereoPanner:st,Inspector:$e,Constant:Ge,MidiMapper:je,VirtualMidi:pt,StepSequencer:nt,VoiceScheduler:De};function ue(n,e){switch(e.moduleType){case"Oscillator":return new Y(n,e);case"Gain":return new H(n,e);case"Master":return new j(n,e);case"MidiSelector":return new Q(n,e);case"Envelope":return new U(n,e);case"Filter":return new $(n,e);case"Scale":return new J(n,e);case"StereoPanner":return new Z(n,e);case"Inspector":return new K(n,e);case"Constant":return new B(n,e);case"MidiMapper":return new W(n,e);case"VirtualMidi":return new ee(n,e);case"StepSequencer":return new X(n,e);case"VoiceScheduler":return new L(n,e);default:Bt(e)}}var O=class n{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=n._engines.get(e);return dt(t),t}static get current(){return dt(this._currentId),this.getById(this._currentId)}static async load(e){let{bpm:t,timeSignature:o,modules:i,routes:r}=e,s=new _t,p=new n(s);return await p.initialize(),p.timeSignature=o,p.bpm=t,i.forEach(u=>{p.addModule(u)}),r.forEach(u=>{p.addRoute(u)}),p}constructor(e){this.id=$t(),this.context=e,this.transport=new Ut(this.context,{generator:(t,o)=>[],consumer:t=>{},onJump:t=>{},onStart:this.onStart,onStop:this.onStop,silence:t=>{}}),this.routes=new V(this),this.modules=new Map,this.midiDeviceManager=new k(this.context),n._engines.set(this.id,this),n._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await ot(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=ue(this.id,e);return this.modules.set(t.id,t),t.serialize()}updateModule(e){let t=this.findModule(e.id);if(t.moduleType!==e.moduleType)throw Error(`The module id ${e.id} isn't moduleType ${e.moduleType}`);let o=Ht(e.changes,["name","props"]);return Object.assign(t,o),t instanceof h&&e.changes.voices!==void 0&&(t.voices=e.changes.voices),t.serialize()}removeModule(e){this.modules.delete(e)}addRoute(e){return this.routes.addRoute(e)}removeRoute(e){this.routes.removeRoute(e)}validRoute(e){let{source:t,destination:o}=e,i=this.findIO(t.moduleId,t.ioName,"output"),r=this.findIO(o.moduleId,o.ioName,"input");return i.isMidi()&&r.isMidi()||i.isAudio()&&r.isAudio()}async start(){await this.resume(),this.transport.start()}stop(){this.transport.stop(),this.transport.reset()}pause(){this.transport.stop()}get bpm(){return this.transport.bpm}set bpm(e){this.transport.bpm=e}get timeSignature(){return this.transport.timeSignature}set timeSignature(e){this.transport.timeSignature=e}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear()}serialize(){return{bpm:this.bpm,timeSignature:this.timeSignature,modules:Array.from(this.modules.values()).map(e=>e.serialize()),routes:this.routes.serialize()}}findModule(e){let t=this.modules.get(e);if(!t)throw Error(`The module with id ${e} is not exists`);return t}findIO(e,t,o){return this.findModule(e)[`${o}s`].findByName(t)}findMidiDevice(e){return this.midiDeviceManager.find(e)}findMidiDeviceByName(e){return this.midiDeviceManager.findByName(e)}findMidiDeviceByFuzzyName(e,t){return this.midiDeviceManager.findByFuzzyName(e,t)}onPropsUpdate(e){this.propsUpdateCallbacks.push(e)}_triggerPropsUpdate(e){this.propsUpdateCallbacks.forEach(t=>{t(e)})}triggerVirtualMidi(e,t,o){let i=this.findModule(e);if(i.moduleType!=="VirtualMidi")throw Error("This is not a virtual mid");i.sendMidi(y.fromNote(t,o==="noteOn",this.context.currentTime))}onStart=e=>{this.modules.forEach(t=>{t.start(e)})};onStop=e=>{this.modules.forEach(t=>{t.stop(e)})}};import{TransportState as Bs}from"@blibliki/transport";import{Context as _s}from"@blibliki/utils";export{_s as Context,O as Engine,A as MidiDevice,Se as MidiMappingMode,ie as MidiPortState,se as ModuleType,m as Note,pe as OscillatorWave,Bs as TransportState,Ae as moduleSchemas};
|
|
1
|
+
import{Transport as ai}from"@blibliki/transport";import{assertDefined as Zt,Context as pi,pick as di,uuidv4 as li}from"@blibliki/utils";import{upperFirst as it,uuidv4 as ao,requestAnimationFrame as po}from"@blibliki/utils";import{assertNever as ro}from"@blibliki/utils";import{sortBy as no}from"es-toolkit";import{deterministicId as eo,uuidv4 as to,requestAnimationFrame as oo}from"@blibliki/utils";var y=class{id;engineId;moduleType;audioModules;inputs;outputs;monoModuleConstructor;_props;_voices;_name;pendingUIUpdates=!1;static create(e,t,o){let i=new e(t,{...o});return i.props={...i.props},i}constructor(e,t){let{id:o,name:i,moduleType:r,voices:n,monoModuleConstructor:u,props:a}=t;this.audioModules=[],this.monoModuleConstructor=u,this.id=o??to(),this.engineId=e,this.name=i,this.moduleType=r,this._props=a,this.inputs=new E(this),this.outputs=new w(this),queueMicrotask(()=>{this.voices=n||1,this.props=a,this.triggerPropsUpdate()})}get name(){return this._name}set name(e){this._name=e,this.audioModules.forEach(t=>t.name=e)}get props(){return this._props}set props(e){this._props={...this._props,...e},this.audioModules.forEach(t=>t.props=e)}get voices(){return this._voices}set voices(e){this._voices=e,this.adjustNumberOfModules(),this.rePlugAll()}start(e){this.audioModules.forEach(t=>{t.start(e)})}stop(e){this.audioModules.forEach(t=>{t.stop(e)})}serialize(){return{id:this.id,name:this.name,moduleType:this.moduleType,voices:this.voices,props:this.props,inputs:this.inputs.serialize(),outputs:this.outputs.serialize()}}plug({audioModule:e,from:t,to:o}){let i=this.outputs.findByName(t),r=e.inputs.findByName(o);i.plug(r)}rePlugAll(e){this.inputs.rePlugAll(e),this.outputs.rePlugAll(e)}unPlugAll(){this.inputs.unPlugAll(),this.outputs.unPlugAll()}dispose(){this.inputs.unPlugAll(),this.outputs.unPlugAll(),this.audioModules.forEach(e=>{e.dispose()})}onMidiEvent=e=>{if(e.cc){this.audioModules.forEach(i=>{i.onMidiEvent(e)});return}let t=e.voiceNo??0;this.findVoice(t).onMidiEvent(e)};triggerPropsUpdate=()=>{this.pendingUIUpdates||(this.pendingUIUpdates=!0,this.sheduleTriggerUpdate())};sheduleTriggerUpdate(){oo(()=>{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=eo(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 R.getById(this.engineId)}get context(){return this.engine.context}};import{AudioParam as ot}from"@blibliki/utils/web-audio-api";import{deterministicId as io}from"@blibliki/utils";var we=class{id;ioType;name;module;connections;constructor(e,t){this.module=e,this.name=t.name,this.ioType=t.ioType,this.id=io(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}}},x=class extends we{plug(e,t){super.plug(e,t)}unPlug(e,t){super.unPlug(e,t)}};var A=class extends x{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof F)&&Ie(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof F)&&Ie(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).inputs.findByName(this.name)}},F=class extends x{plug(e,t=!0){super.plug(e,t),!(!t&&e instanceof A)&&Ie(this,e,!0)}unPlug(e,t=!0){super.unPlug(e,t),!(!t&&e instanceof A)&&Ie(this,e,!1)}findIOByVoice(e){return this.module.findVoice(e).outputs.findByName(this.name)}};function Ie(s,e,t){if(e instanceof A||e instanceof F){let o=Math.max(s.module.voices,e.module.voices);for(let i=0;i<o;i++){let r=s.findIOByVoice(i%s.module.voices),n=e.findIOByVoice(i%e.module.voices);t?r.plug(n):r.unPlug(n)}}else for(let o=0;o<s.module.voices;o++){let i=s.findIOByVoice(o);t?i.plug(e):i.unPlug(e)}}var de=class extends x{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}},le=class extends x{getAudioNode;constructor(e,t){super(e,t),this.getAudioNode=t.getAudioNode}plug(e,t=!0){if(super.plug(e,t),e instanceof A)return;let o=e.getAudioNode();o instanceof ot?this.getAudioNode().connect(o):this.getAudioNode().connect(o)}unPlug(e,t=!0){if(super.unPlug(e,t),e instanceof A)return;let o=e.getAudioNode();try{o instanceof ot?this.getAudioNode().disconnect(o):this.getAudioNode().disconnect(o)}catch{}}};var L=class extends x{onMidiEvent;constructor(e,t){super(e,t),this.onMidiEvent=t.onMidiEvent}},ce=class extends x{onMidiEvent=e=>{this.midiConnections.forEach(t=>{t.onMidiEvent(e)})};get midiConnections(){return this.connections.filter(e=>e instanceof L)}};var me=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 y)throw Error("Not compatible");t=new de(this.module,e);break;case"audioOutput":if(this.module instanceof y)throw Error("Not compatible");t=new le(this.module,e);break;case"polyAudioInput":if(this.module instanceof d)throw Error("Not compatible");t=new A(this.module,e);break;case"polyAudioOutput":if(this.module instanceof d)throw Error("Not compatible");t=new F(this.module,e);break;case"midiInput":t=new L(this.module,e);break;case"midiOutput":t=new ce(this.module,e);break;default:ro(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 no(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`)}},E=class extends me{constructor(e){super("Input",e)}},w=class extends me{constructor(e){super("Output",e)}};var so=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]]),Re=so;var Fe=["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"],uo=2,M=class s{static _notes;name;octave;velocity=1;duration;static fromFrequency(e){let t;for(let[o,i]of Re)if(i===e){t=o;break}if(!t)throw Error("Not matching frequency with a note");return new s(t)}static fromEvent(e){let t=e.data[1];if(t===void 0)throw new Error("Invalid MIDI message: missing data byte");let o=Fe[t%12];if(!o)throw new Error(`Invalid MIDI note number: ${t}`);let i=Math.floor(t/12)-2;return new s(`${o}${i}`)}static notes(e=3){return Fe.map(t=>new s(`${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 Re.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+uo)*12+this.noteIndex}get noteIndex(){return Fe.indexOf(this.name)}valueOf(){return this.fullName}serialize(){return{name:this.name,octave:this.octave,frequency:this.frequency,velocity:this.velocity,duration:this.duration}}fromString(e){let t=/(\w#?)(\d)?/.exec(e);if(!t)throw new Error(`Invalid note string: ${e}`);let o=t[1];if(!o)throw new Error(`Invalid note name in: ${e}`);this.name=o,this.octave=t[2]?parseInt(t[2]):1}fromProps(e){Object.assign(this,e)}};var O=class{data;constructor(e){this.data=e}get dataBytes(){return Array.from(this.data.slice(1))}get type(){let e=this.data[0];if(e===void 0)return"unknown";switch(e&240){case 144:return this.data[2]===0?"noteoff":"noteon";case 128:return"noteoff";case 176:return"controlchange";case 224:return"pitchbend";case 208:return"channelaftertouch";case 160:return"keyaftertouch";case 192:return"programchange";default:return"unknown"}}};var v=class s{note;voiceNo;triggeredAt;message;static fromNote(e,t=!0,o){let i=e instanceof M?e:new M(e);return new s(new O(i.midiData(t)),o)}static fromCC(e,t,o){return new s(new O(new Uint8Array([176,e,t])),o)}constructor(e,t){this.message=e,this.triggeredAt=t,this.defineNotes()}get type(){return this.message.type}get isNote(){return this.type==="noteon"||this.type==="noteoff"}get isCC(){return this.type==="controlchange"}get cc(){if(this.isCC)return this.message.dataBytes[0]}get ccValue(){if(this.isCC)return this.message.dataBytes[1]}defineNotes(){this.isNote&&(this.note||(this.note=M.fromEvent(this.message)))}get rawMessage(){return this.message}clone(e){let t=new s(this.message,this.triggeredAt);return t.voiceNo=e,t}};var d=class{id;engineId;name;moduleType;voiceNo;audioNode;inputs;outputs;_props;_state;activeNotes;_propsInitialized=!1;pendingUIUpdates=!1;static create(e,t,o){let i=new e(t,{...o});return i.props={...i.props},i._propsInitialized=!0,i}constructor(e,t){let{id:o,name:i,moduleType:r,voiceNo:n,audioNodeConstructor:u,props:a}=t;this.id=o??ao(),this.engineId=e,this.name=i,this.moduleType=r,this.voiceNo=n??0,this.activeNotes=[],this.audioNode=u?.(this.context),this._props=a,this.inputs=new E(this),this.outputs=new w(this)}get props(){return this._props}set props(e){let t={},o=!this._propsInitialized;Object.keys(e).forEach(i=>{let r=e[i];if(r!==void 0&&(o||this._props[i]!==r)){let n=this.callPropHook("onSet",i,r);t[i]=n??r}}),Object.keys(t).length!==0&&(this._props={...this._props,...t},Object.keys(t).forEach(i=>{let r=t[i];r!==void 0&&this.callPropHook("onAfterSet",i,r)}))}callPropHook(e,t,o){let i=`${e}${it(t)}`,r=this[i];if(typeof r=="function")return r.call(this,o)}get state(){return this._state}set state(e){let t={};Object.keys(e).forEach(o=>{let i=e[o];if(i!==void 0&&this._state[o]!==i){let r=this.callStateHook("onSetState",o,i);t[o]=r??i}}),Object.keys(t).length!==0&&(this._state={...this._state,...t},Object.keys(t).forEach(o=>{let i=t[o];i!==void 0&&this.callStateHook("onAfterSetState",o,i)}))}callStateHook(e,t,o){let i=`${e}${it(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(){po(()=>{let e={id:this.id,moduleType:this.moduleType,voiceNo:this.voiceNo,name:this.name,props:this.props,state:this._state};this.engine._triggerPropsUpdate(e),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 R.getById(this.engineId)}get context(){return this.engine.context}};import{uuidv4 as lo}from"@blibliki/utils";var he=class{engine;routes;constructor(e){this.engine=e,this.routes=new Map}addRoute(e){let t=e.id??lo(),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"),n=this.engine.findIO(i.moduleId,i.ioName,"input");return{sourceIO:r,destinationIO:n}}};var ve=(t=>(t.connected="connected",t.disconnected="disconnected",t))(ve||{}),k=class{midiPort;constructor(e){this.midiPort=e,this.connect()}get id(){return this.midiPort.id}get name(){return this.midiPort.name}get type(){return this.midiPort.type}get state(){return this.midiPort.state}serialize(){return{id:this.id,name:this.name,type:this.type,state:this.state}}};var co={a:new M("C3"),s:new M("D3"),d:new M("E3"),f:new M("F3"),g:new M("G3"),h:new M("A3"),j:new M("B3"),k:new M("C4"),l:new M("D4"),w:new M("C#3"),e:new M("D#3"),t:new M("F#3"),y:new M("G#3"),u:new M("A#3"),o:new M("C#4"),p:new M("D#4")},mo=()=>({id:"computer_keyboard",name:"Computer Keyboard",state:"connected"}),q=class{id;name;state;eventListerCallbacks=[];context;constructor(e){let{id:t,name:o,state:i}=mo();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=v.fromNote(o,e,this.context.browserToContextTime(t.timeStamp));this.eventListerCallbacks.forEach(r=>{r(i)})};extractNote(e){if(!e.repeat)return co[e.key]}};var C=class extends k{eventListerCallbacks=[];context;messageHandler=null;constructor(e,t){super(e),this.context=t}connect(){this.messageHandler=e=>{this.processEvent(e)},this.midiPort.addEventListener(this.messageHandler)}disconnect(){this.messageHandler&&(this.midiPort.removeEventListener(this.messageHandler),this.messageHandler=null)}addEventListener(e){this.eventListerCallbacks.push(e)}removeEventListener(e){this.eventListerCallbacks=this.eventListerCallbacks.filter(t=>t!==e)}processEvent(e){let t=new O(e.data),o=new v(t,this.context.browserToContextTime(e.timeStamp));switch(o.type){case"noteon":case"noteoff":case"controlchange":this.eventListerCallbacks.forEach(i=>{i(o)})}}};var D=class extends k{constructor(e){super(e),this.connect()}connect(){}disconnect(){}send(e,t){this.midiPort.send(e,t)}};import{isNode as fo}from"es-toolkit";import{isNode as ho}from"es-toolkit";var Le=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}get type(){return"input"}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}}},qe=class{id;name;portIndex;output;_state="disconnected";isOpen=!1;constructor(e,t,o){this.portIndex=e,this.id=`node-midi-out-${e}`,this.name=t,this.output=o}get state(){return this._state}get type(){return"output"}ensureOpen(){if(!this.isOpen)try{this.output.openPort(this.portIndex),this.isOpen=!0,this._state="connected"}catch(e){console.error(`Error opening MIDI output port ${this.portIndex}:`,e)}}send(e,t){this.ensureOpen();try{let o=Array.isArray(e)?e:Array.from(e);this.output.sendMessage(o)}catch(o){console.error("Error sending MIDI message:",o)}}},ze=class{inputPorts=new Map;outputPorts=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.inputPorts.has(r)){let n=new this.MidiModule.Input,u=new Le(o,i,n);this.inputPorts.set(r,u)}}e.isPortOpen()&&e.closePort()}catch(e){console.error("Error scanning MIDI input ports:",e)}try{let e=new this.MidiModule.Output,t=e.getPortCount();for(let o=0;o<t;o++){let i=e.getPortName(o),r=`node-midi-out-${o}`;if(!this.outputPorts.has(r)){let n=new this.MidiModule.Output,u=new qe(o,i,n);this.outputPorts.set(r,u)}}e.isPortOpen()&&e.closePort()}catch(e){console.error("Error scanning MIDI output ports:",e)}}*inputs(){for(let[,e]of this.inputPorts)yield e}*outputs(){for(let[,e]of this.outputPorts)yield e}addEventListener(e,t){console.warn("Hot-plug detection not supported with node-midi adapter. Restart required for new devices.")}},fe=class{async requestMIDIAccess(){try{let e=await import("@julusian/midi"),t="default"in e?e.default:e;return new ze(t)}catch(e){return console.error("Error loading node-midi:",e),null}}isSupported(){return ho()}};var Pe=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 type(){return this.input.type}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)}},Te=class{output;constructor(e){this.output=e}get id(){return this.output.id}get name(){return this.output.name??`Device ${this.output.id}`}get type(){return this.output.type}get state(){return this.output.state}send(e,t){this.output.send(e,t)}},Ve=class{midiAccess;inputCache=new Map;outputCache=new Map;constructor(e){this.midiAccess=e}*inputs(){for(let[,e]of this.midiAccess.inputs)this.inputCache.has(e.id)||this.inputCache.set(e.id,new Pe(e)),yield this.inputCache.get(e.id)}*outputs(){for(let[,e]of this.midiAccess.outputs)this.outputCache.has(e.id)||this.outputCache.set(e.id,new Te(e)),yield this.outputCache.get(e.id)}addEventListener(e,t){this.midiAccess.addEventListener(e,o=>{let i=o.port;if(!i)return;let r={id:i.id,name:i.name??`Device ${i.id}`,state:i.state,type:i.type};if(i.type==="input"){let n=i;this.inputCache.has(n.id)||this.inputCache.set(n.id,new Pe(n))}else{let n=i;this.outputCache.has(n.id)||this.outputCache.set(n.id,new Te(n))}t(r)})}},Me=class{async requestMIDIAccess(){try{if(typeof navigator>"u"||typeof navigator.requestMIDIAccess!="function")return null;let e=await navigator.requestMIDIAccess();return new Ve(e)}catch(e){return console.error("Error enabling Web MIDI API:",e),null}}isSupported(){return typeof navigator<"u"&&typeof navigator.requestMIDIAccess=="function"}};function rt(){return fo()?new fe:new Me}function Se(s){let e=s.toLowerCase();e=e.replace(/\s+\d+:\d+\s*$/g,"");let t=e.split(":");return t.length>1&&(e=t.reduce((o,i)=>i.length>o.length?i:o)),e=e.replace(/\s+(midi|input|output|port)(\s+\d+)?$/gi,""),e=e.replace(/^device\s+/gi,""),e=e.replace(/\s+/g," ").trim(),e}function Be(s){let t=Se(s).split(/[\s\-_:]+/),o=new Set(["midi","input","output","port","device","in","out"]);return t.filter(i=>i.length>1&&!o.has(i))}function nt(s,e){let t=s.length,o=e.length,i=[];for(let n=0;n<=t;n++)i[n]=[n];for(let n=0;n<=o;n++)i[0]&&(i[0][n]=n);for(let n=1;n<=t;n++)for(let u=1;u<=o;u++){let a=s[n-1]===e[u-1]?0:1,p=i[n-1],l=i[n],h=l?.[u-1],m=p?.[u-1],g=p?.[u];l&&g!==void 0&&h!==void 0&&m!==void 0&&(l[u]=Math.min(g+1,h+1,m+a))}return i[t]?.[o]??0}function st(s,e){let t=Se(s),o=Se(e);if(t===o)return 1;let i=new Set(Be(s)),r=new Set(Be(e));if(i.size===0||r.size===0){let f=Math.max(t.length,o.length);return f===0?0:1-nt(t,o)/f}let n=new Set([...i].filter(f=>r.has(f))),u=new Set([...i,...r]),a=n.size/u.size,p=Math.max(t.length,o.length),h=1-nt(t,o)/p,m=t.includes(o)||o.includes(t)?.8:0;return a*.5+h*.3+m*.2}function xe(s,e,t=.6){let o=null;for(let i of e){let r=st(s,i);r>=t&&(!o||r>o.score)&&(o={name:i,score:r})}return o}var z=class{inputDevices=new Map;outputDevices=new Map;initialized=!1;listeners=[];context;midiAccess=null;adapter=rt();constructor(e){this.context=e,this.addComputerKeyboard()}async initialize(){await this.initializeDevices(),this.listenChanges(),this.initialized=!0}find(e){return this.findInput(e)??this.findOutput(e)}findByName(e){return this.findInputByName(e)??this.findOutputByName(e)}findByFuzzyName(e,t=.6){let o=this.findInputByFuzzyName(e,t),i=this.findOutputByFuzzyName(e,t);return o?i?o.score>i.score?o.device:i.device:o.device:i?.device}findInput(e){return this.inputDevices.get(e)}findInputByName(e){return Array.from(this.inputDevices.values()).find(t=>t.name===e)}findOutput(e){return this.outputDevices.get(e)}findOutputByName(e){return Array.from(this.outputDevices.values()).find(t=>t.name===e)}findInputByFuzzyName(e,t=.6){let o=Array.from(this.inputDevices.values()),i=o.map(u=>u.name),r=xe(e,i,t);if(!r)return null;let n=o.find(u=>u.name===r.name);return n?{device:n,score:r.score}:null}findOutputByFuzzyName(e,t=.6){let o=Array.from(this.outputDevices.values()),i=o.map(u=>u.name),r=xe(e,i,t);if(!r)return null;let n=o.find(u=>u.name===r.name);return n?{device:n,score:r.score}:null}addListener(e){this.listeners.push(e)}async initializeDevices(){if(!this.initialized)try{if(!this.adapter.isSupported()){console.warn("MIDI is not supported on this platform");return}if(this.midiAccess=await this.adapter.requestMIDIAccess(),!this.midiAccess){console.error("Failed to get MIDI access");return}for(let e of this.midiAccess.inputs())this.inputDevices.has(e.id)||this.inputDevices.set(e.id,new C(e,this.context));for(let e of this.midiAccess.outputs())this.outputDevices.has(e.id)||this.outputDevices.set(e.id,new D(e))}catch(e){console.error("Error enabling MIDI:",e)}}addComputerKeyboard(){if(typeof document>"u")return;let e=new q(this.context);this.inputDevices.set(e.id,e)}listenChanges(){this.midiAccess&&this.midiAccess.addEventListener("statechange",e=>{if(e.state==="connected")if(e.type==="input"){if(this.inputDevices.has(e.id))return;for(let t of this.midiAccess.inputs())if(t.id===e.id){let o=new C(t,this.context);this.inputDevices.set(o.id,o),this.listeners.forEach(i=>{i(o)});break}}else{if(this.outputDevices.has(e.id))return;for(let t of this.midiAccess.outputs())if(t.id===e.id){let o=new D(t);this.outputDevices.set(o.id,o);break}}else if(e.type==="input"){let t=this.inputDevices.get(e.id);if(!t||t instanceof q)return;t.disconnect(),this.inputDevices.delete(t.id),this.listeners.forEach(o=>{o(t)})}else{let t=this.outputDevices.get(e.id);if(!t)return;t.disconnect(),this.outputDevices.delete(t.id)}})}};import{assertNever as ui}from"@blibliki/utils";var ut={},at={},_e=class extends d{activeNote=null;triggeredAt=0;occupationRanges=[];constructor(e,t){let o={...at,...t.props};super(e,{...t,props:o})}isOccupiedAt(e){return this.occupationRanges.some(t=>e>=t.startTime&&e<t.endTime)}getEarliestEndTimeAfter(e){let t=this.occupationRanges.filter(o=>o.endTime>e);return t.length===0?-1/0:Math.min(...t.map(o=>o.endTime))}cleanupPastRanges(e){this.occupationRanges=this.occupationRanges.filter(t=>t.endTime>e||t.endTime===1/0)}clearOccupationRanges(){this.occupationRanges=[]}midiTriggered=e=>{let{triggeredAt:t,note:o,type:i}=e;if(!o)return;let r=o.fullName;this.cleanupPastRanges(t);let n=this.context.audioContext.currentTime,u=t-n>.01;switch(i){case"noteon":{this.activeNote=r,this.triggeredAt=t,u&&this.occupationRanges.push({noteName:r,startTime:t,endTime:1/0});break}case"noteoff":{this.activeNote=null;let a=this.occupationRanges.find(p=>p.noteName===r&&p.endTime===1/0);a&&(a.endTime=t);break}default:throw Error("This type is not a note")}}},V=class extends y{midiOutput;constructor(e,t){let o={...at,...t.props},i=(r,n)=>new _e(r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerInputs(),this.registerOutputs()}onMidiEvent=e=>{let t;switch(e.type){case"noteon":t=this.findFreeVoice(e.triggeredAt);break;case"noteoff":t=this.audioModules.find(o=>o.activeNote===e.note.fullName);break;case"controlchange":this.midiOutput.onMidiEvent(e);return;default:throw Error("This type is not a note")}t&&(t.midiTriggered(e),e.voiceNo=t.voiceNo,this.midiOutput.onMidiEvent(e))};findFreeVoice(e){let t=this.context.audioContext.currentTime,o=Math.abs(e-t)<.01,i;if(o?i=this.audioModules.find(r=>!r.activeNote):i=this.audioModules.find(r=>!r.isOccupiedAt(e)),!i){if(o?i=this.audioModules.sort((n,u)=>n.triggeredAt-u.triggeredAt)[0]:i=this.audioModules.sort((n,u)=>{let a=n.getEarliestEndTimeAfter(e),p=u.getEarliestEndTimeAfter(e);return a!==p?a-p:n.triggeredAt-u.triggeredAt})[0],!i)throw new Error("No voices available in voice scheduler");if(i.activeNote){let r=i.activeNote,n=this.context.audioContext.currentTime,u=v.fromNote(r,!1,n);u.voiceNo=i.voiceNo,i.midiTriggered(u),this.midiOutput.onMidiEvent(u)}}return i}clearAllOccupationRanges(){this.audioModules.forEach(e=>{e.clearOccupationRanges()})}registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};import{GainNode as Mo}from"@blibliki/utils/web-audio-api";var b=class{dryGainNode;wetGainNode;outputNode;constructor(e){let t=e.audioContext;this.dryGainNode=t.createGain(),this.wetGainNode=t.createGain(),this.outputNode=t.createGain(),this.dryGainNode.gain.value=1,this.wetGainNode.gain.value=0,this.outputNode.gain.value=1,this.dryGainNode.connect(this.outputNode),this.wetGainNode.connect(this.outputNode)}connectInput(e){e.connect(this.dryGainNode)}getWetInput(){return this.wetGainNode}getDryInput(){return this.dryGainNode}getOutput(){return this.outputNode}setMix(e){let t=Math.cos(e*Math.PI/2),o=Math.sin(e*Math.PI/2);this.dryGainNode.gain.value=t,this.wetGainNode.gain.value=o}disconnect(){this.dryGainNode.disconnect(),this.wetGainNode.disconnect(),this.outputNode.disconnect()}};var pt={rate:{kind:"number",min:.1,max:10,step:.1,exp:2,label:"Rate"},depth:{kind:"number",min:0,max:1,step:.01,label:"Depth"},mix:{kind:"number",min:0,max:1,step:.01,label:"Mix"},feedback:{kind:"number",min:0,max:.95,step:.01,label:"Feedback"}},yo={rate:.5,depth:.5,mix:.5,feedback:.2},B=class extends d{outputNode;feedbackGain;delayLeft;delayRight;lfoLeft;lfoRight;depthLeft;depthRight;merger;wetDryMixer;rateModGain;constructor(e,t){let o={...yo,...t.props},i=p=>new Mo(p.audioContext,{gain:1});super(e,{...t,audioNodeConstructor:i,props:o});let r=this.context.audioContext;this.feedbackGain=r.createGain(),this.delayLeft=r.createDelay(.1),this.delayRight=r.createDelay(.1),this.lfoLeft=r.createOscillator(),this.lfoRight=r.createOscillator(),this.depthLeft=r.createGain(),this.depthRight=r.createGain(),this.merger=r.createChannelMerger(2),this.wetDryMixer=new b(this.context),this.rateModGain=r.createGain(),this.lfoLeft.type="sine",this.lfoRight.type="sine",this.wetDryMixer.connectInput(this.audioNode),this.rateModGain.connect(this.lfoLeft.frequency),this.rateModGain.connect(this.lfoRight.frequency),this.lfoLeft.connect(this.depthLeft),this.depthLeft.connect(this.delayLeft.delayTime),this.lfoRight.connect(this.depthRight),this.depthRight.connect(this.delayRight.delayTime),this.audioNode.connect(this.feedbackGain),this.feedbackGain.connect(this.delayLeft),this.feedbackGain.connect(this.delayRight),this.delayLeft.connect(this.merger,0,0),this.delayRight.connect(this.merger,0,1),this.merger.connect(this.feedbackGain),this.merger.connect(this.wetDryMixer.getWetInput()),this.outputNode=this.wetDryMixer.getOutput(),this.delayLeft.delayTime.value=.02,this.delayRight.delayTime.value=.02,this.lfoLeft.frequency.value=o.rate,this.lfoRight.frequency.value=o.rate;let n=o.depth*.01;this.depthLeft.gain.value=n,this.depthRight.gain.value=n,this.feedbackGain.gain.value=o.feedback,this.wetDryMixer.setMix(o.mix);let u=this.context.audioContext.currentTime;this.lfoLeft.start(u);let a=1/(2*o.rate);this.lfoRight.start(u+a),this.registerDefaultIOs("in"),this.registerAdditionalInputs(),this.registerCustomOutput()}registerAdditionalInputs(){this.registerAudioInput({name:"rate",getAudioNode:()=>this.rateModGain})}registerCustomOutput(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputNode})}onAfterSetRate=e=>{this.lfoLeft.frequency.value=e,this.lfoRight.frequency.value=e};onAfterSetDepth=e=>{let t=e*.01;this.depthLeft.gain.value=t,this.depthRight.gain.value=t};onAfterSetMix=e=>{this.wetDryMixer.setMix(e)};onAfterSetFeedback=e=>{let t=Math.min(e,.95);this.feedbackGain.gain.value=t};dispose(){try{this.lfoLeft.stop(),this.lfoRight.stop()}catch{}super.dispose()}};import{ConstantSourceNode as dt}from"@blibliki/utils/web-audio-api";var lt={value:{kind:"number",min:-1/0,max:1/0,step:.01,label:"Value"}},go={value:1},_=class extends d{isStated=!1;constructor(e,t){let o={...go,...t.props},i=r=>new dt(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 dt(this.context.audioContext,{offset:this.props.value})}),this.isStated=!1)}triggerAttack=(e,t)=>{this.audioNode.offset.setValueAtTime(e.frequency,t),this.start(t)};triggerRelease=(e,t)=>{this.stop(t)}};import{divisionToMilliseconds as Io}from"@blibliki/transport";var He=(t=>(t.short="short",t.long="long",t))(He||{}),vo=["1/64","1/48","1/32","1/24","1/16","1/12","1/8","1/6","3/16","1/4","5/16","1/3","3/8","1/2","3/4","1","1.5","2","3","4","6","8","16","32"],ct={time:{kind:"number",min:0,max:5e3,step:1,label:"Delay Time"},timeMode:{kind:"enum",options:["short","long"],label:"Time Mode"},sync:{kind:"boolean",label:"Sync"},division:{kind:"enum",options:vo,label:"Division"},feedback:{kind:"number",min:0,max:.95,step:.01,label:"Feedback"},mix:{kind:"number",min:0,max:1,step:.01,label:"Mix"},stereo:{kind:"boolean",label:"Stereo"}},Po={time:250,timeMode:"short",sync:!1,division:"1/4",feedback:.3,mix:.3,stereo:!1},H=class extends d{outputNode;wetDryMixer;delayNode;feedbackGain;delayLeft=null;delayRight=null;feedbackLeft=null;feedbackRight=null;merger=null;constructor(e,t){let o={...Po,...t.props},i=r=>r.audioContext.createGain();super(e,{...t,props:o,audioNodeConstructor:i}),this.audioNode.gain.value=1,this.wetDryMixer=new b(this.context),this.delayNode=this.context.audioContext.createDelay(179),this.feedbackGain=this.context.audioContext.createGain(),this.connectMonoGraph(),this.wetDryMixer.setMix(o.mix),this.feedbackGain.gain.value=o.feedback,this.updateDelayTime(),o.stereo&&this.switchToStereo(),this.setupBPMListener(),this.registerDefaultIOs("in"),this.registerCustomOutput()}setupBPMListener(){this.engine.transport.addPropertyChangeCallback("bpm",()=>{this.props.sync&&this.updateDelayTime()})}updateDelayTime(){let e;if(this.props.sync){let t=this.engine.transport.bpm;e=Io(this.props.division,t)/1e3}else e=this.props.time/1e3;this.delayNode.delayTime.value=e,this.delayLeft&&(this.delayLeft.delayTime.value=e),this.delayRight&&(this.delayRight.delayTime.value=e)}connectMonoGraph(){this.disconnectAll(),this.wetDryMixer.connectInput(this.audioNode),this.audioNode.connect(this.delayNode),this.delayNode.connect(this.feedbackGain),this.feedbackGain.connect(this.delayNode),this.delayNode.connect(this.wetDryMixer.getWetInput()),this.outputNode=this.wetDryMixer.getOutput()}switchToStereo(){this.delayLeft||(this.delayLeft=this.context.audioContext.createDelay(179),this.delayRight=this.context.audioContext.createDelay(179),this.feedbackLeft=this.context.audioContext.createGain(),this.feedbackRight=this.context.audioContext.createGain(),this.merger=this.context.audioContext.createChannelMerger(2)),this.disconnectAll(),this.wetDryMixer.connectInput(this.audioNode),this.audioNode.connect(this.delayLeft),this.delayLeft.connect(this.merger,0,0),this.delayLeft.connect(this.feedbackRight),this.feedbackRight.connect(this.delayRight),this.delayRight.connect(this.merger,0,1),this.delayRight.connect(this.feedbackLeft),this.feedbackLeft.connect(this.delayLeft),this.merger.connect(this.wetDryMixer.getWetInput()),this.outputNode=this.wetDryMixer.getOutput(),this.delayLeft.delayTime.value=this.delayNode.delayTime.value,this.delayRight.delayTime.value=this.delayNode.delayTime.value,this.feedbackLeft.gain.value=this.feedbackGain.gain.value,this.feedbackRight.gain.value=this.feedbackGain.gain.value}disconnectAll(){try{this.audioNode.disconnect(),this.delayNode.disconnect(),this.feedbackGain.disconnect(),this.delayLeft&&this.delayLeft.disconnect(),this.delayRight&&this.delayRight.disconnect(),this.feedbackLeft&&this.feedbackLeft.disconnect(),this.feedbackRight&&this.feedbackRight.disconnect(),this.merger&&this.merger.disconnect()}catch{}}registerCustomOutput(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputNode})}onAfterSetTime=e=>{this.props.sync||this.updateDelayTime()};onAfterSetTimeMode=e=>{let t=e==="short"?2e3:5e3;this.props.time>t&&(this.props={time:t})};onAfterSetSync=e=>{this.updateDelayTime()};onAfterSetDivision=e=>{this.props.sync&&this.updateDelayTime()};onAfterSetFeedback=e=>{let t=Math.min(e,.95);this.feedbackGain.gain.value=t,this.feedbackLeft&&(this.feedbackLeft.gain.value=t),this.feedbackRight&&(this.feedbackRight.gain.value=t)};onAfterSetMix=e=>{this.wetDryMixer.setMix(e)};onAfterSetStereo=e=>{e?this.switchToStereo():this.connectMonoGraph()}};import{GainNode as To}from"@blibliki/utils/web-audio-api";var mt={drive:{kind:"number",min:0,max:10,step:.1,label:"Drive"},tone:{kind:"number",min:200,max:2e4,step:1,exp:3,label:"Tone"},mix:{kind:"number",min:0,max:1,step:.01,label:"Mix"}},ht={drive:2,tone:8e3,mix:1},Ue=class extends d{outputNode;inputGain;waveshaper;filter;wetDryMixer;constructor(e,t){let o={...ht,...t.props},i=n=>new To(n.audioContext,{gain:1});super(e,{...t,audioNodeConstructor:i,props:o});let r=this.context.audioContext;this.inputGain=r.createGain(),this.waveshaper=r.createWaveShaper(),this.filter=r.createBiquadFilter(),this.wetDryMixer=new b(this.context),this.filter.type="lowpass",this.filter.Q.value=.707,this.wetDryMixer.connectInput(this.audioNode),this.audioNode.connect(this.inputGain),this.inputGain.connect(this.waveshaper),this.waveshaper.connect(this.filter),this.filter.connect(this.wetDryMixer.getWetInput()),this.outputNode=this.wetDryMixer.getOutput(),this.updateInputGain(o.drive),this.updateDistortionCurve(o.drive),this.filter.frequency.value=o.tone,this.wetDryMixer.setMix(o.mix),this.registerDefaultIOs("in"),this.registerCustomOutput()}registerCustomOutput(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputNode})}generateDistortionCurve(e){let o=new ArrayBuffer(65536*Float32Array.BYTES_PER_ELEMENT),i=new Float32Array(o);for(let r=0;r<65536;r++){let u=(r*2/65536-1)*e;i[r]=Math.tanh(u)}return i}updateDistortionCurve(e){this.waveshaper.curve=this.generateDistortionCurve(e)}updateInputGain(e){this.inputGain.gain.value=Math.pow(2,e)}onAfterSetDrive=e=>{this.updateInputGain(e),this.updateDistortionCurve(e)};onAfterSetTone=e=>{this.filter.frequency.value=e};onAfterSetMix=e=>{this.wetDryMixer.setMix(e)}},U=class extends y{constructor(e,t){let o={...ht,...t.props},i=(r,n)=>d.create(Ue,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{GainNode as xo}from"@blibliki/utils/web-audio-api";import{assertNever as So}from"@blibliki/utils";var ft=URL.createObjectURL(new Blob(["(",(()=>{class s extends AudioWorkletProcessor{_lasttrig=0;_trig=0;_phase=0;_value=0;static get parameterDescriptors(){return[{name:"attack",defaultValue:.1,minValue:0,maxValue:60,automationRate:"k-rate"},{name:"attackcurve",defaultValue:.5,minValue:0,maxValue:1,automationRate:"k-rate"},{name:"decay",defaultValue:.1,minValue:0,maxValue:60,automationRate:"k-rate"},{name:"sustain",defaultValue:1,minValue:0,maxValue:1,automationRate:"k-rate"},{name:"release",defaultValue:.1,minValue:0,maxValue:60,automationRate:"k-rate"},{name:"trigger",defaultValue:0,minValue:0,maxValue:1,automationRate:"a-rate"}]}process(t,o,i){let r=o[0];if(!r?.[0])return!0;let n=i.trigger,u=i.attack[0],a=i.decay[0],p=i.sustain[0],l=i.release[0],h=1.01/Math.max(.01,i.attackcurve[0]),m=1-Math.pow(1-1/h,1/(sampleRate*u)),g=1-Math.pow(.36787944,1/(sampleRate*a)),f=1-Math.pow(.36787944,1/(sampleRate*l));n?.length===1&&(this._trig=n[0]);for(let c=0;c<r[0].length;++c){n&&n.length>1&&(this._trig=n[c]),this._trig>=.5?this._lasttrig<.5&&(this._phase=1):this._phase=0,this._phase===1?(this._value+=(h-this._value)*m,this._value>=1&&(this._value=1,this._phase=0)):this._trig>=.5&&this._value>p&&(this._value+=(p-this._value)*g),this._trig<.5&&(this._value+=-this._value*f);for(let P of r)P[c]=this._value;this._lasttrig=this._trig}return!0}}registerProcessor("custom-envelope-processor",s)}).toString(),")()"],{type:"application/javascript"}));var Mt=URL.createObjectURL(new Blob(["(",(()=>{class s 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],n=o[0];if(!r||!n)return!0;let u=i.cutoff,a=i.resonance;if(!u||!a)return!0;for(let p=0;p<r.length;p++){let l=r[p],h=n[p];if(!(!l||!h))for(let m=0;m<l.length;m++){let g=l[m];if(g===void 0)continue;let f=u.length>1?u[m]??u[0]:u[0];if(f===void 0)continue;let c=Math.max(20,Math.min(2e4,f)),P=Math.log(c/20)/Math.log(2e4/20),S=Math.pow(.5,(1-P)/.125),N=a.length>1?a[m]??a[0]:a[0];if(N===void 0)continue;let pe=1-Math.pow(.5,(N+.125)/.125)*S;this.s0=pe*this.s0-S*this.s1+S*g,this.s1=pe*this.s1+S*this.s0,h[m]=this.s1}}return!0}}registerProcessor("filter-processor",s)}).toString(),")()"],{type:"application/javascript"}));var yt=URL.createObjectURL(new Blob(["(",(()=>{class s extends AudioWorkletProcessor{phase=0;randomValue=Math.random()*2-1;static get parameterDescriptors(){return[{name:"frequency",defaultValue:1,minValue:.01,maxValue:100},{name:"waveform",defaultValue:0,minValue:0,maxValue:5},{name:"phase",defaultValue:0,minValue:0,maxValue:1}]}process(t,o,i){let r=o[0];if(!r)return!0;let n=i.frequency,u=i.waveform,a=i.phase;if(!n||!u||!a)return!0;let p=r[0]?.length??128;for(let l=0;l<p;l++){let h=n.length>1?n[l]??1:n[0]??1,m=Math.round(u.length>1?u[l]??0:u[0]??0),g=a.length>1?a[l]??0:a[0]??0,f=(this.phase+g)%1,c=0;switch(m){case 0:c=Math.sin(2*Math.PI*f);break;case 1:c=2*Math.abs(2*f-1)-1;break;case 2:c=f<.5?1:-1;break;case 3:c=2*f-1;break;case 4:c=1-2*f;break;case 5:c=this.randomValue;break;default:c=Math.sin(2*Math.PI*f)}for(let S of r)S[l]=c;let P=h/sampleRate;this.phase+=P,this.phase>=1&&(this.phase-=1,m===5&&(this.randomValue=Math.random()*2-1))}return!0}}registerProcessor("lfo-processor",s)}).toString(),")()"],{type:"application/javascript"}));var gt=URL.createObjectURL(new Blob(["(",(()=>{class s extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:"min",defaultValue:1e-10},{name:"max",defaultValue:1},{name:"current",defaultValue:.5},{name:"mode",defaultValue:0}]}process(t,o,i){let r=t[0],n=o[0];if(!r||!n)return!0;let u=i.min,a=i.max,p=i.current,l=i.mode;if(!u||!a||!p||!l)return!0;let h=r[0];if(!h||h.length===0){for(let m of n){let g=(p.length>1,p[0]??.5);m.fill(g)}return!0}for(let m=0;m<r.length;m++){let g=r[m],f=n[m];if(!(!g||!f))for(let c=0;c<g.length;c++){let P=g[c];if(P===void 0)continue;let S=u.length>1?u[c]??u[0]:u[0],N=a.length>1?a[c]??a[0]:a[0],T=p.length>1?p[c]??p[0]:p[0],pe=l.length>1?l[c]??l[0]:l[0];if(S===void 0||N===void 0||T===void 0||pe===void 0)continue;pe>=.5||T===0||P<0&&S===0||P>0&&N===0?P<0?f[c]=T+-P*(S-T):f[c]=T+P*(N-T):P<0?f[c]=T*Math.pow(S/T,-P):f[c]=T*Math.pow(N/T,P)}}return!0}}registerProcessor("scale-processor",s)}).toString(),")()"],{type:"application/javascript"}));async function It(s){await s.addModule(gt),await s.addModule(Mt),await s.addModule(yt),await s.addModule(ft)}function K(s,e){switch(e){case"ScaleProcessor":return s.newAudioWorklet("scale-processor");case"FilterProcessor":return s.newAudioWorklet("filter-processor");case"LFOProcessor":return s.newAudioWorklet("lfo-processor");case"CustomEnvelopeProcessor":return s.newAudioWorklet("custom-envelope-processor");default:So(e)}}var Pt={attack:.1,attackCurve:.5,decay:.1,sustain:1,release:.1},Tt={attack:{kind:"number",min:0,max:10,step:.01,exp:7,label:"Attack"},attackCurve:{kind:"number",min:0,max:1,step:.01,label:"Attack Curve"},decay:{kind:"number",min:0,max:10,step:.01,exp:6.6,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:0,max:10,step:.01,exp:5,label:"Release"}},Ke=class extends d{gainNode;constructor(e,t){let o={...Pt,...t.props},i=r=>{let n=K(r,"CustomEnvelopeProcessor");return n.parameters.get("attack").value=o.attack,n.parameters.get("attackcurve").value=o.attackCurve,n.parameters.get("decay").value=o.decay,n.parameters.get("sustain").value=o.sustain,n.parameters.get("release").value=o.release,n};super(e,{...t,props:o,audioNodeConstructor:i}),this.gainNode=new xo(this.context.audioContext,{gain:0}),this.audioNode.connect(this.gainNode.gain),this.registerIOs()}get attackParam(){return this.audioNode.parameters.get("attack")}get attackCurveParam(){return this.audioNode.parameters.get("attackcurve")}get decayParam(){return this.audioNode.parameters.get("decay")}get sustainParam(){return this.audioNode.parameters.get("sustain")}get releaseParam(){return this.audioNode.parameters.get("release")}get triggerParam(){return this.audioNode.parameters.get("trigger")}onAfterSetAttack=e=>{this.attackParam.value=e};onAfterSetAttackCurve=e=>{this.attackCurveParam.value=e};onAfterSetDecay=e=>{this.decayParam.value=e};onAfterSetSustain=e=>{this.sustainParam.value=e};onAfterSetRelease=e=>{this.releaseParam.value=e};triggerAttack(e,t){super.triggerAttack(e,t),this.triggerParam.setValueAtTime(1,t)}triggerRelease(e,t){super.triggerRelease(e,t),!(this.activeNotes.length>0)&&this.triggerParam.setValueAtTime(0,t)}dispose(){this.gainNode.disconnect(),super.dispose()}registerIOs(){this.registerAudioInput({name:"in",getAudioNode:()=>this.gainNode}),this.registerAudioOutput({name:"out",getAudioNode:()=>this.gainNode})}},W=class extends y{constructor(e,t){let o={...Pt,...t.props},i=(r,n)=>d.create(Ke,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};import{BiquadFilterNode as Ao}from"@blibliki/utils/web-audio-api";import{GainNode as bo}from"@blibliki/utils/web-audio-api";var St={gain:{kind:"number",min:0,max:2,step:.01,label:"Gain"}},xt={gain:1},ye=class extends d{constructor(e,t){let o={...xt,...t.props},i=r=>new bo(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})}},$=class extends y{constructor(e,t){let o={...xt,...t.props},i=(r,n)=>d.create(ye,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"gain"})}};var bt={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"},mode:{kind:"enum",label:"Mode",options:["exponential","linear"]}},At={min:0,max:1,current:.5,mode:"exponential"},ge=class extends d{constructor(e,t){let o={...At,...t.props},i=r=>K(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")}get mode(){return this.audioNode.parameters.get("mode")}onAfterSetMin=e=>{this.min.value=e};onAfterSetMax=e=>{this.max.value=e};onAfterSetCurrent=e=>{this.current.value=e};onAfterSetMode=e=>{this.mode.value=e==="exponential"?0:1}},j=class extends y{constructor(e,t){let o={...At,...t.props},i=(r,n)=>d.create(ge,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};var Ct=20,$e=2e4,Nt={cutoff:$e,envelopeAmount:0,type:"lowpass",Q:1},Ot={cutoff:{kind:"number",min:Ct,max:$e,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"}},We=class extends d{scale;amount;constructor(e,t){let o={...Nt,...t.props},i=r=>new Ao(r.audioContext,{type:o.type,frequency:0,Q:o.Q});super(e,{...t,props:o,audioNodeConstructor:i}),this.amount=d.create(ye,e,{name:"amount",moduleType:"Gain",props:{gain:o.envelopeAmount}}),this.scale=d.create(ge,e,{name:"scale",moduleType:"Scale",props:{min:Ct,max:$e,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})}},Q=class extends y{constructor(e,t){let o={...Nt,...t.props},i=(r,n)=>d.create(We,r,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"})}};import{AnalyserNode as Co}from"@blibliki/utils/web-audio-api";var kt={fftSize:{kind:"enum",options:[32,64,128,256,512,1024,2048,4096,8192,16384,32768],label:"FFT size"}},No={fftSize:512},Y=class extends d{_buffer;constructor(e,t){let o={...No,...t.props},i=r=>new Co(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs("in")}onAfterSetFftSize=e=>{this._buffer=new Float32Array(e)};get buffer(){return this._buffer?this._buffer:(this._buffer=new Float32Array(this.props.fftSize),this._buffer)}getValue(){return this.getValues()[0]??0}getValues(){return this.audioNode.getFloatTimeDomainData(this.buffer),this.buffer}};import{divisionToFrequency as Oo}from"@blibliki/transport";import{ConstantSourceNode as ko,GainNode as Ae}from"@blibliki/utils/web-audio-api";var J=(n=>(n.sine="sine",n.triangle="triangle",n.square="square",n.sawtooth="sawtooth",n.rampDown="rampDown",n.random="random",n))(J||{}),Do=["1/64","1/48","1/32","1/24","1/16","1/12","1/8","1/6","3/16","1/4","5/16","1/3","3/8","1/2","3/4","1","1.5","2","3","4","6","8","16","32"],Dt={sync:!1,frequency:1,division:"1/4",waveform:"sine",offset:0,amount:1},Et={sync:{kind:"boolean",label:"Sync"},frequency:{kind:"number",min:.01,max:40,step:.01,exp:3,label:"Frequency (Hz)"},division:{kind:"enum",options:Do,label:"Division"},waveform:{kind:"enum",options:Object.values(J),label:"Waveform"},offset:{kind:"number",min:-1,max:1,step:.01,label:"Offset"},amount:{kind:"number",min:0,max:1,step:.01,label:"Amount"}},je=class extends d{offsetConstant;offsetGain;amplitudeGain;mixerGain;amountGain;constructor(e,t){let o={...Dt,...t.props},i=r=>K(r,"LFOProcessor");super(e,{...t,props:o,audioNodeConstructor:i}),this.setupAudioGraph(),this.setupBPMListener(),this.registerOutputs(),this.updateFrequency()}setupAudioGraph(){let e=this.context.audioContext;this.offsetConstant=new ko(e,{offset:1}),this.offsetGain=new Ae(e,{gain:0}),this.amplitudeGain=new Ae(e,{gain:1}),this.mixerGain=new Ae(e,{gain:1}),this.amountGain=new Ae(e,{gain:this.props.amount});let t=Object.values(J).indexOf(this.props.waveform);this.waveformParam.value=t,this.updateOffsetGains(),this.audioNode.connect(this.amplitudeGain),this.amplitudeGain.connect(this.mixerGain),this.offsetConstant.connect(this.offsetGain),this.offsetGain.connect(this.mixerGain),this.mixerGain.connect(this.amountGain),this.offsetConstant.start()}setupBPMListener(){this.engine.transport.addPropertyChangeCallback("bpm",()=>{this.props.sync&&this.updateFrequencyFromBPM()})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.amountGain})}updateFrequency(){this.props.sync?this.updateFrequencyFromBPM():this.frequencyParam.value=this.props.frequency}updateFrequencyFromBPM(){let e=this.engine.transport.bpm,t=Oo(this.props.division,e);this.frequencyParam.value=t}updateOffsetGains(){let e=this.props.offset,t=1-Math.abs(e)/2,o=e/2;this.amplitudeGain.gain.value=t,this.offsetGain.gain.value=o}get frequencyParam(){return this.audioNode.parameters.get("frequency")}get waveformParam(){return this.audioNode.parameters.get("waveform")}get phaseParam(){return this.audioNode.parameters.get("phase")}onAfterSetSync=()=>{this.updateFrequency()};onAfterSetFrequency=e=>{this.frequencyParam.value=e};onAfterSetDivision=()=>{this.updateFrequencyFromBPM()};onAfterSetWaveform=e=>{let t=Object.values(J).indexOf(e);this.waveformParam.value=t};onAfterSetOffset=()=>{this.updateOffsetGains()};onAfterSetAmount=e=>{this.amountGain.gain.value=e};dispose(){this.offsetConstant.stop(),super.dispose()}},X=class extends y{constructor(e,t){let o={...Dt,...t.props},i=(r,n)=>d.create(je,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs("out")}};import{GainNode as Eo}from"@blibliki/utils/web-audio-api";var wt={attack:.01,decay:.1,sustain:.7,release:.3},Rt={attack:{kind:"number",min:.001,max:10,step:.001,exp:3,label:"Attack"},decay:{kind:"number",min:.001,max:10,step:.001,exp:3,label:"Decay"},sustain:{kind:"number",min:0,max:1,step:.01,label:"Sustain"},release:{kind:"number",min:.001,max:10,step:.001,exp:3,label:"Release"}},Ce=1e-5,Qe=class extends d{constructor(e,t){let o={...wt,...t.props},i=r=>{let n=new Eo(r.audioContext);return n.gain.value=0,n};super(e,{...t,props:o,audioNodeConstructor:i}),this.registerDefaultIOs()}triggerAttack(e,t){super.triggerAttack(e,t);let{attack:o,decay:i,sustain:r}=this.props,n=this.audioNode.gain;n.cancelAndHoldAtTime(t);let u=.002,a=u*5;n.setTargetAtTime(Ce,t,u);let p=t+a,l=p+o;if(n.setValueAtTime(Ce,p),n.linearRampToValueAtTime(1,l),r<1){let h=l+i,m=r>0?r:Ce;n.exponentialRampToValueAtTime(m,h)}}triggerRelease(e,t){if(super.triggerRelease(e,t),this.activeNotes.length>0)return;let{release:o}=this.props,i=this.audioNode.gain;i.cancelAndHoldAtTime(t),i.setTargetAtTime(Ce,t,o)}},Z=class extends y{constructor(e,t){let o={...wt,...t.props},i=(r,n)=>d.create(Qe,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerDefaultIOs()}};var wo={},Ft={},ee=class extends d{constructor(e,t){let o={...wo,...t.props},i=r=>r.destination;super(e,{...t,audioNodeConstructor:i,props:o}),this.registerDefaultIOs("in")}};var Gt={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},Ro={selectedId:void 0,selectedName:void 0},te=class extends d{midiOutput;_forwardMidiEvent;constructor(e,t){let o={...Ro,...t.props};super(e,{...t,props:o});let i=this.props.selectedId&&this.engine.findMidiInputDevice(this.props.selectedId);if(!i&&this.props.selectedName&&(i=this.engine.findMidiInputDeviceByName(this.props.selectedName),!i)){let r=this.engine.findMidiInputDeviceByFuzzyName(this.props.selectedName,.6);r&&(i=r.device,console.log(`MIDI device fuzzy matched: "${this.props.selectedName}" -> "${i.name}" (confidence: ${Math.round(r.score*100)}%)`))}i&&this.addEventListener(i),this.registerOutputs()}onSetSelectedId=e=>{if(this.removeEventListener(),!e)return e;let t=this.engine.findMidiInputDevice(e);return t&&(this.props.selectedName!==t.name&&(this.props={selectedName:t.name},this.triggerPropsUpdate()),this.addEventListener(t)),e};get forwardMidiEvent(){return this._forwardMidiEvent?this._forwardMidiEvent:(this._forwardMidiEvent=e=>{this.midiOutput.onMidiEvent(e)},this._forwardMidiEvent)}addEventListener(e){e.addEventListener(this.forwardMidiEvent)}removeEventListener(){if(!this.props.selectedId)return;this.engine.findMidiInputDevice(this.props.selectedId)?.removeEventListener(this.forwardMidiEvent)}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var Ye=(n=>(n.direct="direct",n.directRev="directRev",n.toggleInc="toggleInc",n.toggleDec="toggleDec",n.incDec="incDec",n.incDecRev="incDecRev",n))(Ye||{}),Lt={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"}},Fo={pages:[{name:"Page 1",mappings:[{}]}],activePage:0,globalMappings:[{}]};function Go({value:s,midiValue:e,propSchema:t,mapping:o}){let i=t.min??0,r=t.max??1,n=t.exp??1,{threshold:u=64,mode:a}=o,p=(s-i)/(r-i),h=Math.pow(p,1/n)*127;return h=e>=u&&a==="incDec"||e<=u&&a==="incDecRev"?h+1:h-1,Math.round(Math.max(0,Math.min(127,h)))}var oe=class extends d{_midiOut;constructor(e,t){let o={...Fo,...t.props};super(e,{...t,props:o}),this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent}),this._midiOut=this.registerMidiOutput({name:"midi out"})}onSetActivePage=e=>{let t=Math.max(Math.min(e,this.props.pages.length-1),0),o=this.props.pages[t],i=this.context.currentTime;return o?.mappings.forEach(r=>{if(r.cc!==void 0&&r.value!==void 0){let n=v.fromCC(r.cc,r.value,i);this._midiOut.onMidiEvent(n)}}),t};handleCC=(e,t)=>{this.checkAutoAssign(e);let o=this.props.pages[this.props.activePage];if(!o)return;let i=[...this.props.globalMappings.filter(r=>r.cc===e.cc),...o.mappings.filter(r=>r.cc===e.cc)];if(i.forEach(r=>{this.forwardMapping(e,r,t)}),i.length>0&&e.ccValue!==void 0){let r=this.props.globalMappings.map(a=>a.cc===e.cc?{...a,value:e.ccValue}:a),n=o.mappings.map(a=>a.cc===e.cc?{...a,value:e.ccValue}:a),u=this.props.pages.map((a,p)=>p===this.props.activePage?{...a,mappings:n}:a);this.props={pages:u,globalMappings:r},this.triggerPropsUpdate()}};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 n=t.mode??"direct";if((n==="toggleInc"||n==="toggleDec")&&r!==127)return;let u=this.engine.findModule(t.moduleId),a=Je[u.moduleType][i],p;switch(a.kind){case"number":{let l=u.props[i];if(n==="incDec"||n==="incDecRev"?r=Go({value:l,propSchema:a,mapping:t,midiValue:r}):n==="directRev"&&(r=127-r),n==="toggleInc")p=l+(a.step??1);else if(n==="toggleDec")p=l-(a.step??1);else{let h=a.min??0,m=a.max??1,g=r/127,f=Math.pow(g,a.exp??1);if(p=h+f*(m-h),a.step!==void 0&&(!a.exp||a.exp===1)){let c=Math.round((p-h)/a.step);p=h+c*a.step}}break}case"enum":{let l=Math.floor(r/127*a.options.length),h=Math.min(l,a.options.length-1);p=a.options[h];break}case"boolean":p=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")}u.props={[i]:p},u.triggerPropsUpdate()};checkAutoAssign(e){if(e.cc===void 0)return;let t=this.props.pages[this.props.activePage];if(!t)return;let o=this.props.globalMappings.some(({autoAssign:a})=>a),i=t.mappings.some(({autoAssign:a})=>a);if(!o&&!i)return;let r=o?this.props.globalMappings.map(a=>a.autoAssign?{...a,cc:e.cc,autoAssign:!1}:a):this.props.globalMappings,n=i?t.mappings.map(a=>a.autoAssign?{...a,cc:e.cc,autoAssign:!1}:a):t.mappings,u=this.props.pages.map((a,p)=>p===this.props.activePage?{...a,mappings:n}:a);this.props={pages:u,globalMappings:r},this.triggerPropsUpdate()}};var qt={selectedId:{kind:"string",label:"Midi device ID"},selectedName:{kind:"string",label:"Midi device name"}},Lo={selectedId:void 0,selectedName:void 0},ie=class extends d{midiInput;currentDevice;constructor(e,t){let o={...Lo,...t.props};super(e,{...t,props:o});let i=this.props.selectedId&&this.engine.findMidiOutputDevice(this.props.selectedId);if(!i&&this.props.selectedName&&(i=this.engine.findMidiOutputDeviceByName(this.props.selectedName),!i)){let r=this.engine.findMidiOutputDeviceByFuzzyName(this.props.selectedName,.6);r&&(i=r.device,console.log(`MIDI device fuzzy matched: "${this.props.selectedName}" -> "${i.name}" (confidence: ${Math.round(r.score*100)}%)`))}i&&(this.currentDevice=i),this.registerInputs()}onSetSelectedId=e=>{if(!e)return this.currentDevice=void 0,e;let t=this.engine.findMidiOutputDevice(e);return t&&(this.props.selectedName!==t.name&&(this.props={selectedName:t.name},this.triggerPropsUpdate()),this.currentDevice=t),e};onMidiEvent=e=>{if(!this.currentDevice)return;let t=e.rawMessage.data;this.currentDevice.send(t)};registerInputs(){this.midiInput=this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}};import{AudioBufferSourceNode as Xe}from"@blibliki/utils/web-audio-api";var Ne=(i=>(i.white="white",i.pink="pink",i.brown="brown",i.blue="blue",i))(Ne||{}),zt={type:{kind:"enum",options:Object.values(Ne),label:"Type"}},qo={type:"white"};function zo(s,e){let t=s.sampleRate,o=t*e,i=s.createBuffer(2,o,t);for(let r=0;r<2;r++){let n=i.getChannelData(r);for(let u=0;u<o;u++)n[u]=Math.random()*2-1}return i}function Vo(s,e){let t=s.sampleRate,o=t*e,i=s.createBuffer(2,o,t);for(let r=0;r<2;r++){let n=i.getChannelData(r),u=0,a=0,p=0,l=0,h=0,m=0,g=0;for(let f=0;f<o;f++){let c=Math.random()*2-1;u=.99886*u+c*.0555179,a=.99332*a+c*.0750759,p=.969*p+c*.153852,l=.8665*l+c*.3104856,h=.55*h+c*.5329522,m=-.7616*m-c*.016898;let P=u+a+p+l+h+m+g+c*.5362;g=c*.115926,n[f]=P*.11}}return i}function Bo(s,e){let t=s.sampleRate,o=t*e,i=s.createBuffer(2,o,t);for(let r=0;r<2;r++){let n=i.getChannelData(r),u=0;for(let a=0;a<o;a++){let p=Math.random()*2-1;u=(u+p*.02)*.99,n[a]=u*3.5}}return i}function _o(s,e){let t=s.sampleRate,o=t*e,i=s.createBuffer(2,o,t);for(let r=0;r<2;r++){let n=i.getChannelData(r),u=0;for(let a=0;a<o;a++){let p=Math.random()*2-1;n[a]=(p-u)*.5,u=p}}return i}var re=class extends d{isStated=!1;noiseBuffers;constructor(e,t){let o={...qo,...t.props},i=n=>new Xe(n.audioContext);super(e,{...t,props:o,audioNodeConstructor:i});let r=2;this.noiseBuffers=new Map([["white",zo(this.context.audioContext,r)],["pink",Vo(this.context.audioContext,r)],["brown",Bo(this.context.audioContext,r)],["blue",_o(this.context.audioContext,r)]]),this.audioNode.buffer=this.noiseBuffers.get(o.type),this.audioNode.loop=!0,this.registerDefaultIOs("out")}onAfterSetType=e=>{let t=this.isStated,o=this.context.audioContext.currentTime;t&&this.stop(o),this.rePlugAll(()=>{this.audioNode=new Xe(this.context.audioContext,{buffer:this.noiseBuffers.get(e),loop:!0})}),t&&this.start(o)};start(e){this.isStated||(this.isStated=!0,this.audioNode.start(e))}stop(e){this.isStated&&(this.audioNode.stop(e),this.rePlugAll(()=>{this.audioNode=new Xe(this.context.audioContext,{buffer:this.noiseBuffers.get(this.props.type),loop:!0})}),this.isStated=!1)}};import{dbToGain as Vt}from"@blibliki/utils";import{GainNode as Bt,OscillatorNode as _t}from"@blibliki/utils/web-audio-api";var Ze=-18,Oe=(i=>(i.sine="sine",i.triangle="triangle",i.square="square",i.sawtooth="sawtooth",i))(Oe||{}),Ht={wave:{kind:"enum",options:Object.values(Oe),label:"Waveform"},frequency:{kind:"number",min:0,max:25e3,step:1,label:"Frequency"},fine:{kind:"number",min:-1,max:1,step:.01,label:"Fine"},coarse:{kind:"number",min:-12,max:12,step:1,label:"Coarse"},octave:{kind:"number",min:-1,max:2,step:1,label:"Octave"},lowGain:{kind:"boolean",label:`Use ${Ze}db Gain`}},Ut={wave:"sine",frequency:440,fine:0,coarse:0,octave:0,lowGain:!1},et=class extends d{isStated=!1;outputGain;detuneGain;constructor(e,t){let o={...Ut,...t.props},i=r=>new _t(r.audioContext);super(e,{...t,props:o,audioNodeConstructor:i}),this.outputGain=new Bt(this.context.audioContext,{gain:Vt(Ze)}),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?Vt(Ze):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 _t(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 Bt(this.context.audioContext,{gain:100}),this.detuneGain.connect(this.audioNode.detune)}registerInputs(){this.registerAudioInput({name:"detune",getAudioNode:()=>this.detuneGain}),this.registerAudioInput({name:"fm",getAudioNode:()=>this.audioNode.frequency})}registerOutputs(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputGain})}},ne=class extends y{constructor(e,t){let o={...Ut,...t.props},i=(r,n)=>d.create(et,r,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"}),this.registerAudioInput({name:"fm"})}};var Kt={mix:{kind:"number",min:0,max:1,step:.01,label:"Mix"},decayTime:{kind:"number",min:.1,max:10,step:.1,exp:2,label:"Decay Time"},preDelay:{kind:"number",min:0,max:100,step:1,label:"Pre-delay"},type:{kind:"enum",options:["room","hall","plate","spring","chamber","reflections"]}},Ho={mix:.3,decayTime:1.5,preDelay:0,type:"room"};function Uo(s,e,t){let o=s.audioContext.sampleRate,i=e==="reflections"?Math.min(t,.2):t,r=Math.floor(o*i),n=s.audioContext.createBuffer(2,r,o),u=$o(e);return e==="reflections"?Ko(n,o,u):Wo(n,o,r,u),jo(n),n}function Ko(s,e,t){let o=[0,7,11,17,23,31,41,47,59,67,79,89,103,127];for(let i=0;i<2;i++){let r=s.getChannelData(i),n=i*2.3;for(let u of o){let a=(u+n)/1e3,p=Math.floor(a*e);if(p>=r.length)break;let l=Math.floor(e*.003),h=Math.exp(-a*t.decayFactor);for(let m=0;m<l&&p+m<r.length;m++){let g=Math.random()*2-1,f=Math.exp(-m/(l*.3));r[p+m]=r[p+m]+g*h*f}}Wt(r,t.cutoff)}}function Wo(s,e,t,o){for(let i=0;i<2;i++){let r=s.getChannelData(i);for(let n=0;n<t;n++){let u=Math.random()*2-1,a=n/e,p=Math.exp(-a*o.decayFactor),l=1-o.damping*(n/t);r[n]=u*p*l}Wt(r,o.cutoff)}}function $o(s){switch(s){case"room":return{decayFactor:3,damping:.5,cutoff:.7};case"hall":return{decayFactor:1.5,damping:.3,cutoff:.85};case"plate":return{decayFactor:2.5,damping:.2,cutoff:.9};case"spring":return{decayFactor:4,damping:.1,cutoff:.95};case"chamber":return{decayFactor:2,damping:.35,cutoff:.75};case"reflections":return{decayFactor:5,damping:.4,cutoff:.8};default:return{decayFactor:3,damping:.5,cutoff:.7}}}function Wt(s,e){let t=0,o=1-e;for(let i=0;i<s.length;i++){let r=s[i];t=o*r+(1-o)*t,s[i]=t}}function jo(s){for(let e=0;e<s.numberOfChannels;e++){let t=s.getChannelData(e),o=0;for(let i of t)o=Math.max(o,Math.abs(i));if(o>0){let i=.5/o;for(let r=0;r<t.length;r++)t[r]=t[r]*i}}}var se=class extends d{outputNode;convolverNode;preDelayNode;wetDryMixer;constructor(e,t){let o={...Ho,...t.props},i=r=>r.audioContext.createGain();super(e,{...t,props:o,audioNodeConstructor:i}),this.audioNode.gain.value=1,this.wetDryMixer=new b(this.context),this.convolverNode=this.context.audioContext.createConvolver(),this.preDelayNode=this.context.audioContext.createDelay(.1),this.wetDryMixer.connectInput(this.audioNode),this.audioNode.connect(this.preDelayNode),this.preDelayNode.connect(this.convolverNode),this.convolverNode.connect(this.wetDryMixer.getWetInput()),this.outputNode=this.wetDryMixer.getOutput(),this.regenerateImpulseResponse(),this.wetDryMixer.setMix(o.mix),this.preDelayNode.delayTime.value=o.preDelay/1e3,this.registerDefaultIOs("in"),this.registerCustomOutput()}registerCustomOutput(){this.registerAudioOutput({name:"out",getAudioNode:()=>this.outputNode})}onAfterSetMix=e=>{this.wetDryMixer.setMix(e)};onAfterSetDecayTime=()=>{this.regenerateImpulseResponse()};onAfterSetPreDelay=e=>{this.preDelayNode.delayTime.value=e/1e3};onAfterSetType=()=>{this.regenerateImpulseResponse()};regenerateImpulseResponse(){let e=Uo(this.context,this.props.type,this.props.decayTime);this.convolverNode.buffer=e}};import{divisionToMilliseconds as Qo,TPB as Yo,StepSequencerSource as Jo,Resolution as ke,PlaybackMode as De}from"@blibliki/transport";var Xo=Yo/4/10,$t={activePatternNo:{kind:"number",label:"Active pattern",min:0,max:100,step:1},activePageNo:{kind:"number",label:"Active page",min:0,max:100,step:1},stepsPerPage:{kind:"number",min:1,max:16,step:1,label:"Steps per Page"},resolution:{kind:"enum",options:Object.values(ke),label:"Resolution"},playbackMode:{kind:"enum",options:Object.values(De),label:"Playback Mode"},patternSequence:{kind:"string",label:"Pattern Sequence"},enableSequence:{kind:"boolean",label:"Enable Sequence"}},Zo=["1/64","1/48","1/32","1/24","1/16","1/12","1/8","1/6","3/16","1/4","5/16","1/3","3/8","1/2","3/4","1","1.5","2","3","4","6","8","16","32"],jt={probability:{kind:"number",label:"Probability",min:0,max:100,step:1},duration:{kind:"enum",label:"Duration",options:Zo},microtimeOffset:{kind:"number",label:"Microtiming",min:-100,max:100,step:1}},ei=()=>({active:!1,notes:[],ccMessages:[],probability:100,microtimeOffset:0,duration:"1/16"}),ti=s=>({name:s,steps:Array.from({length:16},()=>ei())}),oi=s=>({name:s,pages:[ti("Page 1")]}),ii={patterns:[oi("A")],activePatternNo:0,activePageNo:0,stepsPerPage:16,resolution:ke.sixteenth,playbackMode:De.loop,patternSequence:"",enableSequence:!1},ri={isRunning:!1,currentStep:0,sequencePosition:void 0},G=class extends d{midiOutput;scheduledNotes=new Map;source;constructor(e,t){let o={...ii,...t.props};super(e,{...t,props:o}),this._state={...ri},this.registerOutputs(),this.initializeSource()}onSetActivePatternNo=e=>Math.max(Math.min(e,this.props.patterns.length-1),0);onAfterSetPatternSequence=e=>{this.source&&(this.source.props={...this.source.props,patternSequence:e})};onAfterSetPatterns=e=>{this.source&&(this.source.props={...this.source.props,patterns:e})};onAfterSetResolution=e=>{this.source&&(this.source.props={...this.source.props,resolution:e})};onAfterSetPlaybackMode=e=>{this.source&&(this.source.props={...this.source.props,playbackMode:e})};onAfterSetEnableSequence=e=>{this.source&&(this.source.props={...this.source.props,enableSequence:e})};initializeSource(){this.source=new Jo(this.engine.transport,{onEvent:this.handleStepEvent,patterns:this.props.patterns,stepsPerPage:this.props.stepsPerPage,resolution:this.props.resolution,playbackMode:this.props.playbackMode,patternSequence:this.props.patternSequence,enableSequence:this.props.enableSequence}),this.engine.transport.addSource(this.source)}handleStepEvent=e=>{this.state={...this.state,currentStep:e.stepNo},e.pageNo!==this.props.activePageNo&&(this.props={...this.props,activePageNo:e.pageNo}),e.patternNo!==this.props.activePatternNo&&(this.props={...this.props,activePatternNo:e.patternNo}),this.triggerStep(e.step,e.contextTime),this.triggerPropsUpdate()};registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi"})}triggerStep(e,t){if(!e.active||e.notes.length===0&&e.ccMessages.length===0||Math.random()*100>e.probability)return;let o=this.engine.bpm;e.ccMessages.forEach(u=>{this.sendCC(u,t)});let i=e.microtimeOffset/Xo*(60/o),r=Qo(e.duration,o)/1e3,n=t+i;e.notes.forEach(u=>{this.sendNoteOn(u,n),r!==1/0&&this.sendNoteOff(u,n+r)})}sendNoteOn(e,t){let o=new M(e.note);o.velocity=e.velocity/127;let i=v.fromNote(o,!0,t);this.midiOutput.onMidiEvent(i),this.scheduledNotes.set(e.note,t)}sendNoteOff(e,t){let o=v.fromNote(e.note,!1,t);this.midiOutput.onMidiEvent(o),this.scheduledNotes.delete(e.note)}sendCC(e,t){let o=v.fromCC(e.cc,e.value,t);this.midiOutput.onMidiEvent(o)}start(e){super.start(e),this.state={isRunning:!0},this.scheduledNotes.clear();let t=this.engine.transport.getTicksAtContextTime(e);this.source.onStart(t),this.triggerPropsUpdate()}stop(e){super.stop(e),this.state={isRunning:!1},this.scheduledNotes.forEach((o,i)=>{let r=v.fromNote(i,!1,e);this.midiOutput.onMidiEvent(r)}),this.scheduledNotes.clear();let t=this.engine.transport.getTicksAtContextTime(e);this.source.onStop(t),this.source.onJump(0),this.state={currentStep:0},this.triggerPropsUpdate()}dispose(){this.source&&this.engine.transport.removeSource(this.source.id)}};import{StereoPannerNode as ni}from"@blibliki/utils/web-audio-api";var Qt={pan:{kind:"number",min:-1,max:1,step:.01,label:"Pan"}},Yt={pan:0},tt=class extends d{constructor(e,t){let o={...Yt,...t.props},i=r=>new ni(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})}},ue=class extends y{constructor(e,t){let o={...Yt,...t.props},i=(r,n)=>d.create(tt,r,n);super(e,{...t,props:o,monoModuleConstructor:i}),this.registerAdditionalInputs(),this.registerDefaultIOs()}registerAdditionalInputs(){this.registerAudioInput({name:"pan"})}};var Jt={activeNotes:{kind:"array",label:"Active notes"}},si={activeNotes:[]},ae=class extends d{midiOutput;constructor(e,t){let o={...si,...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(v.fromNote(e,!0,t))};triggerRelease=(e,t)=>{this.props={activeNotes:this.props.activeNotes.filter(o=>o!==e.fullName)},this.triggerPropsUpdate(),this.sendMidi(v.fromNote(e,!1,t))};registerInputs(){this.registerMidiInput({name:"midi in",onMidiEvent:this.onMidiEvent})}registerOutputs(){this.midiOutput=this.registerMidiOutput({name:"midi out"})}};var be=(I=>(I.Master="Master",I.Oscillator="Oscillator",I.Gain="Gain",I.MidiInput="MidiInput",I.MidiOutput="MidiOutput",I.LegacyEnvelope="LegacyEnvelope",I.Envelope="Envelope",I.Filter="Filter",I.Scale="Scale",I.StereoPanner="StereoPanner",I.Inspector="Inspector",I.Chorus="Chorus",I.Constant="Constant",I.Delay="Delay",I.Distortion="Distortion",I.MidiMapper="MidiMapper",I.VirtualMidi="VirtualMidi",I.StepSequencer="StepSequencer",I.VoiceScheduler="VoiceScheduler",I.LFO="LFO",I.Noise="Noise",I.Reverb="Reverb",I))(be||{}),Je={Oscillator:Ht,Gain:St,Master:Ft,MidiInput:Gt,MidiOutput:qt,LegacyEnvelope:Rt,Envelope:Tt,Filter:Ot,Scale:bt,StereoPanner:Qt,Inspector:kt,Chorus:pt,Constant:lt,Delay:ct,Distortion:mt,MidiMapper:Lt,VirtualMidi:Jt,StepSequencer:$t,VoiceScheduler:ut,LFO:Et,Noise:zt,Reverb:Kt};function Xt(s,e){switch(e.moduleType){case"Oscillator":return ne.create(ne,s,e);case"Gain":return $.create($,s,e);case"Master":return ee.create(ee,s,e);case"MidiInput":return te.create(te,s,e);case"MidiOutput":return ie.create(ie,s,e);case"LegacyEnvelope":return Z.create(Z,s,e);case"Envelope":return W.create(W,s,e);case"Filter":return Q.create(Q,s,e);case"Scale":return j.create(j,s,e);case"StereoPanner":return ue.create(ue,s,e);case"Inspector":return Y.create(Y,s,e);case"Chorus":return B.create(B,s,e);case"Constant":return _.create(_,s,e);case"Delay":return H.create(H,s,e);case"Distortion":return U.create(U,s,e);case"MidiMapper":return oe.create(oe,s,e);case"VirtualMidi":return ae.create(ae,s,e);case"StepSequencer":return G.create(G,s,e);case"VoiceScheduler":return V.create(V,s,e);case"LFO":return X.create(X,s,e);case"Noise":return re.create(re,s,e);case"Reverb":return se.create(se,s,e);default:ui(e)}}var R=class s{static _engines=new Map;static _currentId;propsUpdateCallbacks=[];id;context;isInitialized=!1;routes;transport;modules;midiDeviceManager;static getById(e){let t=s._engines.get(e);return Zt(t),t}static get current(){return Zt(this._currentId),this.getById(this._currentId)}static async load(e){let{bpm:t,timeSignature:o,modules:i,routes:r}=e,n=new pi,u=new s(n);return await u.initialize(),u.timeSignature=o,u.bpm=t,i.forEach(a=>{u.addModule(a)}),r.forEach(a=>{u.addRoute(a)}),u}constructor(e){this.id=li(),this.context=e,this.transport=new ai(this.context,{onStart:this.onStart,onStop:this.onStop}),this.routes=new he(this),this.modules=new Map,this.midiDeviceManager=new z(this.context),s._engines.set(this.id,this),s._currentId=this.id}get state(){return this.transport.state}async initialize(){this.isInitialized||(await It(this.context),await this.midiDeviceManager.initialize(),this.isInitialized=!0)}addModule(e){let t=Xt(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=di(e.changes,["name","props"]);return Object.assign(t,o),t instanceof y&&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();let e=this.context.currentTime;this.transport.start(e)}stop(){let e=this.context.currentTime;this.transport.stop(e),this.transport.reset(e)}pause(){let e=this.context.currentTime;this.transport.stop(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}async resume(){await this.context.resume()}dispose(){this.stop(),this.routes.clear(),this.modules.forEach(e=>{e.dispose()}),this.modules.clear()}serialize(){return{bpm:this.bpm,timeSignature:this.timeSignature,modules:Array.from(this.modules.values()).map(e=>e.serialize()),routes:this.routes.serialize()}}findModule(e){let t=this.modules.get(e);if(!t)throw Error(`The module with id ${e} is not exists`);return t}findIO(e,t,o){return this.findModule(e)[`${o}s`].findByName(t)}findMidiDevice(e){return this.midiDeviceManager.find(e)}findMidiDeviceByName(e){return this.midiDeviceManager.findByName(e)}findMidiDeviceByFuzzyName(e,t){return this.midiDeviceManager.findByFuzzyName(e,t)}findMidiInputDevice(e){return this.midiDeviceManager.findInput(e)}findMidiInputDeviceByName(e){return this.midiDeviceManager.findInputByName(e)}findMidiInputDeviceByFuzzyName(e,t){return this.midiDeviceManager.findInputByFuzzyName(e,t)}findMidiOutputDevice(e){return this.midiDeviceManager.findOutput(e)}findMidiOutputDeviceByName(e){return this.midiDeviceManager.findOutputByName(e)}findMidiOutputDeviceByFuzzyName(e,t){return this.midiDeviceManager.findOutputByFuzzyName(e,t)}onPropsUpdate(e){this.propsUpdateCallbacks.push(e)}_triggerPropsUpdate(e){this.propsUpdateCallbacks.forEach(t=>{t(e)})}triggerVirtualMidi(e,t,o){let i=this.findModule(e);if(i.moduleType!=="VirtualMidi")throw Error("This is not a virtual mid");i.sendMidi(v.fromNote(t,o==="noteOn",this.context.currentTime))}onStart=e=>{let t=this.transport.getContextTimeAtTicks(e);this.modules.forEach(o=>{o.start(t)})};onStop=e=>{let t=this.transport.getContextTimeAtTicks(e);this.modules.forEach(o=>{o.stop(t)})}};import{TransportState as Yp}from"@blibliki/transport";import{Context as Xp}from"@blibliki/utils";export{Xp as Context,He as DelayTimeMode,R as Engine,J as LFOWaveform,C as MidiDevice,C as MidiInputDevice,Ye as MidiMappingMode,D as MidiOutputDevice,ve as MidiPortState,be as ModuleType,Ne as NoiseType,M as Note,Oe as OscillatorWave,De as PlaybackMode,ke as Resolution,G as StepSequencer,Yp as TransportState,Je as moduleSchemas,jt as stepPropSchema};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|