supersonic-scsynth 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/supersonic.js
CHANGED
|
@@ -14,5 +14,5 @@ SharedArrayBuffer is available but cross-origin isolation is not enabled. Please
|
|
|
14
14
|
SharedArrayBuffer is not available. This may be due to:
|
|
15
15
|
1. Missing COOP/COEP headers
|
|
16
16
|
2. Browser doesn't support SharedArrayBuffer
|
|
17
|
-
3. Browser security settings`),r}return this.#u}#V(){let e=this.config.memory;this.#d=new WebAssembly.Memory({initial:e.totalPages,maximum:e.totalPages,shared:!0}),this.#t=this.#d.buffer}#H(){return this.#r=new AudioContext(this.config.audioContextOptions),this.#r}#G(){this.#s=new C({audioContext:this.#r,sharedBuffer:this.#t,bufferPoolConfig:{start:this.config.memory.bufferPoolOffset,size:this.config.memory.bufferPoolSize},sampleBaseURL:this.#p,audioPathMap:this.#y,maxBuffers:this.config.worldOptions.numBuffers})}async#Z(){let e=this.config.wasmBaseURL+"manifest.json";try{let t=await fetch(e);if(!t.ok)return;let r=await t.json();this.config.wasmUrl=this.config.wasmBaseURL+r.wasmFile}catch{}}async#q(){this.config.development&&await this.#Z();let e=await fetch(this.config.wasmUrl);if(!e.ok)throw new Error(`Failed to load WASM: ${e.status} ${e.statusText}`);return await e.arrayBuffer()}async#Y(e){await this.#r.audioWorklet.addModule(this.config.workletUrl),this.#n=new AudioWorkletNode(this.#r,"scsynth-processor",{numberOfInputs:0,numberOfOutputs:1,outputChannelCount:[2]}),this.#n.connect(this.#r.destination),this.#n.port.postMessage({type:"init",sharedBuffer:this.#t}),this.#n.port.postMessage({type:"loadWasm",wasmBytes:e,wasmMemory:this.#d,worldOptions:this.config.worldOptions,sampleRate:this.#r.sampleRate}),await this.#K()}async#Q(){this.#o=new R(this.config.workerBaseURL),this.#o.onRawOSC(e=>{this.onOSC&&this.onOSC(e)}),this.#o.onParsedOSC(e=>{if(e.address==="/buffer/freed")this.#s?.handleBufferFreed(e.args);else if(e.address==="/buffer/allocated")this.#s?.handleBufferAllocated(e.args);else if(e.address==="/synced"&&e.args.length>0){let t=e.args[0];this.#c&&this.#c.has(t)&&this.#c.get(t)(e)}this.onMessage&&this.onMessage(e)}),this.#o.onDebugMessage(e=>{this.onDebugMessage&&this.onDebugMessage(e)}),this.#o.onError((e,t)=>{console.error(`[SuperSonic] ${t} error:`,e),this.onError&&this.onError(new Error(`${t}: ${e}`))}),await this.#o.init(this.#t,this.#i,this.#e)}#j(){this.#a=!0,this.#h=!1,this.bootStats.initDuration=performance.now()-this.bootStats.initStartTime,this.onInitialized&&this.onInitialized({capabilities:this.#u,bootStats:this.bootStats})}async init(e={}){if(this.#a){console.warn("[SuperSonic] Already initialized");return}if(this.#h){console.warn("[SuperSonic] Initialization already in progress");return}this.config={...this.config,...e,audioContextOptions:{...this.config.audioContextOptions,...e.audioContextOptions||{}}},this.#h=!0,this.bootStats.initStartTime=performance.now();try{this.setAndValidateCapabilities(),this.#V(),this.#H(),this.#G();let t=await this.#q();await this.#Y(t),await this.#Q(),this.#J(),this.#N(),this.#j()}catch(t){throw this.#h=!1,console.error("[SuperSonic] Initialization failed:",t),this.onError&&this.onError(t),t}}#K(){return new Promise((e,t)=>{let r=setTimeout(()=>{t(new Error("AudioWorklet initialization timeout"))},5e3),i=async o=>{if(o.data.type!=="debug"){if(o.data.type==="error"){console.error("[AudioWorklet] Error:",o.data.error),clearTimeout(r),this.#n.port.removeEventListener("message",i),t(new Error(o.data.error||"AudioWorklet error"));return}o.data.type==="initialized"&&(clearTimeout(r),this.#n.port.removeEventListener("message",i),o.data.success?(o.data.ringBufferBase!==void 0?this.#i=o.data.ringBufferBase:console.warn("[SuperSonic] Warning: ringBufferBase not provided by worklet"),o.data.bufferConstants!==void 0?(this.#e=o.data.bufferConstants,this.#te(),await this.initializeNTPTiming(),this.#se()):console.warn("[SuperSonic] Warning: bufferConstants not provided by worklet"),e()):t(new Error(o.data.error||"AudioWorklet initialization failed")))}};this.#n.port.addEventListener("message",i),this.#n.port.start()})}#J(){this.#n.port.onmessage=e=>{let{data:t}=e;switch(t.type){case"error":console.error("[Worklet] Error:",t.error),t.diagnostics&&(console.error("[Worklet] Diagnostics:",t.diagnostics),console.table(t.diagnostics)),this.onError&&this.onError(new Error(t.error));break;case"process_debug":break;case"debug":break;case"console":this.onConsoleMessage&&this.onConsoleMessage(t.message);break;case"version":this.onVersion&&this.onVersion(t.version);break}}}#X(){if(!this.#f)return null;let e=this.#f;return{workletProcessCount:e[0],workletMessagesProcessed:e[1],workletMessagesDropped:e[2],workletSchedulerDepth:e[3],workletSchedulerMax:e[4],workletSchedulerDropped:e[5],workletSequenceGaps:e[24],preschedulerPending:e[6],preschedulerPeak:e[7],preschedulerSent:e[8],preschedulerRetriesSucceeded:e[9],preschedulerRetriesFailed:e[10],preschedulerBundlesScheduled:e[11],preschedulerEventsCancelled:e[12],preschedulerTotalDispatches:e[13],preschedulerMessagesRetried:e[14],preschedulerRetryQueueSize:e[15],preschedulerRetryQueueMax:e[16],preschedulerBypassed:e[25],oscInMessagesReceived:e[17],oscInMessagesDropped:e[18],oscInBytesReceived:e[19],debugMessagesReceived:e[20],debugBytesReceived:e[21],mainMessagesSent:e[22],mainBytesSent:e[23]}}#ee(){if(!this.#m||!this.#e||!this.#i)return null;let e=this.#i+this.#e.CONTROL_START,t=this.#m,r=Atomics.load(t,(e+0)/4),i=Atomics.load(t,(e+4)/4),o=Atomics.load(t,(e+8)/4),a=Atomics.load(t,(e+12)/4),l=Atomics.load(t,(e+16)/4),c=Atomics.load(t,(e+20)/4),u=(r-i+this.#e.IN_BUFFER_SIZE)%this.#e.IN_BUFFER_SIZE,f=(o-a+this.#e.OUT_BUFFER_SIZE)%this.#e.OUT_BUFFER_SIZE,h=(l-c+this.#e.DEBUG_BUFFER_SIZE)%this.#e.DEBUG_BUFFER_SIZE;return{inBufferUsed:{bytes:u,percentage:u/this.#e.IN_BUFFER_SIZE*100},outBufferUsed:{bytes:f,percentage:f/this.#e.OUT_BUFFER_SIZE*100},debugBufferUsed:{bytes:h,percentage:h/this.#e.DEBUG_BUFFER_SIZE*100}}}#k(e,t=1){if(!this.#f)return;let r={mainMessagesSent:22,mainBytesSent:23,preschedulerBypassed:25};Atomics.add(this.#f,r[e],t)}#x(){let e=performance.now(),t=this.#X()||{},r=this.#ee();if(r&&Object.assign(t,r),t.driftOffsetMs=this.getDriftOffset(),t.audioContextState=this.#r?.state||"unknown",this.#s){let o=this.#s.getStats();t.bufferPoolUsedBytes=o.used,t.bufferPoolAvailableBytes=o.available,t.bufferPoolAllocations=o.allocations}t.loadedSynthDefs=this.loadedSynthDefs?.size||0;let i=performance.now()-e;return i>1&&console.warn(`[SuperSonic] Slow metrics gathering: ${i.toFixed(2)}ms`),t}#N(){this.#P();let e=this.#F;this.#R=setInterval(()=>{if(this.onMetricsUpdate){if(this.#I){console.warn(`[SuperSonic] Metrics gathering took >${e}ms, skipping this interval`);return}this.#I=!0;try{let t=this.#x();this.onMetricsUpdate(t)}catch(t){console.error("[SuperSonic] Metrics gathering failed:",t)}finally{this.#I=!1}}},e)}#P(){this.#R&&(clearInterval(this.#R),this.#R=null)}getMetrics(){return this.#x()}setMetricsInterval(e){this.#F=e,this.#N()}stopMetricsPolling(){this.#P()}async send(e,...t){this.#A("send OSC messages");let r=t.map(a=>{if(typeof a=="string")return{type:"s",value:a};if(typeof a=="number")return{type:Number.isInteger(a)?"i":"f",value:a};if(a instanceof Uint8Array||a instanceof ArrayBuffer)return{type:"b",value:a instanceof ArrayBuffer?new Uint8Array(a):a};throw new Error(`Unsupported argument type: ${typeof a}`)}),i={address:e,args:r},o=s.osc.encode(i);return this.sendOSC(o)}#A(e="perform this operation"){if(!this.#a)throw new Error(`SuperSonic not initialized. Call init() before attempting to ${e}.`)}#te(){if(!this.#t||!this.#i||!this.#e){console.warn("[SuperSonic] Cannot initialize direct write views - missing buffer info");return}this.#m=new Int32Array(this.#t),this.#b=new DataView(this.#t),this.#B=new Uint8Array(this.#t);let e=this.#i+this.#e.METRICS_START;this.#f=new Uint32Array(this.#t,e,this.#e.METRICS_SIZE/4);let t=this.#e.CONTROL_START;this.#_={IN_HEAD:(this.#i+t+0)/4,IN_TAIL:(this.#i+t+4)/4,IN_SEQUENCE:(this.#i+t+24)/4,IN_WRITE_LOCK:(this.#i+t+40)/4}}#re(e){return e.length>=8&&e[0]===35}#ie(e){return!this.#m||!this.#_?!1:X({atomicView:this.#m,dataView:this.#b,uint8View:this.#B,bufferConstants:this.#e,ringBufferBase:this.#i,controlIndices:this.#_,oscMessage:e})}async sendOSC(e,t={}){this.#A("send OSC data");let r=this.#oe(e),i=await this.#ae(r);if(this.#k("mainMessagesSent"),this.#k("mainBytesSent",i.length),this.onMessageSent&&this.onMessageSent(i),!this.#re(i)&&this.#ie(i)){this.#k("preschedulerBypassed");return}let o=this.#me(i),a={...t};o&&(a.audioTimeS=o.audioTimeS,a.currentTimeS=o.currentTimeS),this.#o.send(i,a)}get audioContext(){return this.#r}get workletNode(){return this.#n}get osc(){return this.#o}getInfo(){return this.#A("get info"),{sampleRate:this.#r.sampleRate,numBuffers:this.config.worldOptions.numBuffers,totalMemory:this.config.memory.totalMemory,wasmHeapSize:this.config.memory.wasmHeapSize,bufferPoolSize:this.config.memory.bufferPoolSize,bootTimeMs:this.bootStats.initDuration,capabilities:{...this.#u}}}async destroy(){this.#z(),this.#P(),this.#o&&(this.#o.terminate(),this.#o=null),this.#n&&(this.#n.disconnect(),this.#n=null),this.#r&&(await this.#r.close(),this.#r=null),this.#s&&(this.#s.destroy(),this.#s=null),this.#t=null,this.#a=!1,this.loadedSynthDefs.clear()}waitForTimeSync(){return this.#A("wait for time sync"),new Float64Array(this.#t,this.#i+this.#e.NTP_START_TIME_START,1)[0]}async loadSample(e,t,r=0,i=0){this.#A("load samples");let o;if(this.#L(t))o=t;else{if(!this.#p)throw new Error("sampleBaseURL not configured. Either provide a full path or set sampleBaseURL in constructor options.");o=`${this.#p}${t}`}let a=await this.#O().prepareFromFile({bufnum:e,path:o,startFrame:r,numFrames:i});return await this.send("/b_allocPtr",e,a.ptr,a.numFrames,a.numChannels,a.sampleRate,a.uuid),a.allocationComplete}#L(e){return e.includes("/")||e.includes("://")}async loadSynthDef(e){if(!this.#a)throw new Error("SuperSonic not initialized. Call init() first.");let t;if(this.#L(e))t=e;else{if(!this.#g)throw new Error("synthdefBaseURL not configured. Either provide a full path or set synthdefBaseURL in constructor options.");t=`${this.#g}${e}.scsyndef`}try{let r=await fetch(t);if(!r.ok)throw new Error(`Failed to load synthdef from ${t}: ${r.status} ${r.statusText}`);let i=await r.arrayBuffer(),o=new Uint8Array(i);await this.send("/d_recv",o);let a=this.#ne(t);a&&this.loadedSynthDefs.add(a)}catch(r){throw console.error("[SuperSonic] Failed to load synthdef:",r),r}}async loadSynthDefs(e){if(!this.#a)throw new Error("SuperSonic not initialized. Call init() first.");let t={};await Promise.all(e.map(async i=>{try{await this.loadSynthDef(i),t[i]={success:!0}}catch(o){console.error(`[SuperSonic] Failed to load ${i}:`,o),t[i]={success:!1,error:o.message}}}));let r=Object.values(t).filter(i=>i.success).length;return t}async sync(e){if(!this.#a)throw new Error("SuperSonic not initialized. Call init() first.");if(!Number.isInteger(e))throw new Error("sync() requires an integer syncId parameter");let t=new Promise((r,i)=>{let o=setTimeout(()=>{this.#c&&this.#c.delete(e),i(new Error("Timeout waiting for /synced response"))},1e4),a=l=>{clearTimeout(o),this.#c.delete(e),r()};this.#c||(this.#c=new Map),this.#c.set(e,a)});await this.send("/sync",e),await t}#Se(){return this.#s?.getStats()}async initializeNTPTiming(){if(!this.#e||!this.#r)return;let e;for(;e=this.#r.getOutputTimestamp(),!(e.contextTime>0);)await new Promise(a=>setTimeout(a,50));let i=(performance.timeOrigin+e.performanceTime)/1e3+2208988800-e.contextTime,o=new Float64Array(this.#t,this.#i+this.#e.NTP_START_TIME_START,1);o[0]=i,this.#w=i}updateDriftOffset(){if(!this.#e||!this.#r||this.#w===void 0)return;let e=this.#r.getOutputTimestamp(),o=(performance.timeOrigin+e.performanceTime)/1e3+2208988800-this.#w-e.contextTime,a=Math.round(o*1e3),l=new Int32Array(this.#t,this.#i+this.#e.DRIFT_OFFSET_START,1);Atomics.store(l,0,a)}getDriftOffset(){if(!this.#e)return 0;let e=new Int32Array(this.#t,this.#i+this.#e.DRIFT_OFFSET_START,1);return Atomics.load(e,0)}#se(){this.#z(),this.#l=setInterval(()=>{this.updateDriftOffset()},15e3)}#z(){this.#l&&(clearInterval(this.#l),this.#l=null)}#ne(e){return!e||typeof e!="string"?null:(e.split("/").filter(Boolean).pop()||e).replace(/\.scsyndef$/i,"")}#oe(e){if(e instanceof Uint8Array)return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);throw new Error("oscData must be ArrayBuffer or Uint8Array")}async#ae(e){let t={metadata:!0,unpackSingleArgs:!1};try{let r=s.osc.decode(e,t),{packet:i,changed:o}=await this.#$(r);return o?s.osc.encode(i):e}catch(r){throw console.error("[SuperSonic] Failed to prepare OSC packet:",r),r}}async#$(e){if(e&&e.address){let{message:t,changed:r}=await this.#le(e);return{packet:t,changed:r}}if(this.#pe(e)){let t=await Promise.all(e.packets.map(o=>this.#$(o)));if(!t.some(o=>o.changed))return{packet:e,changed:!1};let i=t.map(o=>o.packet);return{packet:{timeTag:e.timeTag,packets:i},changed:!0}}return{packet:e,changed:!1}}async#le(e){switch(e.address){case"/b_alloc":return{message:await this.#fe(e),changed:!0};case"/b_allocRead":return{message:await this.#ce(e),changed:!0};case"/b_allocReadChannel":return{message:await this.#ue(e),changed:!0};default:return{message:e,changed:!1}}}async#ce(e){let t=this.#O(),r=this.#v(e.args,0,"/b_allocRead requires a buffer number"),i=this.#W(e.args,1,"/b_allocRead requires a file path"),o=this.#T(e.args,2,0),a=this.#T(e.args,3,0),l=await t.prepareFromFile({bufnum:r,path:i,startFrame:o,numFrames:a});return this.#U(l.allocationComplete,`/b_allocRead ${r}`),this.#C(r,l)}async#ue(e){let t=this.#O(),r=this.#v(e.args,0,"/b_allocReadChannel requires a buffer number"),i=this.#W(e.args,1,"/b_allocReadChannel requires a file path"),o=this.#T(e.args,2,0),a=this.#T(e.args,3,0),l=[];for(let u=4;u<(e.args?.length||0)&&this.#D(e.args[u]);u++)l.push(Math.floor(this.#E(e.args[u])));let c=await t.prepareFromFile({bufnum:r,path:i,startFrame:o,numFrames:a,channels:l.length>0?l:null});return this.#U(c.allocationComplete,`/b_allocReadChannel ${r}`),this.#C(r,c)}async#fe(e){let t=this.#O(),r=this.#v(e.args,0,"/b_alloc requires a buffer number"),i=this.#v(e.args,1,"/b_alloc requires a frame count"),o=2,a=1,l=this.#r?.sampleRate||44100;this.#D(this.#S(e.args,o))&&(a=Math.max(1,this.#T(e.args,o,1)),o++),this.#S(e.args,o)?.type==="b"&&o++,this.#D(this.#S(e.args,o))&&(l=this.#E(this.#S(e.args,o)));let c=await t.prepareEmpty({bufnum:r,numFrames:i,numChannels:a,sampleRate:l});return this.#U(c.allocationComplete,`/b_alloc ${r}`),this.#C(r,c)}#C(e,t){return{address:"/b_allocPtr",args:[this.#M(e),this.#M(t.ptr),this.#M(t.numFrames),this.#M(t.numChannels),this.#he(t.sampleRate),this.#de(t.uuid)]}}#M(e){return{type:"i",value:Math.floor(e)}}#he(e){return{type:"f",value:e}}#de(e){return{type:"s",value:String(e)}}#S(e,t){if(Array.isArray(e))return e[t]}#E(e){if(e!=null)return typeof e=="object"&&Object.prototype.hasOwnProperty.call(e,"value")?e.value:e}#v(e,t,r){let i=this.#E(this.#S(e,t));if(!Number.isFinite(i))throw new Error(r);return Math.floor(i)}#T(e,t,r=0){let i=this.#E(this.#S(e,t));return Number.isFinite(i)?Math.floor(i):r}#W(e,t,r){let i=this.#E(this.#S(e,t));if(typeof i!="string")throw new Error(r);return i}#D(e){if(!e)return!1;let t=this.#E(e);return Number.isFinite(t)}#U(e,t){!e||typeof e.catch!="function"||e.catch(r=>{console.error(`[SuperSonic] ${t} allocation failed:`,r)})}#O(){if(!this.#s)throw new Error("Buffer manager not ready. Call init() before issuing buffer commands.");return this.#s}#pe(e){return e&&e.timeTag!==void 0&&Array.isArray(e.packets)}#me(e){if(e.length<16||String.fromCharCode.apply(null,e.slice(0,8))!=="#bundle\0")return null;let i=new Float64Array(this.#t,this.#i+this.#e.NTP_START_TIME_START,1)[0];if(i===0)return console.warn("[SuperSonic] NTP start time not yet initialized"),null;let o=new Int32Array(this.#t,this.#i+this.#e.DRIFT_OFFSET_START,1),l=Atomics.load(o,0)/1e3,c=new Int32Array(this.#t,this.#i+this.#e.GLOBAL_OFFSET_START,1),f=Atomics.load(c,0)/1e3,h=i+l+f,S=new DataView(e.buffer,e.byteOffset),m=S.getUint32(8,!1),d=S.getUint32(12,!1);if(m===0&&(d===0||d===1))return null;let p=m+d/4294967296-h,E=this.#r.currentTime;return{audioTimeS:p,currentTimeS:E}}};export{se as SuperSonic};
|
|
17
|
+
3. Browser security settings`),r}return this.#u}#V(){let e=this.config.memory;this.#d=new WebAssembly.Memory({initial:e.totalPages,maximum:e.totalPages,shared:!0}),this.#t=this.#d.buffer}#H(){return this.#r=new AudioContext(this.config.audioContextOptions),this.#r}#G(){this.#s=new C({audioContext:this.#r,sharedBuffer:this.#t,bufferPoolConfig:{start:this.config.memory.bufferPoolOffset,size:this.config.memory.bufferPoolSize},sampleBaseURL:this.#p,audioPathMap:this.#y,maxBuffers:this.config.worldOptions.numBuffers})}async#Z(){let e=this.config.wasmBaseURL+"manifest.json";try{let t=await fetch(e);if(!t.ok)return;let r=await t.json();this.config.wasmUrl=this.config.wasmBaseURL+r.wasmFile}catch{}}async#q(){this.config.development&&await this.#Z();let e=await fetch(this.config.wasmUrl);if(!e.ok)throw new Error(`Failed to load WASM: ${e.status} ${e.statusText}`);return await e.arrayBuffer()}async#Y(e){await this.#r.audioWorklet.addModule(this.config.workletUrl),this.#n=new AudioWorkletNode(this.#r,"scsynth-processor",{numberOfInputs:0,numberOfOutputs:1,outputChannelCount:[2]}),this.#n.connect(this.#r.destination),this.#n.port.postMessage({type:"init",sharedBuffer:this.#t}),this.#n.port.postMessage({type:"loadWasm",wasmBytes:e,wasmMemory:this.#d,worldOptions:this.config.worldOptions,sampleRate:this.#r.sampleRate}),await this.#K()}async#Q(){this.#o=new R(this.config.workerBaseURL),this.#o.onRawOSC(e=>{this.onOSC&&this.onOSC(e)}),this.#o.onParsedOSC(e=>{if(e.address==="/buffer/freed")this.#s?.handleBufferFreed(e.args);else if(e.address==="/buffer/allocated")this.#s?.handleBufferAllocated(e.args);else if(e.address==="/synced"&&e.args.length>0){let t=e.args[0];this.#c&&this.#c.has(t)&&this.#c.get(t)(e)}this.onMessage&&this.onMessage(e)}),this.#o.onDebugMessage(e=>{this.onDebugMessage&&this.onDebugMessage(e)}),this.#o.onError((e,t)=>{console.error(`[SuperSonic] ${t} error:`,e),this.onError&&this.onError(new Error(`${t}: ${e}`))}),await this.#o.init(this.#t,this.#i,this.#e)}#j(){this.#a=!0,this.#h=!1,this.bootStats.initDuration=performance.now()-this.bootStats.initStartTime,this.onInitialized&&this.onInitialized({capabilities:this.#u,bootStats:this.bootStats})}async init(e={}){if(this.#a){console.warn("[SuperSonic] Already initialized");return}if(this.#h){console.warn("[SuperSonic] Initialization already in progress");return}this.config={...this.config,...e,audioContextOptions:{...this.config.audioContextOptions,...e.audioContextOptions||{}}},this.#h=!0,this.bootStats.initStartTime=performance.now();try{this.setAndValidateCapabilities(),this.#V(),this.#H(),this.#G();let t=await this.#q();await this.#Y(t),await this.#Q(),this.#J(),this.#N(),this.#j()}catch(t){throw this.#h=!1,console.error("[SuperSonic] Initialization failed:",t),this.onError&&this.onError(t),t}}#K(){return new Promise((e,t)=>{let r=setTimeout(()=>{t(new Error("AudioWorklet initialization timeout"))},5e3),i=async o=>{if(o.data.type!=="debug"){if(o.data.type==="error"){console.error("[AudioWorklet] Error:",o.data.error),clearTimeout(r),this.#n.port.removeEventListener("message",i),t(new Error(o.data.error||"AudioWorklet error"));return}o.data.type==="initialized"&&(clearTimeout(r),this.#n.port.removeEventListener("message",i),o.data.success?(o.data.ringBufferBase!==void 0?this.#i=o.data.ringBufferBase:console.warn("[SuperSonic] Warning: ringBufferBase not provided by worklet"),o.data.bufferConstants!==void 0?(this.#e=o.data.bufferConstants,this.#te(),await this.initializeNTPTiming(),this.#se()):console.warn("[SuperSonic] Warning: bufferConstants not provided by worklet"),e()):t(new Error(o.data.error||"AudioWorklet initialization failed")))}};this.#n.port.addEventListener("message",i),this.#n.port.start()})}#J(){this.#n.port.onmessage=e=>{let{data:t}=e;switch(t.type){case"error":console.error("[Worklet] Error:",t.error),t.diagnostics&&(console.error("[Worklet] Diagnostics:",t.diagnostics),console.table(t.diagnostics)),this.onError&&this.onError(new Error(t.error));break;case"process_debug":break;case"debug":break;case"console":this.onConsoleMessage&&this.onConsoleMessage(t.message);break;case"version":this.onVersion&&this.onVersion(t.version);break}}}#X(){if(!this.#f)return null;let e=this.#f;return{workletProcessCount:e[0],workletMessagesProcessed:e[1],workletMessagesDropped:e[2],workletSchedulerDepth:e[3],workletSchedulerMax:e[4],workletSchedulerDropped:e[5],workletSequenceGaps:e[24],preschedulerPending:e[6],preschedulerPeak:e[7],preschedulerSent:e[8],preschedulerRetriesSucceeded:e[9],preschedulerRetriesFailed:e[10],preschedulerBundlesScheduled:e[11],preschedulerEventsCancelled:e[12],preschedulerTotalDispatches:e[13],preschedulerMessagesRetried:e[14],preschedulerRetryQueueSize:e[15],preschedulerRetryQueueMax:e[16],preschedulerBypassed:e[25],oscInMessagesReceived:e[17],oscInMessagesDropped:e[18],oscInBytesReceived:e[19],debugMessagesReceived:e[20],debugBytesReceived:e[21],mainMessagesSent:e[22],mainBytesSent:e[23]}}#ee(){if(!this.#m||!this.#e||!this.#i)return null;let e=this.#i+this.#e.CONTROL_START,t=this.#m,r=Atomics.load(t,(e+0)/4),i=Atomics.load(t,(e+4)/4),o=Atomics.load(t,(e+8)/4),a=Atomics.load(t,(e+12)/4),l=Atomics.load(t,(e+16)/4),c=Atomics.load(t,(e+20)/4),u=(r-i+this.#e.IN_BUFFER_SIZE)%this.#e.IN_BUFFER_SIZE,f=(o-a+this.#e.OUT_BUFFER_SIZE)%this.#e.OUT_BUFFER_SIZE,h=(l-c+this.#e.DEBUG_BUFFER_SIZE)%this.#e.DEBUG_BUFFER_SIZE;return{inBufferUsed:{bytes:u,percentage:u/this.#e.IN_BUFFER_SIZE*100},outBufferUsed:{bytes:f,percentage:f/this.#e.OUT_BUFFER_SIZE*100},debugBufferUsed:{bytes:h,percentage:h/this.#e.DEBUG_BUFFER_SIZE*100}}}#k(e,t=1){if(!this.#f)return;let r={mainMessagesSent:22,mainBytesSent:23,preschedulerBypassed:25};Atomics.add(this.#f,r[e],t)}#x(){let e=performance.now(),t=this.#X()||{},r=this.#ee();if(r&&Object.assign(t,r),t.driftOffsetMs=this.getDriftOffset(),t.audioContextState=this.#r?.state||"unknown",this.#s){let o=this.#s.getStats();t.bufferPoolUsedBytes=o.used,t.bufferPoolAvailableBytes=o.available,t.bufferPoolAllocations=o.allocations}t.loadedSynthDefs=this.loadedSynthDefs?.size||0;let i=performance.now()-e;return i>1&&console.warn(`[SuperSonic] Slow metrics gathering: ${i.toFixed(2)}ms`),t}#N(){this.#P();let e=this.#F;this.#R=setInterval(()=>{if(this.onMetricsUpdate){if(this.#I){console.warn(`[SuperSonic] Metrics gathering took >${e}ms, skipping this interval`);return}this.#I=!0;try{let t=this.#x();this.onMetricsUpdate(t)}catch(t){console.error("[SuperSonic] Metrics gathering failed:",t)}finally{this.#I=!1}}},e)}#P(){this.#R&&(clearInterval(this.#R),this.#R=null)}getMetrics(){return this.#x()}setMetricsInterval(e){this.#F=e,this.#N()}stopMetricsPolling(){this.#P()}async send(e,...t){this.#A("send OSC messages");let r=t.map(a=>{if(typeof a=="string")return{type:"s",value:a};if(typeof a=="number")return{type:Number.isInteger(a)?"i":"f",value:a};if(a instanceof Uint8Array||a instanceof ArrayBuffer)return{type:"b",value:a instanceof ArrayBuffer?new Uint8Array(a):a};throw new Error(`Unsupported argument type: ${typeof a}`)}),i={address:e,args:r},o=s.osc.encode(i);return this.sendOSC(o)}#A(e="perform this operation"){if(!this.#a)throw new Error(`SuperSonic not initialized. Call init() before attempting to ${e}.`)}#te(){if(!this.#t||!this.#i||!this.#e){console.warn("[SuperSonic] Cannot initialize direct write views - missing buffer info");return}this.#m=new Int32Array(this.#t),this.#b=new DataView(this.#t),this.#B=new Uint8Array(this.#t);let e=this.#i+this.#e.METRICS_START;this.#f=new Uint32Array(this.#t,e,this.#e.METRICS_SIZE/4);let t=this.#e.CONTROL_START;this.#_={IN_HEAD:(this.#i+t+0)/4,IN_TAIL:(this.#i+t+4)/4,IN_SEQUENCE:(this.#i+t+24)/4,IN_WRITE_LOCK:(this.#i+t+40)/4}}#re(e){return e.length>=8&&e[0]===35}#ie(e){return!this.#m||!this.#_?!1:X({atomicView:this.#m,dataView:this.#b,uint8View:this.#B,bufferConstants:this.#e,ringBufferBase:this.#i,controlIndices:this.#_,oscMessage:e})}async sendOSC(e,t={}){this.#A("send OSC data");let r=this.#oe(e),i=await this.#ae(r);if(this.#k("mainMessagesSent"),this.#k("mainBytesSent",i.length),this.onMessageSent&&this.onMessageSent(i),!this.#re(i)&&this.#ie(i)){this.#k("preschedulerBypassed");return}let o=this.#me(i),a={...t};o&&(a.audioTimeS=o.audioTimeS,a.currentTimeS=o.currentTimeS),this.#o.send(i,a)}get audioContext(){return this.#r}get workletNode(){return this.#n}get osc(){return this.#o}getInfo(){return this.#A("get info"),{sampleRate:this.#r.sampleRate,numBuffers:this.config.worldOptions.numBuffers,totalMemory:this.config.memory.totalMemory,wasmHeapSize:this.config.memory.wasmHeapSize,bufferPoolSize:this.config.memory.bufferPoolSize,bootTimeMs:this.bootStats.initDuration,capabilities:{...this.#u}}}async destroy(){this.#z(),this.#P(),this.#o&&(this.#o.terminate(),this.#o=null),this.#n&&(this.#n.disconnect(),this.#n=null),this.#r&&(await this.#r.close(),this.#r=null),this.#s&&(this.#s.destroy(),this.#s=null),this.#t=null,this.#a=!1,this.loadedSynthDefs.clear()}waitForTimeSync(){return this.#A("wait for time sync"),new Float64Array(this.#t,this.#i+this.#e.NTP_START_TIME_START,1)[0]}async loadSample(e,t,r=0,i=0){this.#A("load samples");let o;if(this.#L(t))o=t;else{if(!this.#p)throw new Error("sampleBaseURL not configured. Either provide a full path or set sampleBaseURL in constructor options.");o=`${this.#p}${t}`}let a=await this.#O().prepareFromFile({bufnum:e,path:o,startFrame:r,numFrames:i});return await this.send("/b_allocPtr",e,a.ptr,a.numFrames,a.numChannels,a.sampleRate,a.uuid),a.allocationComplete}#L(e){return e.includes("/")||e.includes("://")}async loadSynthDef(e){if(!this.#a)throw new Error("SuperSonic not initialized. Call init() first.");let t;if(this.#L(e))t=e;else{if(!this.#g)throw new Error("synthdefBaseURL not configured. Either provide a full path or set synthdefBaseURL in constructor options.");t=`${this.#g}${e}.scsyndef`}try{let r=await fetch(t);if(!r.ok)throw new Error(`Failed to load synthdef from ${t}: ${r.status} ${r.statusText}`);let i=await r.arrayBuffer(),o=new Uint8Array(i);await this.send("/d_recv",o);let a=this.#ne(t);a&&this.loadedSynthDefs.add(a)}catch(r){throw console.error("[SuperSonic] Failed to load synthdef:",r),r}}async loadSynthDefs(e){if(!this.#a)throw new Error("SuperSonic not initialized. Call init() first.");let t={};await Promise.all(e.map(async i=>{try{await this.loadSynthDef(i),t[i]={success:!0}}catch(o){console.error(`[SuperSonic] Failed to load ${i}:`,o),t[i]={success:!1,error:o.message}}}));let r=Object.values(t).filter(i=>i.success).length;return t}async sync(e=Math.floor(Math.random()*2147483647)){if(!this.#a)throw new Error("SuperSonic not initialized. Call init() first.");let t=new Promise((r,i)=>{let o=setTimeout(()=>{this.#c&&this.#c.delete(e),i(new Error("Timeout waiting for /synced response"))},1e4),a=l=>{clearTimeout(o),this.#c.delete(e),r()};this.#c||(this.#c=new Map),this.#c.set(e,a)});await this.send("/sync",e),await t}#Se(){return this.#s?.getStats()}async initializeNTPTiming(){if(!this.#e||!this.#r)return;let e;for(;e=this.#r.getOutputTimestamp(),!(e.contextTime>0);)await new Promise(a=>setTimeout(a,50));let i=(performance.timeOrigin+e.performanceTime)/1e3+2208988800-e.contextTime,o=new Float64Array(this.#t,this.#i+this.#e.NTP_START_TIME_START,1);o[0]=i,this.#w=i}updateDriftOffset(){if(!this.#e||!this.#r||this.#w===void 0)return;let e=this.#r.getOutputTimestamp(),o=(performance.timeOrigin+e.performanceTime)/1e3+2208988800-this.#w-e.contextTime,a=Math.round(o*1e3),l=new Int32Array(this.#t,this.#i+this.#e.DRIFT_OFFSET_START,1);Atomics.store(l,0,a)}getDriftOffset(){if(!this.#e)return 0;let e=new Int32Array(this.#t,this.#i+this.#e.DRIFT_OFFSET_START,1);return Atomics.load(e,0)}#se(){this.#z(),this.#l=setInterval(()=>{this.updateDriftOffset()},15e3)}#z(){this.#l&&(clearInterval(this.#l),this.#l=null)}#ne(e){return!e||typeof e!="string"?null:(e.split("/").filter(Boolean).pop()||e).replace(/\.scsyndef$/i,"")}#oe(e){if(e instanceof Uint8Array)return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);throw new Error("oscData must be ArrayBuffer or Uint8Array")}async#ae(e){let t={metadata:!0,unpackSingleArgs:!1};try{let r=s.osc.decode(e,t),{packet:i,changed:o}=await this.#$(r);return o?s.osc.encode(i):e}catch(r){throw console.error("[SuperSonic] Failed to prepare OSC packet:",r),r}}async#$(e){if(e&&e.address){let{message:t,changed:r}=await this.#le(e);return{packet:t,changed:r}}if(this.#pe(e)){let t=await Promise.all(e.packets.map(o=>this.#$(o)));if(!t.some(o=>o.changed))return{packet:e,changed:!1};let i=t.map(o=>o.packet);return{packet:{timeTag:e.timeTag,packets:i},changed:!0}}return{packet:e,changed:!1}}async#le(e){switch(e.address){case"/b_alloc":return{message:await this.#fe(e),changed:!0};case"/b_allocRead":return{message:await this.#ce(e),changed:!0};case"/b_allocReadChannel":return{message:await this.#ue(e),changed:!0};default:return{message:e,changed:!1}}}async#ce(e){let t=this.#O(),r=this.#v(e.args,0,"/b_allocRead requires a buffer number"),i=this.#W(e.args,1,"/b_allocRead requires a file path"),o=this.#T(e.args,2,0),a=this.#T(e.args,3,0),l=await t.prepareFromFile({bufnum:r,path:i,startFrame:o,numFrames:a});return this.#U(l.allocationComplete,`/b_allocRead ${r}`),this.#C(r,l)}async#ue(e){let t=this.#O(),r=this.#v(e.args,0,"/b_allocReadChannel requires a buffer number"),i=this.#W(e.args,1,"/b_allocReadChannel requires a file path"),o=this.#T(e.args,2,0),a=this.#T(e.args,3,0),l=[];for(let u=4;u<(e.args?.length||0)&&this.#D(e.args[u]);u++)l.push(Math.floor(this.#E(e.args[u])));let c=await t.prepareFromFile({bufnum:r,path:i,startFrame:o,numFrames:a,channels:l.length>0?l:null});return this.#U(c.allocationComplete,`/b_allocReadChannel ${r}`),this.#C(r,c)}async#fe(e){let t=this.#O(),r=this.#v(e.args,0,"/b_alloc requires a buffer number"),i=this.#v(e.args,1,"/b_alloc requires a frame count"),o=2,a=1,l=this.#r?.sampleRate||44100;this.#D(this.#S(e.args,o))&&(a=Math.max(1,this.#T(e.args,o,1)),o++),this.#S(e.args,o)?.type==="b"&&o++,this.#D(this.#S(e.args,o))&&(l=this.#E(this.#S(e.args,o)));let c=await t.prepareEmpty({bufnum:r,numFrames:i,numChannels:a,sampleRate:l});return this.#U(c.allocationComplete,`/b_alloc ${r}`),this.#C(r,c)}#C(e,t){return{address:"/b_allocPtr",args:[this.#M(e),this.#M(t.ptr),this.#M(t.numFrames),this.#M(t.numChannels),this.#he(t.sampleRate),this.#de(t.uuid)]}}#M(e){return{type:"i",value:Math.floor(e)}}#he(e){return{type:"f",value:e}}#de(e){return{type:"s",value:String(e)}}#S(e,t){if(Array.isArray(e))return e[t]}#E(e){if(e!=null)return typeof e=="object"&&Object.prototype.hasOwnProperty.call(e,"value")?e.value:e}#v(e,t,r){let i=this.#E(this.#S(e,t));if(!Number.isFinite(i))throw new Error(r);return Math.floor(i)}#T(e,t,r=0){let i=this.#E(this.#S(e,t));return Number.isFinite(i)?Math.floor(i):r}#W(e,t,r){let i=this.#E(this.#S(e,t));if(typeof i!="string")throw new Error(r);return i}#D(e){if(!e)return!1;let t=this.#E(e);return Number.isFinite(t)}#U(e,t){!e||typeof e.catch!="function"||e.catch(r=>{console.error(`[SuperSonic] ${t} allocation failed:`,r)})}#O(){if(!this.#s)throw new Error("Buffer manager not ready. Call init() before issuing buffer commands.");return this.#s}#pe(e){return e&&e.timeTag!==void 0&&Array.isArray(e.packets)}#me(e){if(e.length<16||String.fromCharCode.apply(null,e.slice(0,8))!=="#bundle\0")return null;let i=new Float64Array(this.#t,this.#i+this.#e.NTP_START_TIME_START,1)[0];if(i===0)return console.warn("[SuperSonic] NTP start time not yet initialized"),null;let o=new Int32Array(this.#t,this.#i+this.#e.DRIFT_OFFSET_START,1),l=Atomics.load(o,0)/1e3,c=new Int32Array(this.#t,this.#i+this.#e.GLOBAL_OFFSET_START,1),f=Atomics.load(c,0)/1e3,h=i+l+f,S=new DataView(e.buffer,e.byteOffset),m=S.getUint32(8,!1),d=S.getUint32(12,!1);if(m===0&&(d===0||d===1))return null;let p=m+d/4294967296-h,E=this.#r.currentTime;return{audioTimeS:p,currentTimeS:E}}};export{se as SuperSonic};
|
|
18
18
|
/*! osc.js 2.4.5, Copyright 2024 Colin Clark | github.com/colinbdclark/osc.js */
|
package/dist/wasm/manifest.json
CHANGED
|
Binary file
|
package/package.json
CHANGED