supersonic-scsynth 0.68.1 → 0.70.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.
@@ -1 +1 @@
1
- (()=>{function O(E,e,s){return(s-1-E+e)%s}function D({uint8View:E,dataView:e,bufferStart:s,bufferSize:_,head:i,payload:A,sequence:l,messageMagic:U,headerSize:C,sourceId:f=0,headerScratch:r=null,headerScratchView:p=null}){let c=A.length,I=C+c+3&-4,a=_-i;if(I>a){let o=r||new Uint8Array(C),n=p||new DataView(o.buffer);n.setUint32(0,U,!0),n.setUint32(4,I,!0),n.setUint32(8,l,!0),n.setUint32(12,f,!0);let t=s+i,S=s;if(a>=C){E.set(o,t);let R=a-C;for(let T=0;T<R;T++)E[t+C+T]=A[T];for(let T=R;T<c;T++)E[S+T-R]=A[T]}else{for(let T=0;T<a;T++)E[t+T]=o[T];for(let T=a;T<C;T++)E[S+T-a]=o[T];let R=C-a;E.set(A,S+R)}}else{let o=s+i;e.setUint32(o,U,!0),e.setUint32(o+4,I,!0),e.setUint32(o+8,l,!0),e.setUint32(o+12,f,!0),E.set(A,o+C)}return(i+I)%_}function d({uint8View:E,dataView:e,bufferStart:s,bufferSize:_,head:i,tail:A,messageMagic:l,paddingMagic:U,headerSize:C,maxMessages:f=1/0,onMessage:r,onCorruption:p}){let c=A,L=0,I=a=>{let o=a;if(o+4<=_)return e.getUint32(s+o,!0);let n=0;for(let t=0;t<4;t++)n|=E[s+(o+t)%_]<<t*8;return n};for(;c!==i&&L<f;){let a=_-c,o;if(a>=4?o=e.getUint32(s+c,!0):o=I(c),o===U){c=0;continue}if(o!==l){p&&p(c),c=(c+1)%_;continue}let n=I((c+4)%_),t=I((c+8)%_),S=I((c+12)%_);if(n<C||n>_){p&&p(c),c=(c+1)%_;continue}let R=n-C,T=s+(c+C)%_;r(T,R,t,S),c=(c+n)%_,L++}return{newTail:c,messagesRead:L}}function P(E,e,s,_){let i=E+e+_*s;return{controlBase:i,headIndex:(i+0)/4,tailIndex:(i+4)/4,activeIndex:(i+8)/4,dropsIndex:(i+12)/4}}function B(E,e){let s=E+e;return{OUT_HEAD:(s+8)/4,OUT_TAIL:(s+12)/4}}function F(){return(performance.timeOrigin+performance.now())/1e3+2208988800}function H(E){let{name:e,calculateControlIndices:s,headIndex:_,tailIndex:i,readMessages:A,postResults:l,initMetrics:U=!0,onInit:C,extraHandlers:f}=E,r={sharedBuffer:null,ringBufferBase:null,bufferConstants:null,atomicView:null,dataView:null,uint8View:null,metricsView:null,CONTROL_INDICES:{}},p=!1;function c(o,n,t){if(r.sharedBuffer=o,r.ringBufferBase=n,r.bufferConstants=t,r.atomicView=new Int32Array(o),r.dataView=new DataView(o),r.uint8View=new Uint8Array(o),r.CONTROL_INDICES=s(n,t.CONTROL_START),U){let S=n+t.METRICS_START;r.metricsView=new Uint32Array(o,S,t.METRICS_SIZE/4)}C?.(r)}function L(){let o=_(r.CONTROL_INDICES),n=i(r.CONTROL_INDICES);for(;p;)try{let t=Atomics.load(r.atomicView,o),S=Atomics.load(r.atomicView,n);t===S&&Atomics.wait(r.atomicView,o,t);let R=A(r);R&&R.length>0&&l(R)}catch(t){console.error(`[${e}] Error in wait loop:`,t),self.postMessage({type:"error",error:t.message}),Atomics.wait(r.atomicView,0,r.atomicView[0],10)}}function I(){if(!r.sharedBuffer){console.error(`[${e}] Cannot start - not initialized`);return}p||(p=!0,L())}function a(){p=!1}self.addEventListener("message",o=>{let{data:n}=o;try{if(f?.[n.type]){f[n.type](n,r);return}switch(n.type){case"init":n.sharedBuffer&&c(n.sharedBuffer,n.ringBufferBase,n.bufferConstants),self.postMessage({type:"initialized"});break;case"start":r.sharedBuffer&&I();break;case"stop":a();break;default:}}catch(t){console.error(`[${e}] Error:`,t),self.postMessage({type:"error",error:t.message})}})}var x=-1,u=null,N=null,M=null;function X(E){let{atomicView:e,uint8View:s,dataView:_,ringBufferBase:i,bufferConstants:A,metricsView:l,CONTROL_INDICES:U}=E,C=Atomics.load(e,U.OUT_HEAD),f=Atomics.load(e,U.OUT_TAIL);if(C===f)return[];let r=[],{newTail:p,messagesRead:c}=d({uint8View:s,dataView:_,bufferStart:i+A.OUT_BUFFER_START,bufferSize:A.OUT_BUFFER_SIZE,head:C,tail:f,messageMagic:A.MESSAGE_MAGIC,paddingMagic:A.PADDING_MAGIC,headerSize:A.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(L,I,a,o)=>{if(x>=0){let t=x+1&4294967295;if(a!==t){let S=a-t+4294967296&4294967295;S<1e3&&(console.error("[OSCInWorker] Detected",S,"dropped messages (expected seq",t,"got",a,")"),l&&Atomics.add(l,28,S))}}x=a;let n=new Uint8Array(I);for(let t=0;t<I;t++)n[t]=s[L+t];if(r.push({oscData:n,sequence:a,timestamp:F()}),u)for(let t=0;t<u.length;t++){let S=u[t];if(Atomics.load(e,S.activeIndex)!==1)continue;let R=Atomics.load(e,S.headIndex),T=Atomics.load(e,S.tailIndex),g=O(R,T,S.bufferSize);if((A.MESSAGE_HEADER_SIZE+I+3&-4)>g){Atomics.add(e,S.dropsIndex,1);continue}let G=D({uint8View:s,dataView:_,bufferStart:S.bufferStart,bufferSize:S.bufferSize,head:R,payload:n,sequence:a,messageMagic:A.MESSAGE_MAGIC,headerSize:A.MESSAGE_HEADER_SIZE,headerScratch:N,headerScratchView:M});Atomics.store(e,S.headIndex,G),Atomics.notify(e,S.headIndex)}l&&(Atomics.add(l,26,1),Atomics.add(l,27,I))},onCorruption:L=>{console.error("[OSCInWorker] Corrupted message at position",L),l&&(Atomics.add(l,28,1),Atomics.add(l,29,1))}});return c>0&&Atomics.store(e,U.OUT_TAIL,p),r}H({name:"OSCInWorker",calculateControlIndices:B,headIndex:E=>E.OUT_HEAD,tailIndex:E=>E.OUT_TAIL,readMessages:X,postResults:E=>self.postMessage({type:"messages",messages:E}),onInit:E=>{let e=E.bufferConstants;if(!e.REPLY_CHANNEL_COUNT)return;N=new Uint8Array(e.MESSAGE_HEADER_SIZE),M=new DataView(N.buffer);let s=E.ringBufferBase;u=[];for(let _=0;_<e.REPLY_CHANNEL_COUNT;_++){let i=P(s,e.REPLY_CHANNELS_CONTROL_START,e.REPLY_CHANNEL_CONTROL_SIZE,_);u.push({bufferStart:s+e.REPLY_CHANNELS_BUFFER_START+_*e.REPLY_CHANNEL_BUFFER_SIZE,bufferSize:e.REPLY_CHANNEL_BUFFER_SIZE,headIndex:i.headIndex,tailIndex:i.tailIndex,activeIndex:i.activeIndex,dropsIndex:i.dropsIndex})}}});})();
1
+ (()=>{function u({uint8View:c,dataView:r,bufferStart:s,bufferSize:I,head:U,tail:A,messageMagic:T,paddingMagic:R,headerSize:C,maxMessages:f=1/0,onMessage:t,onCorruption:a}){let o=A,O=0,S=()=>{a&&a(o),o=U};for(;o!==U&&O<f;){let i=I-o,_=(U-o+I)%I;if(i<4||_<4){S();break}let e=r.getUint32(s+o,!0);if(e===R){if(o===0){S();break}o=0;continue}if(e!==T){S();break}if(i<C){S();break}let n=r.getUint32(s+o+4,!0),p=r.getUint32(s+o+8,!0),N=r.getUint32(s+o+12,!0),E=n+3&-4;if(n<C||E>I||E>i||E>_){S();break}let l=n-C,d=s+o+C;t(d,l,p,N),o=(o+E)%I,O++}return{newTail:o,messagesRead:O}}function L(c,r){let s=c+r;return{OUT_HEAD:(s+8)/4,OUT_TAIL:(s+12)/4}}function D(){return(performance.timeOrigin+performance.now())/1e3+2208988800}function F(c){let{name:r,calculateControlIndices:s,headIndex:I,tailIndex:U,readMessages:A,postResults:T,initMetrics:R=!0,onInit:C,extraHandlers:f}=c,t={sharedBuffer:null,ringBufferBase:null,bufferConstants:null,atomicView:null,dataView:null,uint8View:null,metricsView:null,CONTROL_INDICES:{}},a=!1;function o(_,e,n){if(t.sharedBuffer=_,t.ringBufferBase=e,t.bufferConstants=n,t.atomicView=new Int32Array(_),t.dataView=new DataView(_),t.uint8View=new Uint8Array(_),t.CONTROL_INDICES=s(e,n.CONTROL_START),R){let p=e+n.METRICS_START;t.metricsView=new Uint32Array(_,p,n.METRICS_SIZE/4)}C?.(t)}function O(){let _=I(t.CONTROL_INDICES),e=U(t.CONTROL_INDICES);for(;a;)try{let n=Atomics.load(t.atomicView,_),p=Atomics.load(t.atomicView,e);n===p&&Atomics.wait(t.atomicView,_,n);let N=A(t);N&&N.length>0&&T(N)}catch(n){console.error(`[${r}] Error in wait loop:`,n),self.postMessage({type:"error",error:n.message}),Atomics.wait(t.atomicView,0,t.atomicView[0],10)}}function S(){if(!t.sharedBuffer){console.error(`[${r}] Cannot start - not initialized`);return}a||(a=!0,O())}function i(){a=!1}self.addEventListener("message",_=>{let{data:e}=_;try{if(f?.[e.type]){f[e.type](e,t);return}switch(e.type){case"init":e.sharedBuffer&&o(e.sharedBuffer,e.ringBufferBase,e.bufferConstants),self.postMessage({type:"initialized"});break;case"start":t.sharedBuffer&&S();break;case"stop":i();break;default:}}catch(n){console.error(`[${r}] Error:`,n),self.postMessage({type:"error",error:n.message})}})}var x=-1;function H(c){let{atomicView:r,uint8View:s,dataView:I,ringBufferBase:U,bufferConstants:A,metricsView:T,CONTROL_INDICES:R}=c,C=Atomics.load(r,R.OUT_HEAD),f=Atomics.load(r,R.OUT_TAIL);if(C===f)return[];let t=[],{newTail:a,messagesRead:o}=u({uint8View:s,dataView:I,bufferStart:U+A.OUT_BUFFER_START,bufferSize:A.OUT_BUFFER_SIZE,head:C,tail:f,messageMagic:A.MESSAGE_MAGIC,paddingMagic:A.PADDING_MAGIC,headerSize:A.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(O,S,i,_)=>{if(x>=0){let E=x+1&4294967295;if(i!==E){let l=i-E+4294967296&4294967295;l<1e3&&(console.error("[OSCInWorker] Detected",l,"dropped messages (expected seq",E,"got",i,")"),T&&Atomics.add(T,13,l))}}x=i;let e=4,n=O+e,p=S-e;if(p<=0)return;let N=new Uint8Array(p);for(let E=0;E<p;E++)N[E]=s[n+E];t.push({oscData:N,sequence:i,timestamp:D()}),T&&(Atomics.add(T,11,1),Atomics.add(T,12,S))},onCorruption:O=>{console.error("[OSCInWorker] Corrupted message at position",O),T&&(Atomics.add(T,13,1),Atomics.add(T,14,1))}});return o>0&&Atomics.store(r,R.OUT_TAIL,a),t}F({name:"OSCInWorker",calculateControlIndices:L,headIndex:c=>c.OUT_HEAD,tailIndex:c=>c.OUT_TAIL,readMessages:H,postResults:c=>self.postMessage({type:"messages",messages:c})});})();
@@ -1 +1 @@
1
- (()=>{function m({uint8View:i,dataView:c,bufferStart:s,bufferSize:a,head:g,tail:E,messageMagic:T,paddingMagic:p,headerSize:u,maxMessages:f=1/0,onMessage:e,onCorruption:_}){let o=E,d=0,L=l=>{let t=l;if(t+4<=a)return c.getUint32(s+t,!0);let n=0;for(let r=0;r<4;r++)n|=i[s+(t+r)%a]<<r*8;return n};for(;o!==g&&d<f;){let l=a-o,t;if(l>=4?t=c.getUint32(s+o,!0):t=L(o),t===p){o=0;continue}if(t!==T){_&&_(o),o=(o+1)%a;continue}let n=L((o+4)%a),r=L((o+8)%a),A=L((o+12)%a);if(n<u||n>a){_&&_(o),o=(o+1)%a;continue}let I=n-u,S=s+(o+u)%a;e(S,I,r,A),o=(o+n)%a,d++}return{newTail:o,messagesRead:d}}function O(i,c){let s=i+c;return{IN_HEAD:(s+0)/4,IN_TAIL:(s+4)/4,IN_SEQUENCE:(s+24)/4,IN_WRITE_LOCK:(s+40)/4,IN_LOG_TAIL:(s+44)/4}}function C(){return(performance.timeOrigin+performance.now())/1e3+2208988800}function U(i){let{name:c,calculateControlIndices:s,headIndex:a,tailIndex:g,readMessages:E,postResults:T,initMetrics:p=!0,onInit:u,extraHandlers:f}=i,e={sharedBuffer:null,ringBufferBase:null,bufferConstants:null,atomicView:null,dataView:null,uint8View:null,metricsView:null,CONTROL_INDICES:{}},_=!1;function o(t,n,r){if(e.sharedBuffer=t,e.ringBufferBase=n,e.bufferConstants=r,e.atomicView=new Int32Array(t),e.dataView=new DataView(t),e.uint8View=new Uint8Array(t),e.CONTROL_INDICES=s(n,r.CONTROL_START),p){let A=n+r.METRICS_START;e.metricsView=new Uint32Array(t,A,r.METRICS_SIZE/4)}u?.(e)}function d(){let t=a(e.CONTROL_INDICES),n=g(e.CONTROL_INDICES);for(;_;)try{let r=Atomics.load(e.atomicView,t),A=Atomics.load(e.atomicView,n);r===A&&Atomics.wait(e.atomicView,t,r);let I=E(e);I&&I.length>0&&T(I)}catch(r){console.error(`[${c}] Error in wait loop:`,r),self.postMessage({type:"error",error:r.message}),Atomics.wait(e.atomicView,0,e.atomicView[0],10)}}function L(){if(!e.sharedBuffer){console.error(`[${c}] Cannot start - not initialized`);return}_||(_=!0,d())}function l(){_=!1}self.addEventListener("message",t=>{let{data:n}=t;try{if(f?.[n.type]){f[n.type](n,e);return}switch(n.type){case"init":n.sharedBuffer&&o(n.sharedBuffer,n.ringBufferBase,n.bufferConstants),self.postMessage({type:"initialized"});break;case"start":e.sharedBuffer&&L();break;case"stop":l();break;default:}}catch(r){console.error(`[${c}] Error:`,r),self.postMessage({type:"error",error:r.message})}})}var N=0;function x(i){let{atomicView:c,uint8View:s,dataView:a,ringBufferBase:g,bufferConstants:E,CONTROL_INDICES:T}=i,p=Atomics.load(c,T.IN_HEAD),u=Atomics.load(c,T.IN_LOG_TAIL);if(p===u)return[];let f=g+E.IN_BUFFER_START,e=E.IN_BUFFER_SIZE,_;if(u+4<=e)_=a.getUint32(f+u,!0);else{_=0;for(let l=0;l<4;l++)_|=s[f+(u+l)%e]<<l*8}if(_!==E.MESSAGE_MAGIC)return Atomics.store(c,T.IN_LOG_TAIL,p),[];let o=[],{newTail:d,messagesRead:L}=m({uint8View:s,dataView:a,bufferStart:f,bufferSize:e,head:p,tail:u,messageMagic:E.MESSAGE_MAGIC,paddingMagic:E.PADDING_MAGIC,headerSize:E.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(l,t,n,r)=>{let A=new Uint8Array(t);for(let I=0;I<t;I++)A[I]=s[l+I];o.push({sourceId:r,oscData:A,sequence:n,timestamp:C()})},onCorruption:l=>{if(N++,N<=3){let t=g+E.IN_BUFFER_START+l,n=a.getUint32(t,!0),r=s[t],A=s[t+1],I=s[t+2],S=s[t+3],R=Atomics.load(c,T.IN_TAIL);console.error(`[OSCOutLogWorker] Corrupted message at position ${l}: head=${p} logTail=${u} inTail=${R} got=0x${(n>>>0).toString(16).padStart(8,"0")} expected=0x${(E.MESSAGE_MAGIC>>>0).toString(16).padStart(8,"0")} bytes=[${r},${A},${I},${S}] bufStart=${f} bufSize=${e}`)}else N===4&&console.error(`[OSCOutLogWorker] Suppressing further corruption logs (${N}+ total)`)}});return L>0&&Atomics.store(c,T.IN_LOG_TAIL,d),o}U({name:"OSCOutLogWorker",calculateControlIndices:O,headIndex:i=>i.IN_HEAD,tailIndex:i=>i.IN_LOG_TAIL,readMessages:x,postResults:i=>self.postMessage({type:"oscLog",entries:i}),initMetrics:!1,onInit:i=>{let c=Atomics.load(i.atomicView,i.CONTROL_INDICES.IN_HEAD);Atomics.store(i.atomicView,i.CONTROL_INDICES.IN_LOG_TAIL,c)}});})();
1
+ (()=>{function C({uint8View:a,dataView:o,bufferStart:e,bufferSize:I,head:T,tail:_,messageMagic:E,paddingMagic:f,headerSize:c,maxMessages:A=1/0,onMessage:t,onCorruption:u}){let i=_,g=0,p=()=>{u&&u(i),i=T};for(;i!==T&&g<A;){let l=I-i,n=(T-i+I)%I;if(l<4||n<4){p();break}let r=o.getUint32(e+i,!0);if(r===f){if(i===0){p();break}i=0;continue}if(r!==E){p();break}if(l<c){p();break}let s=o.getUint32(e+i+4,!0),N=o.getUint32(e+i+8,!0),S=o.getUint32(e+i+12,!0),d=s+3&-4;if(s<c||d>I||d>l||d>n){p();break}let O=s-c,M=e+i+c;t(M,O,N,S),i=(i+d)%I,g++}return{newTail:i,messagesRead:g}}function L(a,o,e,I,T,_,E=0){let f=I-o;for(let c=0;c<T;c++)_[E+c]=a[o+(f+c)%e]}function U(a,o){let e=a+o;return{IN_HEAD:(e+0)/4,IN_TAIL:(e+4)/4,IN_SEQUENCE:(e+24)/4,IN_WRITE_LOCK:(e+40)/4,IN_LOG_TAIL:(e+44)/4}}function R(){return(performance.timeOrigin+performance.now())/1e3+2208988800}function x(a){let{name:o,calculateControlIndices:e,headIndex:I,tailIndex:T,readMessages:_,postResults:E,initMetrics:f=!0,onInit:c,extraHandlers:A}=a,t={sharedBuffer:null,ringBufferBase:null,bufferConstants:null,atomicView:null,dataView:null,uint8View:null,metricsView:null,CONTROL_INDICES:{}},u=!1;function i(n,r,s){if(t.sharedBuffer=n,t.ringBufferBase=r,t.bufferConstants=s,t.atomicView=new Int32Array(n),t.dataView=new DataView(n),t.uint8View=new Uint8Array(n),t.CONTROL_INDICES=e(r,s.CONTROL_START),f){let N=r+s.METRICS_START;t.metricsView=new Uint32Array(n,N,s.METRICS_SIZE/4)}c?.(t)}function g(){let n=I(t.CONTROL_INDICES),r=T(t.CONTROL_INDICES);for(;u;)try{let s=Atomics.load(t.atomicView,n),N=Atomics.load(t.atomicView,r);s===N&&Atomics.wait(t.atomicView,n,s);let S=_(t);S&&S.length>0&&E(S)}catch(s){console.error(`[${o}] Error in wait loop:`,s),self.postMessage({type:"error",error:s.message}),Atomics.wait(t.atomicView,0,t.atomicView[0],10)}}function p(){if(!t.sharedBuffer){console.error(`[${o}] Cannot start - not initialized`);return}u||(u=!0,g())}function l(){u=!1}self.addEventListener("message",n=>{let{data:r}=n;try{if(A?.[r.type]){A[r.type](r,t);return}switch(r.type){case"init":r.sharedBuffer&&i(r.sharedBuffer,r.ringBufferBase,r.bufferConstants),self.postMessage({type:"initialized"});break;case"start":t.sharedBuffer&&p();break;case"stop":l();break;default:}}catch(s){console.error(`[${o}] Error:`,s),self.postMessage({type:"error",error:s.message})}})}var m=0;function w(a){let{atomicView:o,uint8View:e,dataView:I,ringBufferBase:T,bufferConstants:_,CONTROL_INDICES:E}=a,f=Atomics.load(o,E.IN_HEAD),c=Atomics.load(o,E.IN_LOG_TAIL);if(f===c)return[];let A=T+_.IN_BUFFER_START,t=_.IN_BUFFER_SIZE,u;if(c+4<=t)u=I.getUint32(A+c,!0);else{u=0;for(let l=0;l<4;l++)u|=e[A+(c+l)%t]<<l*8}if(u!==_.MESSAGE_MAGIC&&u!==_.PADDING_MAGIC)return Atomics.store(o,E.IN_LOG_TAIL,f),[];let i=[],{newTail:g,messagesRead:p}=C({uint8View:e,dataView:I,bufferStart:A,bufferSize:t,head:f,tail:c,messageMagic:_.MESSAGE_MAGIC,paddingMagic:_.PADDING_MAGIC,headerSize:_.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(l,n,r,s)=>{let N=new Uint8Array(n);L(e,A,t,l,n,N,0),i.push({sourceId:s,oscData:N,sequence:r,timestamp:R()})},onCorruption:l=>{if(m++,m<=3){let n=T+_.IN_BUFFER_START+l,r=I.getUint32(n,!0),s=e[n],N=e[n+1],S=e[n+2],d=e[n+3],O=Atomics.load(o,E.IN_TAIL);console.error(`[OSCOutLogWorker] Corrupted message at position ${l}: head=${f} logTail=${c} inTail=${O} got=0x${(r>>>0).toString(16).padStart(8,"0")} expected=0x${(_.MESSAGE_MAGIC>>>0).toString(16).padStart(8,"0")} bytes=[${s},${N},${S},${d}] bufStart=${A} bufSize=${t}`)}else m===4&&console.error(`[OSCOutLogWorker] Suppressing further corruption logs (${m}+ total)`)}});return p>0&&Atomics.store(o,E.IN_LOG_TAIL,g),i}x({name:"OSCOutLogWorker",calculateControlIndices:U,headIndex:a=>a.IN_HEAD,tailIndex:a=>a.IN_LOG_TAIL,readMessages:w,postResults:a=>self.postMessage({type:"oscLog",entries:a}),initMetrics:!1,onInit:a=>{let o=Atomics.load(a.atomicView,a.CONTROL_INDICES.IN_HEAD);Atomics.store(a.atomicView,a.CONTROL_INDICES.IN_LOG_TAIL,o)}});})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supersonic-scsynth",
3
- "version": "0.68.1",
3
+ "version": "0.70.0",
4
4
  "description": "SuperCollider scsynth WebAssembly port for AudioWorklet - Run SuperCollider synthesis in the browser",
5
5
  "main": "dist/supersonic.js",
6
6
  "types": "supersonic.d.ts",
@@ -15,6 +15,7 @@
15
15
  "pretest": "esbuild node_modules/fast-check/lib/fast-check.js --bundle --format=esm --outfile=test/assets/fast-check.bundle.js",
16
16
  "test": "playwright test --reporter=list --max-failures=1",
17
17
  "test:all": "playwright test --reporter=list",
18
+ "test:unit": "node --test \"test/unit/*.test.mjs\"",
18
19
  "prepublishOnly": "./scripts/build-web.sh --release",
19
20
  "test:types": "tsd",
20
21
  "docs:api": "typedoc && mv docs/api-generated/README.md docs/API.md && rm -rf docs/api-generated",
package/supersonic.d.ts CHANGED
@@ -240,10 +240,6 @@ export interface SuperSonicOptions {
240
240
 
241
241
  /** How often to snapshot metrics/tree in postMessage mode (ms). */
242
242
  snapshotIntervalMs?: number;
243
- /** Max pending events in the JS prescheduler. Default: 65536. */
244
- preschedulerCapacity?: number;
245
- /** Bundles scheduled within this many ms of now are dispatched immediately for lowest latency. Bundles further in the future are held and dispatched closer to their scheduled time. Default: 500. */
246
- bypassLookaheadMs?: number;
247
243
 
248
244
  /** Enable all debug console logging. Default: false. */
249
245
  debug?: boolean;
@@ -300,38 +296,6 @@ export interface SuperSonicMetrics {
300
296
  /** Bundles executed after their scheduled time. */
301
297
  scsynthSchedulerLates: number;
302
298
 
303
- // Prescheduler metrics
304
- /** Events waiting in JS prescheduler queue. */
305
- preschedulerPending: number;
306
- /** Peak pending events. */
307
- preschedulerPendingPeak: number;
308
- /** Bundles added to prescheduler. */
309
- preschedulerBundlesScheduled: number;
310
- /** Events sent from prescheduler to worklet. */
311
- preschedulerDispatched: number;
312
- /** Bundles cancelled before dispatch. */
313
- preschedulerEventsCancelled: number;
314
- /** Smallest time gap between dispatch and execution (ms). 0xFFFFFFFF = no data yet. */
315
- preschedulerMinHeadroomMs: number;
316
- /** Bundles dispatched after their scheduled time. */
317
- preschedulerLates: number;
318
- /** Ring buffer write retries that succeeded. */
319
- preschedulerRetriesSucceeded: number;
320
- /** Ring buffer write retries that failed. */
321
- preschedulerRetriesFailed: number;
322
- /** Current retry queue size. */
323
- preschedulerRetryQueueSize: number;
324
- /** Peak retry queue size. */
325
- preschedulerRetryQueuePeak: number;
326
- /** Total messages that needed retry. */
327
- preschedulerMessagesRetried: number;
328
- /** Total dispatch cycles. */
329
- preschedulerTotalDispatches: number;
330
- /** Messages sent directly, bypassing prescheduler (aggregate). */
331
- preschedulerBypassed: number;
332
- /** Maximum lateness at prescheduler (ms). */
333
- preschedulerMaxLateMs: number;
334
-
335
299
  // OSC Out metrics
336
300
  /** OSC messages sent from JS to scsynth. */
337
301
  oscOutMessagesSent: number;
@@ -368,16 +332,6 @@ export interface SuperSonicMetrics {
368
332
  /** Peak bytes used in DEBUG ring buffer. */
369
333
  debugBufferPeakBytes: number;
370
334
 
371
- // Bypass categories
372
- /** Plain OSC messages (not bundles) that bypassed prescheduler. */
373
- bypassNonBundle: number;
374
- /** Bundles with timetag 0 or 1 that bypassed prescheduler. */
375
- bypassImmediate: number;
376
- /** Bundles within lookahead threshold that bypassed prescheduler. */
377
- bypassNearFuture: number;
378
- /** Late bundles that bypassed prescheduler. */
379
- bypassLate: number;
380
-
381
335
  // scsynth late timing diagnostics
382
336
  /** Maximum lateness observed in scsynth scheduler (ms). */
383
337
  scsynthSchedulerMaxLateMs: number;
@@ -386,9 +340,35 @@ export interface SuperSonicMetrics {
386
340
  /** Process count when last scsynth late occurred. */
387
341
  scsynthSchedulerLastLateTick: number;
388
342
 
389
- /** SAB mode only: optimistic direct writes that fell back to prescheduler. */
343
+ /** SAB mode only: direct IN-ring writes dropped on lock contention / full ring. */
390
344
  ringBufferDirectWriteFails: number;
391
345
 
346
+ // System info (cross-platform; written by shared C++ at init)
347
+ /** SuperSonic major version. */
348
+ supersonicVersionMajor: number;
349
+ /** SuperSonic minor version. */
350
+ supersonicVersionMinor: number;
351
+ /** SuperSonic patch version. */
352
+ supersonicVersionPatch: number;
353
+ /** Output sample rate in Hz. */
354
+ audioSampleRate: number;
355
+ /** Audio block size in frames (128 on web). */
356
+ audioBlockSize: number;
357
+ /** Number of output bus channels. */
358
+ audioOutputChannels: number;
359
+ /** Number of input bus channels. */
360
+ audioInputChannels: number;
361
+
362
+ // SuperClock readouts (cross-platform; written per audio block)
363
+ /** Tempo in milli-BPM (bpm * 1000). Divide by 1000 for BPM. */
364
+ clockTempoMbpm: number;
365
+ /** Beat position * 100. Divide by 100 for the beat. */
366
+ clockBeatCenti: number;
367
+ /** Phase within the quantum * 100. Divide by 100 for the phase. */
368
+ clockPhaseCenti: number;
369
+ /** Transport playing (0 = stopped, 1 = playing). */
370
+ clockPlaying: number;
371
+
392
372
  // Context metrics (main thread only)
393
373
  /** Clock drift between AudioContext and wall clock (ms, signed). */
394
374
  driftOffsetMs: number;
@@ -414,8 +394,6 @@ export interface SuperSonicMetrics {
414
394
  loadedSynthDefs: number;
415
395
  /** Maximum scsynth scheduler queue size (compile-time constant). */
416
396
  scsynthSchedulerCapacity: number;
417
- /** Maximum pending events in JS prescheduler. */
418
- preschedulerCapacity: number;
419
397
  /** IN ring buffer capacity (bytes). */
420
398
  inBufferCapacity: number;
421
399
  /** OUT ring buffer capacity (bytes). */
@@ -485,11 +463,6 @@ export interface MetricsSchema {
485
463
  }>;
486
464
  }>;
487
465
  };
488
- /** Magic values used in the metrics array. */
489
- sentinels: {
490
- /** Value of preschedulerMinHeadroomMs before any data arrives. */
491
- HEADROOM_UNSET: number;
492
- };
493
466
  }
494
467
 
495
468
  // ============================================================================
@@ -912,26 +885,10 @@ export type SuperSonicEvent = keyof SuperSonicEventMap;
912
885
  // OscChannel
913
886
  // ============================================================================
914
887
 
915
- /**
916
- * Classification category for OSC message routing.
917
- *
918
- * - `'nonBundle'` — plain message (not a bundle), sent directly
919
- * - `'immediate'` — bundle with timetag 0 or 1, sent directly
920
- * - `'nearFuture'` — bundle within the bypass lookahead threshold, sent directly
921
- * - `'late'` — bundle past its scheduled time, sent directly
922
- * - `'farFuture'` — bundle beyond the lookahead threshold, routed to the prescheduler
923
- */
924
- export type OscCategory = 'nonBundle' | 'immediate' | 'nearFuture' | 'late' | 'farFuture';
925
-
926
888
  /** OscChannel metrics counters. */
927
889
  export interface OscChannelMetrics {
928
890
  messagesSent: number;
929
891
  bytesSent: number;
930
- nonBundle: number;
931
- immediate: number;
932
- nearFuture: number;
933
- late: number;
934
- bypassed: number;
935
892
  }
936
893
 
937
894
  /** Transferable config for SAB mode OscChannel. */
@@ -941,8 +898,6 @@ export interface OscChannelSABTransferable {
941
898
  ringBufferBase: number;
942
899
  bufferConstants: Record<string, number>;
943
900
  controlIndices: Record<string, number>;
944
- preschedulerPort: MessagePort | null;
945
- bypassLookaheadS: number;
946
901
  sourceId: number;
947
902
  blocking: boolean;
948
903
  }
@@ -951,8 +906,6 @@ export interface OscChannelSABTransferable {
951
906
  export interface OscChannelPMTransferable {
952
907
  mode: 'postMessage';
953
908
  port: MessagePort;
954
- preschedulerPort: MessagePort | null;
955
- bypassLookaheadS: number;
956
909
  sourceId: number;
957
910
  blocking: boolean;
958
911
  }
@@ -981,17 +934,9 @@ export type OscChannelTransferable = OscChannelSABTransferable | OscChannelPMTra
981
934
  */
982
935
  export class OscChannel {
983
936
  /**
984
- * Classify an OSC message to determine its routing.
985
- * @param oscData - Encoded OSC bytes
986
- */
987
- classify(oscData: Uint8Array): OscCategory;
988
-
989
- /**
990
- * Send an OSC message with automatic routing.
991
- *
992
- * Classifies the message and routes it:
993
- * - bypass categories → sent directly to the AudioWorklet
994
- * - far-future bundles → routed to the prescheduler for timed dispatch
937
+ * Send an OSC message: frames it onto the IN ring (SAB) or postMessages it to
938
+ * the worklet (PM). Classification and scheduling happen on the audio thread
939
+ * (the engine's OscIngress + BundleScheduler) — the producer never classifies.
995
940
  *
996
941
  * @param oscData - Encoded OSC bytes
997
942
  * @returns true if sent successfully
@@ -999,19 +944,12 @@ export class OscChannel {
999
944
  send(oscData: Uint8Array): boolean;
1000
945
 
1001
946
  /**
1002
- * Send directly to worklet without classification or metrics tracking.
947
+ * Alias of {@link send} kept for callers that used the explicit direct path.
1003
948
  * @param oscData - Encoded OSC bytes
1004
949
  * @returns true if sent successfully
1005
950
  */
1006
951
  sendDirect(oscData: Uint8Array): boolean;
1007
952
 
1008
- /**
1009
- * Send to prescheduler without classification.
1010
- * @param oscData - Encoded OSC bytes
1011
- * @returns true if sent successfully
1012
- */
1013
- sendToPrescheduler(oscData: Uint8Array): boolean;
1014
-
1015
953
  /** Get current metrics snapshot. */
1016
954
  getMetrics(): OscChannelMetrics;
1017
955
 
@@ -1029,73 +967,6 @@ export class OscChannel {
1029
967
  */
1030
968
  nextNodeId(): number;
1031
969
 
1032
- /**
1033
- * Register a handler for OSC replies from scsynth. Idempotent — replaces
1034
- * any previously-registered handler. In AudioWorklet contexts the worklet
1035
- * must call {@link pollReplies} from `process()` to drain; in all other
1036
- * contexts delivery is automatic. All registered channels receive all
1037
- * replies (broadcast); filter locally if you only need specific addresses.
1038
- *
1039
- * SAB mode: handler receives `(view, offset, length, sequence)` — zero-copy
1040
- * into the shared buffer. Read bytes from `view[offset..offset+length]`.
1041
- * Data is valid only for the duration of the handler call.
1042
- *
1043
- * PM mode: handler receives `(oscData, sequence)` where `oscData` is a copy.
1044
- *
1045
- * @example
1046
- * channel.setReplyHandler((view, offset, length, sequence) => {
1047
- * // SAB: read raw OSC bytes from view[offset..offset+length]
1048
- * });
1049
- */
1050
- setReplyHandler(
1051
- handler:
1052
- | ((view: Uint8Array, offset: number, length: number, sequence: number) => void)
1053
- | ((oscData: Uint8Array, sequence: number) => void),
1054
- ): void;
1055
-
1056
- /**
1057
- * Clear the reply handler and release the reply channel. Idempotent.
1058
- */
1059
- clearReplyHandler(): void;
1060
-
1061
- /**
1062
- * Activate the reply slot without registering a handler. Usually called
1063
- * for you by {@link setReplyHandler}; only call directly if you want to
1064
- * claim the slot before installing a handler (or to use the optional
1065
- * one-shot handler argument of {@link pollReplies}).
1066
- */
1067
- activateReplies(): void;
1068
-
1069
- /**
1070
- * Release the reply slot. Usually called for you by {@link clearReplyHandler}.
1071
- */
1072
- deactivateReplies(): void;
1073
-
1074
- /**
1075
- * Drain pending replies, calling the registered handler (or `handler`
1076
- * argument, if given) once per message. Returns the number of messages
1077
- * processed. Zero-allocation on the hot path.
1078
- *
1079
- * Call from an AudioWorklet's `process()` method to receive replies on
1080
- * the audio thread. In other contexts automatic delivery already calls
1081
- * this for you.
1082
- *
1083
- * @param handler - Optional override for this call only
1084
- * @returns Number of messages drained
1085
- */
1086
- pollReplies(
1087
- handler?:
1088
- | ((view: Uint8Array, offset: number, length: number, sequence: number) => void)
1089
- | ((oscData: Uint8Array, sequence: number) => void),
1090
- ): number;
1091
-
1092
- /**
1093
- * Number of reply messages dropped because the reply buffer was full.
1094
- * SAB mode only — undefined in PM mode or before {@link activateReplies}.
1095
- * Counter resets each time the slot is (re)claimed.
1096
- */
1097
- get replyDrops(): number | undefined;
1098
-
1099
970
  /** Close the channel and release its ports. */
1100
971
  close(): void;
1101
972
 
@@ -1246,14 +1117,6 @@ export type BlockedCommand =
1246
1117
  // SuperSonic
1247
1118
  // ============================================================================
1248
1119
 
1249
- /** Options for {@link SuperSonic.sendOSC}. */
1250
- export interface SendOSCOptions {
1251
- /** Session ID for cancellation via {@link SuperSonic.cancelSession}. */
1252
- sessionId?: string;
1253
- /** Run tag for cancellation via {@link SuperSonic.cancelTag}. */
1254
- runTag?: string;
1255
- }
1256
-
1257
1120
  /**
1258
1121
  * SuperSonic — WebAssembly SuperCollider synthesis engine for the browser.
1259
1122
  *
@@ -1631,7 +1494,7 @@ export class SuperSonic {
1631
1494
  send(address: '/b_write', ...args: OscArg[]): never;
1632
1495
  /** @deprecated File writing is not available in the browser. */
1633
1496
  send(address: '/b_close', ...args: OscArg[]): never;
1634
- /** @deprecated Use purge() to clear both the JS prescheduler and WASM scheduler. */
1497
+ /** @deprecated Use purge() to clear the WASM BundleScheduler + IN ring. */
1635
1498
  send(address: '/clearSched', ...args: OscArg[]): never;
1636
1499
  /** @deprecated SuperSonic always enables error notifications so you never miss a /fail reply. */
1637
1500
  send(address: '/error', ...args: OscArg[]): never;
@@ -1784,48 +1647,17 @@ export class SuperSonic {
1784
1647
  * Use {@link send} for buffer commands so they are handled correctly.
1785
1648
  *
1786
1649
  * @param oscData - Encoded OSC message or bundle bytes
1787
- * @param options - Optional session/tag for cancellation
1788
- * @throws If the bundle exceeds the maximum schedulable size
1650
+ * @throws If the message exceeds the IN ring size
1789
1651
  *
1790
1652
  * @example
1791
1653
  * const msg = SuperSonic.osc.encodeMessage('/n_set', [1001, 'freq', 880]);
1792
1654
  * sonic.sendOSC(msg);
1793
- *
1794
- * // With cancellation tags:
1795
- * const bundle = SuperSonic.osc.encodeBundle(futureTime, packets);
1796
- * sonic.sendOSC(bundle, { sessionId: 'song1', runTag: 'verse' });
1797
- */
1798
- sendOSC(oscData: Uint8Array | ArrayBuffer, options?: SendOSCOptions): void;
1799
-
1800
- /**
1801
- * Cancel all scheduled messages with the given run tag.
1802
- * Only affects messages in the JS prescheduler (not yet dispatched to WASM).
1803
- * @param runTag - Tag to cancel
1804
- */
1805
- cancelTag(runTag: string): void;
1806
-
1807
- /**
1808
- * Cancel all scheduled messages for a session.
1809
- * @param sessionId - Session to cancel
1810
1655
  */
1811
- cancelSession(sessionId: string): void;
1656
+ sendOSC(oscData: Uint8Array | ArrayBuffer): void;
1812
1657
 
1813
1658
  /**
1814
- * Cancel scheduled messages matching both a session and run tag.
1815
- * @param sessionId - Session to match
1816
- * @param runTag - Tag to match within that session
1817
- */
1818
- cancelSessionTag(sessionId: string, runTag: string): void;
1819
-
1820
- /** Cancel all scheduled messages in the JS prescheduler. */
1821
- cancelAll(): void;
1822
-
1823
- /**
1824
- * Cancel all pending scheduled messages everywhere in the pipeline.
1825
- *
1826
- * Unlike {@link cancelAll} which only clears messages still waiting in JS,
1827
- * `purge()` guarantees that nothing already in-flight will fire either.
1828
- * Resolves when the flush is confirmed complete.
1659
+ * Flush all pending scheduled OSC: clears the WASM BundleScheduler and the IN
1660
+ * ring so nothing already in-flight will fire. Resolves when confirmed.
1829
1661
  */
1830
1662
  purge(): Promise<void>;
1831
1663
 
@@ -1,3 +0,0 @@
1
- (()=>{function L({uint8View:_,dataView:o,bufferStart:c,bufferSize:t,head:A,tail:T,messageMagic:I,paddingMagic:R,headerSize:C,maxMessages:D=1/0,onMessage:e,onCorruption:S}){let E=T,a=0,i=U=>{let r=U;if(r+4<=t)return o.getUint32(c+r,!0);let s=0;for(let n=0;n<4;n++)s|=_[c+(r+n)%t]<<n*8;return s};for(;E!==A&&a<D;){let U=t-E,r;if(U>=4?r=o.getUint32(c+E,!0):r=i(E),r===R){E=0;continue}if(r!==I){S&&S(E),E=(E+1)%t;continue}let s=i((E+4)%t),n=i((E+8)%t),l=i((E+12)%t);if(s<C||s>t){S&&S(E),E=(E+1)%t;continue}let p=s-C,O=c+(E+C)%t;e(O,p,n,l),E=(E+s)%t,a++}return{newTail:E,messagesRead:a}}function N(_,o){let c=_+o;return{DEBUG_HEAD:(c+16)/4,DEBUG_TAIL:(c+20)/4}}function x(_){let{name:o,calculateControlIndices:c,headIndex:t,tailIndex:A,readMessages:T,postResults:I,initMetrics:R=!0,onInit:C,extraHandlers:D}=_,e={sharedBuffer:null,ringBufferBase:null,bufferConstants:null,atomicView:null,dataView:null,uint8View:null,metricsView:null,CONTROL_INDICES:{}},S=!1;function E(r,s,n){if(e.sharedBuffer=r,e.ringBufferBase=s,e.bufferConstants=n,e.atomicView=new Int32Array(r),e.dataView=new DataView(r),e.uint8View=new Uint8Array(r),e.CONTROL_INDICES=c(s,n.CONTROL_START),R){let l=s+n.METRICS_START;e.metricsView=new Uint32Array(r,l,n.METRICS_SIZE/4)}C?.(e)}function a(){let r=t(e.CONTROL_INDICES),s=A(e.CONTROL_INDICES);for(;S;)try{let n=Atomics.load(e.atomicView,r),l=Atomics.load(e.atomicView,s);n===l&&Atomics.wait(e.atomicView,r,n);let p=T(e);p&&p.length>0&&I(p)}catch(n){console.error(`[${o}] Error in wait loop:`,n),self.postMessage({type:"error",error:n.message}),Atomics.wait(e.atomicView,0,e.atomicView[0],10)}}function i(){if(!e.sharedBuffer){console.error(`[${o}] Cannot start - not initialized`);return}S||(S=!0,a())}function U(){S=!1}self.addEventListener("message",r=>{let{data:s}=r;try{if(D?.[s.type]){D[s.type](s,e);return}switch(s.type){case"init":s.sharedBuffer&&E(s.sharedBuffer,s.ringBufferBase,s.bufferConstants),self.postMessage({type:"initialized"});break;case"start":e.sharedBuffer&&i();break;case"stop":U();break;default:}}catch(n){console.error(`[${o}] Error:`,n),self.postMessage({type:"error",error:n.message})}})}var u=new TextDecoder("utf-8");function d(_,o,c){let t=new Uint8Array(c);for(let T=0;T<c;T++)t[T]=_[o+T];let A=u.decode(t);return A.endsWith(`
2
- `)&&(A=A.slice(0,-1)),A}function P(_){let{atomicView:o,uint8View:c,dataView:t,ringBufferBase:A,bufferConstants:T,metricsView:I,CONTROL_INDICES:R}=_,C=Atomics.load(o,R.DEBUG_HEAD),D=Atomics.load(o,R.DEBUG_TAIL);if(C===D)return[];let e=[],{newTail:S,messagesRead:E}=L({uint8View:c,dataView:t,bufferStart:A+T.DEBUG_BUFFER_START,bufferSize:T.DEBUG_BUFFER_SIZE,head:C,tail:D,messageMagic:T.MESSAGE_MAGIC,paddingMagic:T.PADDING_MAGIC,headerSize:T.MESSAGE_HEADER_SIZE,maxMessages:1e3,onMessage:(a,i,U)=>{e.push({text:d(c,a,i),timestamp:performance.now(),sequence:U}),I&&(Atomics.add(I,30,1),Atomics.add(I,31,i))},onCorruption:a=>{console.error("[DebugWorker] Corrupted message at position",a)}});return E>0&&Atomics.store(o,R.DEBUG_TAIL,S),e}function m(_){let o=[];for(let c of _)try{let t=u.decode(new Uint8Array(c.bytes));t.endsWith(`
3
- `)&&(t=t.slice(0,-1)),o.push({text:t,timestamp:performance.now(),sequence:c.sequence})}catch(t){console.error("[DebugWorker] Failed to decode message:",t)}o.length>0&&self.postMessage({type:"debug",messages:o})}x({name:"DebugWorker",calculateControlIndices:N,headIndex:_=>_.DEBUG_HEAD,tailIndex:_=>_.DEBUG_TAIL,readMessages:P,postResults:_=>self.postMessage({type:"debug",messages:_}),extraHandlers:{clear:(_,o)=>{o.sharedBuffer&&(Atomics.store(o.atomicView,o.CONTROL_INDICES.DEBUG_HEAD,0),Atomics.store(o.atomicView,o.CONTROL_INDICES.DEBUG_TAIL,0))},debugRaw:_=>{_.messages&&m(_.messages)}}});})();
@@ -1 +0,0 @@
1
- (()=>{function Ce(t,e,n){return(n-1-t+e)%n}function Ie({uint8View:t,dataView:e,bufferStart:n,bufferSize:s,head:r,payload:o,sequence:u,messageMagic:p,headerSize:c,sourceId:T=0,headerScratch:X=null,headerScratchView:z=null}){let B=o.length,R=c+B+3&-4,h=s-r;if(R>h){let S=X||new Uint8Array(c),L=z||new DataView(S.buffer);L.setUint32(0,p,!0),L.setUint32(4,R,!0),L.setUint32(8,u,!0),L.setUint32(12,T,!0);let P=n+r,O=n;if(h>=c){t.set(S,P);let y=h-c;for(let l=0;l<y;l++)t[P+c+l]=o[l];for(let l=y;l<B;l++)t[O+l-y]=o[l]}else{for(let l=0;l<h;l++)t[P+l]=S[l];for(let l=h;l<c;l++)t[O+l-h]=S[l];let y=c-h;t.set(o,O+y)}}else{let S=n+r;e.setUint32(S,p,!0),e.setUint32(S+4,R,!0),e.setUint32(S+8,u,!0),e.setUint32(S+12,T,!0),t.set(o,S+c)}return(r+R)%s}function De(t,e,n=0,s=!1){for(let r=0;r<=n;r++)if(Atomics.compareExchange(t,e,0,1)===0)return!0;if(s){for(let o=0;o<100;o++)if(Atomics.wait(t,e,1,100),Atomics.compareExchange(t,e,0,1)===0)return!0;return console.error("[RingBuffer] Lock acquisition timeout after 10s - possible deadlock"),!1}return!1}function xe(t,e){Atomics.store(t,e,0),Atomics.notify(t,e,1)}function ne({atomicView:t,dataView:e,uint8View:n,bufferConstants:s,ringBufferBase:r,controlIndices:o,oscMessage:u,sourceId:p=0,maxSpins:c=0,useWait:T=!1,headerScratch:X=null,headerScratchView:z=null}){let B=u.length,v=s.MESSAGE_HEADER_SIZE+B;if(v>s.IN_BUFFER_SIZE-s.MESSAGE_HEADER_SIZE||!De(t,o.IN_WRITE_LOCK,c,T))return!1;try{let R=Atomics.load(t,o.IN_HEAD),h=Atomics.load(t,o.IN_TAIL),S=v+3&-4;if(Ce(R,h,s.IN_BUFFER_SIZE)<S)return!1;let P=Atomics.add(t,o.IN_SEQUENCE,1),O=Ie({uint8View:n,dataView:e,bufferStart:r+s.IN_BUFFER_START,bufferSize:s.IN_BUFFER_SIZE,head:R,payload:u,sequence:P,messageMagic:s.MESSAGE_MAGIC,headerSize:s.MESSAGE_HEADER_SIZE,sourceId:p,headerScratch:X,headerScratchView:z});return Atomics.load(t,o.IN_HEAD),Atomics.store(t,o.IN_HEAD,O),Atomics.notify(t,o.IN_HEAD,1),!0}finally{xe(t,o.IN_WRITE_LOCK)}}function se(t,e){let n=t+e;return{IN_HEAD:(n+0)/4,IN_TAIL:(n+4)/4,IN_SEQUENCE:(n+24)/4,IN_WRITE_LOCK:(n+40)/4,IN_LOG_TAIL:(n+44)/4}}function re(t){return t.length>=8&&t[0]===35&&t[1]===98&&t[2]===117&&t[3]===110&&t[4]===100&&t[5]===108&&t[6]===101&&t[7]===0}function oe(){return(performance.timeOrigin+performance.now())/1e3+2208988800}var Le=new Uint8Array(2097152),Je=new DataView(Le.buffer);var je=new TextDecoder,et=new TextEncoder;var Pe=4294967296,tt=new Uint8Array([35,98,117,110,100,108,101,0]);function Oe(t){return!t||t.length<8?!1:t[0]===35&&t[1]===98}function ie(t){if(!Oe(t))return null;let e=new DataView(t.buffer,t.byteOffset,t.byteLength),n=e.getUint32(8,!1),s=e.getUint32(12,!1);return n+s/Pe}var M=class{constructor({lookaheadS:e=.5,maxPending:n=1e3,maxFutureS:s=3600,poolBytes:r=524288}={}){this.lookaheadS=e,this.maxPending=n,this.maxFutureS=s,this.poolBytes=r,this._heap=[],this._seq=0,this.retryCount=0}size(){return this._heap.length}schedule(e,n){if(this._heap.length+this.retryCount>=this.maxPending)return{ok:!1,reason:"queue_full"};if(e.ntpTime==null)return{ok:!0,immediate:e};if((e.bytes||0)>this.poolBytes)return{ok:!1,reason:"too_large"};if(e.ntpTime-n>this.maxFutureS)return{ok:!1,reason:"too_far_future"};let r={ntpTime:e.ntpTime,seq:this._seq++,sessionId:e.sessionId||0,runTag:e.runTag||"",payload:e.payload};return this._push(r),{ok:!0,scheduled:r}}nextDueTime(){return this._heap.length?this._heap[0].ntpTime-this.lookaheadS:null}dispatchDue(e,n){let s=e+this.lookaheadS,r=0;for(;this._heap.length&&this._heap[0].ntpTime<=s;)n(this._pop()),r++;return r}cancelBy(e){if(this._heap.length===0)return 0;let n=this._heap.length;this._heap=this._heap.filter(r=>!e(r));let s=n-this._heap.length;return s>0&&this._heapify(),s}cancelTag(e){return this.cancelBy(n=>n.runTag===e)}cancelSession(e){return this.cancelBy(n=>n.sessionId===e)}cancelSessionTag(e,n){return this.cancelBy(s=>s.sessionId===e&&s.runTag===n)}cancelAll(){let e=this._heap.length;return this._heap=[],e}_cmp(e,n){return e.ntpTime===n.ntpTime?e.seq-n.seq:e.ntpTime-n.ntpTime}_swap(e,n){let s=this._heap[e];this._heap[e]=this._heap[n],this._heap[n]=s}_push(e){this._heap.push(e),this._siftUp(this._heap.length-1)}_pop(){let e=this._heap[0],n=this._heap.pop();return this._heap.length&&(this._heap[0]=n,this._siftDown(0)),e}_siftUp(e){for(;e>0;){let n=e-1>>1;if(this._cmp(this._heap[e],this._heap[n])>=0)break;this._swap(e,n),e=n}}_siftDown(e){let n=this._heap.length;for(;;){let s=2*e+1,r=2*e+2,o=e;if(s<n&&this._cmp(this._heap[s],this._heap[o])<0&&(o=s),r<n&&this._cmp(this._heap[r],this._heap[o])<0&&(o=r),o===e)break;this._swap(e,o),e=o}}_heapify(){for(let e=(this._heap.length>>1)-1;e>=0;e--)this._siftDown(e)}};var A="sab",Z=null,H=524288,Fe=65536,Te=3600,he=4294967295,C=null,w=null,_=null,g=null,Ae=null,de=null,q=null,pe=null,b={},a=null,k=null,ue=null,V=150,I=(t,e)=>{a&&(A==="sab"?Atomics.store(a,t,e):a[t]=e)},j=t=>a?A==="sab"?Atomics.load(a,t):a[t]:0,d=(t,e)=>{a&&(A==="sab"?Atomics.add(a,t,e):a[t]+=e)},G=I,_e=j,He=()=>{if(A!=="postMessage"||ue!==null)return;let t=()=>{k&&a&&self.postMessage({type:"preschedulerMetrics",metrics:new Uint32Array(k.slice(0))}),ue=setTimeout(t,V)};t(),f("[PreScheduler] Started metrics sending (every "+V+"ms)")};var U=null,F=1/0,ae=!1,E=[],Q=!1,D=65536,$=.5,i=new M({lookaheadS:$,maxPending:D,maxFutureS:Te,poolBytes:H}),f=(...t)=>{},ee=oe,we=t=>ie(t),be=()=>{if(!C||!_){console.error("[PreScheduler] Cannot init - missing buffer or constants");return}g=new Int32Array(C),Ae=new DataView(C),de=new Uint8Array(C);let t=_.MESSAGE_HEADER_SIZE||16;q=new Uint8Array(t),pe=new DataView(q.buffer),b=se(w,_.CONTROL_START);let e=w+_.METRICS_START;a=new Uint32Array(C,e,_.METRICS_SIZE/4),f("[PreScheduler] SharedArrayBuffer initialized with direct ring buffer writing and metrics")},Y=()=>{if(!a)return;I(9,i.size());let t=i.size(),e=j(10);t>e&&I(10,t)},x=(t,e,n=0,s=!1)=>{if(A==="postMessage")return Z?(Z.postMessage({type:"osc",oscData:t,sourceId:n}),d(12,1),!0):(console.error("[PreScheduler] No worklet port available"),!1);if(!C||!g)return console.error("[PreScheduler] Not initialized for ring buffer writing"),!1;let r=t.length,o=_.MESSAGE_HEADER_SIZE+r;return o>_.IN_BUFFER_SIZE-_.MESSAGE_HEADER_SIZE?(console.error("[PreScheduler] Message too large:",o),!1):ne({atomicView:g,dataView:Ae,uint8View:de,bufferConstants:_,ringBufferBase:w,controlIndices:b,oscMessage:t,sourceId:n,maxSpins:10,useWait:s,headerScratch:q,headerScratchView:pe})?(d(12,1),!0):!1},m=(t,e,n=0)=>{let s=i.size()+E.length;if(s>=D){console.error("[PreScheduler] Backpressure: dropping retry ("+s+" pending)"),d(17,1);return}E.push({oscData:t,context:e||"unknown",queuedAt:performance.now(),sourceId:n}),I(18,E.length);let r=j(19);E.length>r&&I(19,E.length),f("[PreScheduler] Queued message for retry:",e,"queue size:",E.length),Ue()},Ue=()=>{if(Q||E.length===0||A!=="sab")return;Q=!0;let t=Atomics.load(g,b.IN_TAIL),e=Atomics.waitAsync(g,b.IN_TAIL,t),n=()=>{Q=!1,ke(),E.length>0&&Ue()};e.async?e.value.then(n):queueMicrotask(n)},ke=()=>{if(E.length===0)return;let t=0;for(let e=0;e<E.length;e++){let n=E[e];if(x(n.oscData,!0,n.sourceId,!0))t++,d(16,1);else break}t>0&&(E.splice(0,t),I(18,E.length))},fe=(t,e,n,s=0)=>{let r=we(t),o=ee();i.retryCount=E.length;let u=i.schedule({ntpTime:r,bytes:t.length,sessionId:e,runTag:n,payload:{oscData:t,sourceId:s}},o);if(!u.ok){let[p,c]={queue_full:["PRESCHEDULER_QUEUE_FULL",`Prescheduler queue full (${i.size()+E.length} >= ${D} max)`],too_large:["BUNDLE_TOO_LARGE",`Bundle too large for scheduler pool (${t.length} > ${H} bytes)`],too_far_future:["BUNDLE_TOO_FAR_FUTURE",`Bundle scheduled too far in future (${(r-o).toFixed(0)}s > ${Te}s max)`]}[u.reason];return console.error("[PreScheduler]",c),self.postMessage({type:"error",error:c,code:p}),!1}return u.immediate?(f("[PreScheduler] Non-bundle message, dispatching immediately"),x(t,!1,s,!0)||m(t,"immediate message",s),!0):(d(11,1),Y(),f("[PreScheduler] Scheduled bundle:","NTP="+r.toFixed(3),"current="+o.toFixed(3),"wait="+((r-o)*1e3).toFixed(1)+"ms","pending="+i.size()),N(),!0)},N=()=>{let t=i.nextDueTime();if(t===null){U!==null&&(clearTimeout(U),U=null,F=1/0);return}if(t<F){U!==null&&clearTimeout(U);let e=Math.max(0,(t-ee())*1e3);F=t,U=setTimeout(Ye,e)}},Ge=()=>{U===null&&(f("[PreScheduler] Starting demand-driven dispatching"),N())};var Ye=()=>{ae=!0;let t=ee(),e=0,n=s=>{Y();let{oscData:r,sourceId:o}=s.payload,u=s.ntpTime-t;if(d(21,1),u<0){let c=Math.round(-u*1e3);d(15,1);let T=_e(23);c>T&&G(23,c)}else{let c=Math.round(u*1e3),T=_e(14);(T===he||c<T)&&G(14,c)}f("[PreScheduler] Dispatching bundle:","NTP="+s.ntpTime.toFixed(3),"current="+t.toFixed(3),"early="+(u*1e3).toFixed(1)+"ms","remaining="+i.size()),x(r,!1,o,!0)||m(r,"scheduled bundle NTP="+s.ntpTime.toFixed(3),o),e++};i.dispatchDue(t,n),(e>0||i.size()>0||E.length>0)&&f("[PreScheduler] Dispatch cycle complete:","dispatched="+e,"pending="+i.size(),"retrying="+E.length),ae=!1,U=null,F=1/0,N()},te=t=>{t>0&&(d(13,t),Y(),f("[PreScheduler] Cancelled "+t+" events, "+i.size()+" remaining"),N())},Xe=(t,e)=>te(i.cancelSessionTag(t,e)),ze=t=>te(i.cancelSession(t)),ve=t=>te(i.cancelTag(t)),Qe=()=>{let t=i.cancelAll(),e=E.length;t===0&&e===0||(d(13,t+e),E=[],I(18,0),Y(),f("[PreScheduler] Cancelled all "+t+" events, "+e+" retries"),N())},Ke=re,We=t=>{let e=[],n=new DataView(t.buffer,t.byteOffset,t.byteLength),s=16;for(;s+4<=t.length;){let r=n.getInt32(s,!1);if(s+=4,r<=0||r>Fe||s+r>t.length)break;let o=t.slice(s,s+r);for(e.push(o),s+=r;s%4!==0&&s<t.length;)s++}return e},Ze=(t,e=0)=>{if(Ke(t)){let n=We(t);for(let s=0;s<n.length;s++)x(n[s],!1,e,!0)||m(n[s],"immediate bundle message "+s,e)}else x(t,!1,e,!0)||m(t,"immediate message",e)};self.addEventListener("message",t=>{let{data:e}=t;try{switch(e.type){case"init":if(A=e.mode||"sab",e.maxPendingMessages&&(D=e.maxPendingMessages),e.snapshotIntervalMs&&(V=e.snapshotIntervalMs),e.bypassLookaheadS!==void 0&&($=e.bypassLookaheadS),A==="sab")C=e.sharedBuffer,w=e.ringBufferBase,_=e.bufferConstants,be(),_&&_.scheduler_data_pool_size&&(H=_.scheduler_data_pool_size);else{Z=e.workletPort;let s=184;k=new ArrayBuffer(s),a=new Uint32Array(k),He()}G(14,he),G(23,0),i.maxPending=D,i.lookaheadS=$,i.poolBytes=H,Ge(),f("[OSCPreSchedulerWorker] Initialized with NTP-based scheduling, mode="+A+", capacity="+D),self.postMessage({type:"initialized"});break;case"addOscSource":let n=t.ports[0];n&&(n.onmessage=s=>{s.data.type==="osc"&&s.data.oscData&&fe(s.data.oscData,0,"",s.data.sourceId||0)},f("[OSCPreSchedulerWorker] Added external OSC source"));break;case"send":fe(e.oscData,e.sessionId||0,e.runTag||"",e.sourceId||0);break;case"sendImmediate":Ze(e.oscData,e.sourceId||0);break;case"directDispatch":x(e.oscData,!1,e.sourceId||0,!0)||m(e.oscData,"directDispatch fallback",e.sourceId||0);break;case"cancelSessionTag":e.runTag!==void 0&&e.runTag!==null&&e.runTag!==""&&Xe(e.sessionId||0,e.runTag);break;case"cancelSession":ze(e.sessionId||0);break;case"cancelTag":e.runTag!==void 0&&e.runTag!==null&&e.runTag!==""&&ve(e.runTag);break;case"cancelAll":Qe(),e.ack&&self.postMessage({type:"cancelAllAck"});break;default:}}catch(n){console.error("[OSCPreSchedulerWorker] Error:",n),self.postMessage({type:"error",error:n.message})}});f("[OSCPreSchedulerWorker] Script loaded");})();