supersonic-scsynth 0.65.0 → 0.67.2
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/LICENSE +11 -0
- package/README.md +394 -123
- package/README.npm.md +394 -123
- package/dist/osc_channel.js +1 -1
- package/dist/supersonic.js +5 -5
- package/dist/workers/debug_worker.js +3 -3
- package/dist/workers/osc_in_worker.js +1 -1
- package/dist/workers/osc_out_log_sab_worker.js +1 -1
- package/dist/workers/osc_out_prescheduler_worker.js +1 -1
- package/package.json +1 -1
- package/supersonic.d.ts +102 -10
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(()=>{function
|
|
2
|
-
`)&&(
|
|
3
|
-
`)&&(
|
|
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 +1 @@
|
|
|
1
|
-
(()=>{function
|
|
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 +1 @@
|
|
|
1
|
-
(()=>{function
|
|
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 +1 @@
|
|
|
1
|
-
(()=>{function
|
|
1
|
+
(()=>{function De(e,t,r){return(r-1-e+t)%r}function Le({uint8View:e,dataView:t,bufferStart:r,bufferSize:n,head:s,payload:c,sequence:E,messageMagic:a,headerSize:T,sourceId:u=0,headerScratch:k=null,headerScratchView:Y=null}){let M=c.length,h=T+M+3&-4,U=n-s;if(h>U){let A=k||new Uint8Array(T),D=Y||new DataView(A.buffer);D.setUint32(0,a,!0),D.setUint32(4,h,!0),D.setUint32(8,E,!0),D.setUint32(12,u,!0);let L=r+s,P=r;if(U>=T){e.set(A,L);let x=U-T;for(let i=0;i<x;i++)e[L+T+i]=c[i];for(let i=x;i<M;i++)e[P+i-x]=c[i]}else{for(let i=0;i<U;i++)e[L+i]=A[i];for(let i=U;i<T;i++)e[P+i-U]=A[i];let x=T-U;e.set(c,P+x)}}else{let A=r+s;t.setUint32(A,a,!0),t.setUint32(A+4,h,!0),t.setUint32(A+8,E,!0),t.setUint32(A+12,u,!0),e.set(c,A+T)}return(s+h)%n}function Pe(e,t,r=0,n=!1){for(let s=0;s<=r;s++)if(Atomics.compareExchange(e,t,0,1)===0)return!0;if(n){for(let c=0;c<100;c++)if(Atomics.wait(e,t,1,100),Atomics.compareExchange(e,t,0,1)===0)return!0;return console.error("[RingBuffer] Lock acquisition timeout after 10s - possible deadlock"),!1}return!1}function xe(e,t){Atomics.store(e,t,0),Atomics.notify(e,t,1)}function ne({atomicView:e,dataView:t,uint8View:r,bufferConstants:n,ringBufferBase:s,controlIndices:c,oscMessage:E,sourceId:a=0,maxSpins:T=0,useWait:u=!1,headerScratch:k=null,headerScratchView:Y=null}){let M=E.length,v=n.MESSAGE_HEADER_SIZE+M;if(v>n.IN_BUFFER_SIZE-n.MESSAGE_HEADER_SIZE||!Pe(e,c.IN_WRITE_LOCK,T,u))return!1;try{let h=Atomics.load(e,c.IN_HEAD),U=Atomics.load(e,c.IN_TAIL),A=v+3&-4;if(De(h,U,n.IN_BUFFER_SIZE)<A)return!1;let L=Atomics.add(e,c.IN_SEQUENCE,1),P=Le({uint8View:r,dataView:t,bufferStart:s+n.IN_BUFFER_START,bufferSize:n.IN_BUFFER_SIZE,head:h,payload:E,sequence:L,messageMagic:n.MESSAGE_MAGIC,headerSize:n.MESSAGE_HEADER_SIZE,sourceId:a,headerScratch:k,headerScratchView:Y});return Atomics.load(e,c.IN_HEAD),Atomics.store(e,c.IN_HEAD,P),Atomics.notify(e,c.IN_HEAD,1),!0}finally{xe(e,c.IN_WRITE_LOCK)}}function re(e,t){let r=e+t;return{IN_HEAD:(r+0)/4,IN_TAIL:(r+4)/4,IN_SEQUENCE:(r+24)/4,IN_WRITE_LOCK:(r+40)/4,IN_LOG_TAIL:(r+44)/4}}function se(e){return e.length>=8&&e[0]===35&&e[1]===98&&e[2]===117&&e[3]===110&&e[4]===100&&e[5]===108&&e[6]===101&&e[7]===0}function oe(){return(performance.timeOrigin+performance.now())/1e3+2208988800}var Oe=new Uint8Array(2097152),ct=new DataView(Oe.buffer);var it=new TextDecoder,lt=new TextEncoder;var Ne=4294967296,Et=new Uint8Array([35,98,117,110,100,108,101,0]);function ye(e){return!e||e.length<8?!1:e[0]===35&&e[1]===98}function ce(e){if(!ye(e))return null;let t=new DataView(e.buffer,e.byteOffset,e.byteLength),r=t.getUint32(8,!1),n=t.getUint32(12,!1);return r+n/Ne}var d="sab",W=null,Z=524288,we=65536,ue=3600,Ae=4294967295,g=null,H=null,f=null,O=null,Ue=null,de=null,V=null,Re=null,F={},_=null,b=null,ae=null,q=150,p=(e,t)=>{_&&(d==="sab"?Atomics.store(_,e,t):_[e]=t)},J=e=>_?d==="sab"?Atomics.load(_,e):_[e]:0,R=(e,t)=>{_&&(d==="sab"?Atomics.add(_,e,t):_[e]+=t)},w=p,fe=J,Ge=()=>{if(d!=="postMessage"||ae!==null)return;let e=()=>{b&&_&&self.postMessage({type:"preschedulerMetrics",metrics:new Uint32Array(b.slice(0))}),ae=setTimeout(e,q)};e(),S("[PreScheduler] Started metrics sending (every "+q+"ms)")};var o=[],C=null,B=1/0,ke=0,_e=!1,l=[],X=!1,N=65536,j=.5,S=(...e)=>{},ee=oe,Ye=e=>ce(e),ve=()=>{if(!g||!f){console.error("[PreScheduler] Cannot init - missing buffer or constants");return}O=new Int32Array(g),Ue=new DataView(g),de=new Uint8Array(g);let e=f.MESSAGE_HEADER_SIZE||16;V=new Uint8Array(e),Re=new DataView(V.buffer),F=re(H,f.CONTROL_START);let t=H+f.METRICS_START;_=new Uint32Array(g,t,f.METRICS_SIZE/4),S("[PreScheduler] SharedArrayBuffer initialized with direct ring buffer writing and metrics")},G=()=>{if(!_)return;p(9,o.length);let e=o.length,t=J(10);e>t&&p(10,e)},I=(e,t,r=0,n=!1)=>{if(d==="postMessage")return W?(W.postMessage({type:"osc",oscData:e,sourceId:r}),R(12,1),!0):(console.error("[PreScheduler] No worklet port available"),!1);if(!g||!O)return console.error("[PreScheduler] Not initialized for ring buffer writing"),!1;let s=e.length,c=f.MESSAGE_HEADER_SIZE+s;return c>f.IN_BUFFER_SIZE-f.MESSAGE_HEADER_SIZE?(console.error("[PreScheduler] Message too large:",c),!1):ne({atomicView:O,dataView:Ue,uint8View:de,bufferConstants:f,ringBufferBase:H,controlIndices:F,oscMessage:e,sourceId:r,maxSpins:10,useWait:n,headerScratch:V,headerScratchView:Re})?(R(12,1),!0):!1},y=(e,t,r=0)=>{let n=o.length+l.length;if(n>=N){console.error("[PreScheduler] Backpressure: dropping retry ("+n+" pending)"),R(17,1);return}l.push({oscData:e,context:t||"unknown",queuedAt:performance.now(),sourceId:r}),p(18,l.length);let s=J(19);l.length>s&&p(19,l.length),S("[PreScheduler] Queued message for retry:",t,"queue size:",l.length),Ce()},Ce=()=>{if(X||l.length===0||d!=="sab")return;X=!0;let e=Atomics.load(O,F.IN_TAIL),t=Atomics.waitAsync(O,F.IN_TAIL,e),r=()=>{X=!1,Xe(),l.length>0&&Ce()};t.async?t.value.then(r):queueMicrotask(r)},Xe=()=>{if(l.length===0)return;let e=0;for(let t=0;t<l.length;t++){let r=l[t];if(I(r.oscData,!0,r.sourceId,!0))e++,R(16,1);else break}e>0&&(l.splice(0,e),p(18,l.length))},Se=(e,t,r,n=0)=>{let s=o.length+l.length;if(s>=N){let u=`Prescheduler queue full (${s} >= ${N} max)`;return console.error("[PreScheduler]",u),self.postMessage({type:"error",error:u,code:"PRESCHEDULER_QUEUE_FULL"}),!1}let c=Ye(e);if(c===null)return S("[PreScheduler] Non-bundle message, dispatching immediately"),I(e,!1,n,!0)||y(e,"immediate message",n),!0;let E=ee(),a=c-E;if(e.length>Z){let u=`Bundle too large for scheduler pool (${e.length} > ${Z} bytes)`;return console.error("[PreScheduler]",u),self.postMessage({type:"error",error:u,code:"BUNDLE_TOO_LARGE"}),!1}if(a>ue){let u=`Bundle scheduled too far in future (${a.toFixed(0)}s > ${ue}s max)`;return console.error("[PreScheduler]",u),self.postMessage({type:"error",error:u,code:"BUNDLE_TOO_FAR_FUTURE"}),!1}let T={ntpTime:c,seq:ke++,sessionId:t||0,runTag:r||"",oscData:e,sourceId:n};return Qe(T),R(11,1),G(),S("[PreScheduler] Scheduled bundle:","NTP="+c.toFixed(3),"current="+E.toFixed(3),"wait="+(a*1e3).toFixed(1)+"ms","pending="+o.length),m(),!0},Qe=e=>{o.push(e),We(o.length-1)},he=()=>o.length>0?o[0]:null,Ke=()=>{if(o.length===0)return null;let e=o[0],t=o.pop();return o.length>0&&(o[0]=t,ge(0)),e},We=e=>{for(;e>0;){let t=Math.floor((e-1)/2);if(z(o[e],o[t])>=0)break;pe(e,t),e=t}},ge=e=>{let t=o.length;for(;;){let r=2*e+1,n=2*e+2,s=e;if(r<t&&z(o[r],o[s])<0&&(s=r),n<t&&z(o[n],o[s])<0&&(s=n),s===e)break;pe(e,s),e=s}},z=(e,t)=>e.ntpTime===t.ntpTime?e.seq-t.seq:e.ntpTime-t.ntpTime,pe=(e,t)=>{let r=o[e];o[e]=o[t],o[t]=r},m=()=>{if(o.length===0){C!==null&&(clearTimeout(C),C=null,B=1/0);return}let e=he().ntpTime-j,t=ee();if(e<B){C!==null&&clearTimeout(C);let r=Math.max(0,(e-t)*1e3);B=e,C=setTimeout(Ve,r)}},Ze=()=>{C===null&&(S("[PreScheduler] Starting demand-driven dispatching"),m())};var Ve=()=>{_e=!0;let e=ee(),t=e+j,r=0;for(;o.length>0;){let n=he();if(n.ntpTime<=t){Ke(),G();let s=n.ntpTime-e;if(R(21,1),s<0){let E=Math.round(-s*1e3);R(15,1);let a=fe(23);E>a&&w(23,E)}else{let E=Math.round(s*1e3),a=fe(14);(a===Ae||E<a)&&w(14,E)}S("[PreScheduler] Dispatching bundle:","NTP="+n.ntpTime.toFixed(3),"current="+e.toFixed(3),"early="+(s*1e3).toFixed(1)+"ms","remaining="+o.length),I(n.oscData,!1,n.sourceId,!0)||y(n.oscData,"scheduled bundle NTP="+n.ntpTime.toFixed(3),n.sourceId),r++}else break}(r>0||o.length>0||l.length>0)&&S("[PreScheduler] Dispatch cycle complete:","dispatched="+r,"pending="+o.length,"retrying="+l.length),_e=!1,C=null,B=1/0,m()},te=e=>{if(o.length===0)return;let t=o.length,r=[];for(let s=0;s<o.length;s++){let c=o[s];e(c)||r.push(c)}let n=t-r.length;n>0&&(o=r,qe(),R(13,n),G(),S("[PreScheduler] Cancelled "+n+" events, "+o.length+" remaining"),m())},qe=()=>{for(let e=Math.floor(o.length/2)-1;e>=0;e--)ge(e)},ze=(e,t)=>{te(r=>r.sessionId===e&&r.runTag===t)},$e=e=>{te(t=>t.sessionId===e)},Je=e=>{te(t=>t.runTag===e)},je=()=>{let e=o.length,t=l.length;e===0&&t===0||(R(13,e+t),o=[],l=[],p(18,0),G(),S("[PreScheduler] Cancelled all "+e+" events, "+t+" retries"),m())},et=se,tt=e=>{let t=[],r=new DataView(e.buffer,e.byteOffset,e.byteLength),n=16;for(;n+4<=e.length;){let s=r.getInt32(n,!1);if(n+=4,s<=0||s>we||n+s>e.length)break;let c=e.slice(n,n+s);for(t.push(c),n+=s;n%4!==0&&n<e.length;)n++}return t},nt=(e,t=0)=>{if(et(e)){let r=tt(e);for(let n=0;n<r.length;n++)I(r[n],!1,t,!0)||y(r[n],"immediate bundle message "+n,t)}else I(e,!1,t,!0)||y(e,"immediate message",t)};self.addEventListener("message",e=>{let{data:t}=e;try{switch(t.type){case"init":if(d=t.mode||"sab",t.maxPendingMessages&&(N=t.maxPendingMessages),t.snapshotIntervalMs&&(q=t.snapshotIntervalMs),t.bypassLookaheadS!==void 0&&(j=t.bypassLookaheadS),d==="sab")g=t.sharedBuffer,H=t.ringBufferBase,f=t.bufferConstants,ve(),f&&f.scheduler_data_pool_size&&(Z=f.scheduler_data_pool_size);else{W=t.workletPort;let n=184;b=new ArrayBuffer(n),_=new Uint32Array(b),Ge()}w(14,Ae),w(23,0),Ze(),S("[OSCPreSchedulerWorker] Initialized with NTP-based scheduling, mode="+d+", capacity="+N),self.postMessage({type:"initialized"});break;case"addOscSource":let r=e.ports[0];r&&(r.onmessage=n=>{n.data.type==="osc"&&n.data.oscData&&Se(n.data.oscData,0,"",n.data.sourceId||0)},S("[OSCPreSchedulerWorker] Added external OSC source"));break;case"send":Se(t.oscData,t.sessionId||0,t.runTag||"",t.sourceId||0);break;case"sendImmediate":nt(t.oscData,t.sourceId||0);break;case"directDispatch":I(t.oscData,!1,t.sourceId||0,!0)||y(t.oscData,"directDispatch fallback",t.sourceId||0);break;case"cancelSessionTag":t.runTag!==void 0&&t.runTag!==null&&t.runTag!==""&&ze(t.sessionId||0,t.runTag);break;case"cancelSession":$e(t.sessionId||0);break;case"cancelTag":t.runTag!==void 0&&t.runTag!==null&&t.runTag!==""&&Je(t.runTag);break;case"cancelAll":je(),t.ack&&self.postMessage({type:"cancelAllAck"});break;default:}}catch(r){console.error("[OSCPreSchedulerWorker] Error:",r),self.postMessage({type:"error",error:r.message})}});S("[OSCPreSchedulerWorker] Script loaded");})();
|
package/package.json
CHANGED
package/supersonic.d.ts
CHANGED
|
@@ -14,13 +14,9 @@
|
|
|
14
14
|
export type UUID = Uint8Array;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* A node identifier
|
|
18
|
-
*
|
|
19
|
-
* UUIDs are rewritten to i32s at the AudioWorklet boundary and back again
|
|
20
|
-
* on the way out, so concurrent clients can create and track synths without
|
|
21
|
-
* coordinating over a shared integer numbering system.
|
|
17
|
+
* A node identifier (int32).
|
|
22
18
|
*/
|
|
23
|
-
export type NodeID = number
|
|
19
|
+
export type NodeID = number;
|
|
24
20
|
|
|
25
21
|
// ============================================================================
|
|
26
22
|
// OSC Types
|
|
@@ -36,7 +32,7 @@ export type NodeID = number | UUID;
|
|
|
36
32
|
* - `boolean` → `T` / `F`
|
|
37
33
|
* - `Uint8Array` / `ArrayBuffer` → `b` (blob)
|
|
38
34
|
*
|
|
39
|
-
* For 64-bit
|
|
35
|
+
* For 64-bit or timetag types, use the tagged object form:
|
|
40
36
|
* @example
|
|
41
37
|
* { type: 'int', value: 42 }
|
|
42
38
|
* { type: 'float', value: 440 } // force float32 for whole numbers
|
|
@@ -46,7 +42,6 @@ export type NodeID = number | UUID;
|
|
|
46
42
|
* { type: 'int64', value: 9007199254740992n }
|
|
47
43
|
* { type: 'double', value: 3.141592653589793 }
|
|
48
44
|
* { type: 'timetag', value: ntpTimestamp }
|
|
49
|
-
* { type: 'uuid', value: new Uint8Array(16) }
|
|
50
45
|
*/
|
|
51
46
|
export type OscArg =
|
|
52
47
|
| number
|
|
@@ -61,8 +56,7 @@ export type OscArg =
|
|
|
61
56
|
| { type: 'bool'; value: boolean }
|
|
62
57
|
| { type: 'int64'; value: number | bigint }
|
|
63
58
|
| { type: 'double'; value: number }
|
|
64
|
-
| { type: 'timetag'; value: number }
|
|
65
|
-
| { type: 'uuid'; value: UUID };
|
|
59
|
+
| { type: 'timetag'; value: number };
|
|
66
60
|
|
|
67
61
|
/**
|
|
68
62
|
* Decoded OSC message as a plain array.
|
|
@@ -552,6 +546,8 @@ export interface RawTreeNode {
|
|
|
552
546
|
headId: NodeID;
|
|
553
547
|
/** SynthDef name (synths only, empty string for groups). */
|
|
554
548
|
defName: string;
|
|
549
|
+
/** UUID if the node was created with a UUID node ID, null otherwise. */
|
|
550
|
+
uuid: UUID | null;
|
|
555
551
|
}
|
|
556
552
|
|
|
557
553
|
/**
|
|
@@ -955,6 +951,73 @@ export class OscChannel {
|
|
|
955
951
|
*/
|
|
956
952
|
nextNodeId(): number;
|
|
957
953
|
|
|
954
|
+
/**
|
|
955
|
+
* Register a handler for OSC replies from scsynth. Idempotent — replaces
|
|
956
|
+
* any previously-registered handler. In AudioWorklet contexts the worklet
|
|
957
|
+
* must call {@link pollReplies} from `process()` to drain; in all other
|
|
958
|
+
* contexts delivery is automatic. All registered channels receive all
|
|
959
|
+
* replies (broadcast); filter locally if you only need specific addresses.
|
|
960
|
+
*
|
|
961
|
+
* SAB mode: handler receives `(view, offset, length, sequence)` — zero-copy
|
|
962
|
+
* into the shared buffer. Read bytes from `view[offset..offset+length]`.
|
|
963
|
+
* Data is valid only for the duration of the handler call.
|
|
964
|
+
*
|
|
965
|
+
* PM mode: handler receives `(oscData, sequence)` where `oscData` is a copy.
|
|
966
|
+
*
|
|
967
|
+
* @example
|
|
968
|
+
* channel.setReplyHandler((view, offset, length, sequence) => {
|
|
969
|
+
* // SAB: read raw OSC bytes from view[offset..offset+length]
|
|
970
|
+
* });
|
|
971
|
+
*/
|
|
972
|
+
setReplyHandler(
|
|
973
|
+
handler:
|
|
974
|
+
| ((view: Uint8Array, offset: number, length: number, sequence: number) => void)
|
|
975
|
+
| ((oscData: Uint8Array, sequence: number) => void),
|
|
976
|
+
): void;
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Clear the reply handler and release the reply channel. Idempotent.
|
|
980
|
+
*/
|
|
981
|
+
clearReplyHandler(): void;
|
|
982
|
+
|
|
983
|
+
/**
|
|
984
|
+
* Activate the reply slot without registering a handler. Usually called
|
|
985
|
+
* for you by {@link setReplyHandler}; only call directly if you want to
|
|
986
|
+
* claim the slot before installing a handler (or to use the optional
|
|
987
|
+
* one-shot handler argument of {@link pollReplies}).
|
|
988
|
+
*/
|
|
989
|
+
activateReplies(): void;
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Release the reply slot. Usually called for you by {@link clearReplyHandler}.
|
|
993
|
+
*/
|
|
994
|
+
deactivateReplies(): void;
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Drain pending replies, calling the registered handler (or `handler`
|
|
998
|
+
* argument, if given) once per message. Returns the number of messages
|
|
999
|
+
* processed. Zero-allocation on the hot path.
|
|
1000
|
+
*
|
|
1001
|
+
* Call from an AudioWorklet's `process()` method to receive replies on
|
|
1002
|
+
* the audio thread. In other contexts automatic delivery already calls
|
|
1003
|
+
* this for you.
|
|
1004
|
+
*
|
|
1005
|
+
* @param handler - Optional override for this call only
|
|
1006
|
+
* @returns Number of messages drained
|
|
1007
|
+
*/
|
|
1008
|
+
pollReplies(
|
|
1009
|
+
handler?:
|
|
1010
|
+
| ((view: Uint8Array, offset: number, length: number, sequence: number) => void)
|
|
1011
|
+
| ((oscData: Uint8Array, sequence: number) => void),
|
|
1012
|
+
): number;
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* Number of reply messages dropped because the reply buffer was full.
|
|
1016
|
+
* SAB mode only — undefined in PM mode or before {@link activateReplies}.
|
|
1017
|
+
* Counter resets each time the slot is (re)claimed.
|
|
1018
|
+
*/
|
|
1019
|
+
get replyDrops(): number | undefined;
|
|
1020
|
+
|
|
958
1021
|
/** Close the channel and release its ports. */
|
|
959
1022
|
close(): void;
|
|
960
1023
|
|
|
@@ -1400,6 +1463,35 @@ export class SuperSonic {
|
|
|
1400
1463
|
*/
|
|
1401
1464
|
reload(): Promise<boolean>;
|
|
1402
1465
|
|
|
1466
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1467
|
+
// State observation
|
|
1468
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* Returns true if the engine has finished booting and is ready to send
|
|
1472
|
+
* and receive messages.
|
|
1473
|
+
*
|
|
1474
|
+
* Mirrors the C++ `SupersonicEngine::isRunning()` accessor; returns the
|
|
1475
|
+
* same value as the existing `initialized` getter, exposed as a method
|
|
1476
|
+
* to match the C++ API shape.
|
|
1477
|
+
*/
|
|
1478
|
+
isRunning(): boolean;
|
|
1479
|
+
|
|
1480
|
+
/**
|
|
1481
|
+
* Returns the current engine lifecycle state.
|
|
1482
|
+
*
|
|
1483
|
+
* One of:
|
|
1484
|
+
* - `'stopped'` — before `init()` or after `shutdown()`/`destroy()`.
|
|
1485
|
+
* - `'booting'` — while `init()` is in progress.
|
|
1486
|
+
* - `'running'` — after `init()` resolves and before any teardown.
|
|
1487
|
+
*
|
|
1488
|
+
* Mirrors the C++ `SupersonicEngine::engineState()` accessor. The C++
|
|
1489
|
+
* enum also has `'restarting'` and `'error'` states that the web runtime
|
|
1490
|
+
* does not currently distinguish — `recover()` and `resume()` do not
|
|
1491
|
+
* surface a `'restarting'` state from JS.
|
|
1492
|
+
*/
|
|
1493
|
+
getEngineState(): 'stopped' | 'booting' | 'running';
|
|
1494
|
+
|
|
1403
1495
|
// ──────────────────────────────────────────────────────────────────────────
|
|
1404
1496
|
// OSC Messaging
|
|
1405
1497
|
// ──────────────────────────────────────────────────────────────────────────
|