supersonic-scsynth 0.66.0 → 0.68.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/LICENSE +11 -0
- package/README.md +395 -123
- package/README.npm.md +395 -123
- package/dist/osc_channel.js +1 -1
- package/dist/osc_fast.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 +186 -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 he(e,t,s){return(s-1-e+t)%s}function pe({uint8View:e,dataView:t,bufferStart:s,bufferSize:n,head:r,payload:c,sequence:l,messageMagic:u,headerSize:T,sourceId:E=0,headerScratch:w=null,headerScratchView:N=null}){let y=c.length,h=T+y+3&-4,R=n-r;if(h>R){let _=w||new Uint8Array(T),p=N||new DataView(_.buffer);p.setUint32(0,u,!0),p.setUint32(4,h,!0),p.setUint32(8,l,!0),p.setUint32(12,E,!0);let G=s+r,k=s;if(R>=T){e.set(_,G);let D=R-T;D>0&&e.set(c.subarray(0,D),G+T),e.set(c.subarray(D),k)}else{e.set(_.subarray(0,R),G),e.set(_.subarray(R),k);let D=T-R;e.set(c,k+D)}}else{let _=s+r;t.setUint32(_,u,!0),t.setUint32(_+4,h,!0),t.setUint32(_+8,l,!0),t.setUint32(_+12,E,!0),e.set(c,_+T)}return(r+h)%n}function Ie(e,t,s=0,n=!1){for(let r=0;r<=s;r++)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 De(e,t){Atomics.store(e,t,0),Atomics.notify(e,t,1)}function ee({atomicView:e,dataView:t,uint8View:s,bufferConstants:n,ringBufferBase:r,controlIndices:c,oscMessage:l,sourceId:u=0,maxSpins:T=0,useWait:E=!1}){let w=l.length,N=n.MESSAGE_HEADER_SIZE+w;if(N>n.IN_BUFFER_SIZE-n.MESSAGE_HEADER_SIZE||!Ie(e,c.IN_WRITE_LOCK,T,E))return!1;try{let y=Atomics.load(e,c.IN_HEAD),j=Atomics.load(e,c.IN_TAIL),h=N+3&-4;if(he(y,j,n.IN_BUFFER_SIZE)<h)return!1;let _=Atomics.add(e,c.IN_SEQUENCE,1),p=pe({uint8View:s,dataView:t,bufferStart:r+n.IN_BUFFER_START,bufferSize:n.IN_BUFFER_SIZE,head:y,payload:l,sequence:_,messageMagic:n.MESSAGE_MAGIC,headerSize:n.MESSAGE_HEADER_SIZE,sourceId:u});return Atomics.load(e,c.IN_HEAD),Atomics.store(e,c.IN_HEAD,p),Atomics.notify(e,c.IN_HEAD,1),!0}finally{De(e,c.IN_WRITE_LOCK)}}function te(e,t){let s=e+t;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 ne(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 se(){return(performance.timeOrigin+performance.now())/1e3+2208988800}var Pe=new Uint8Array(2097152),st=new DataView(Pe.buffer);var rt=new TextDecoder,ot=new TextEncoder;var Le=4294967296,ct=new Uint8Array([35,98,117,110,100,108,101,0]);function xe(e){return!e||e.length<8?!1:e[0]===35&&e[1]===98}function re(e){if(!xe(e))return null;let t=new DataView(e.buffer,e.byteOffset,e.byteLength),s=t.getUint32(8,!1),n=t.getUint32(12,!1);return s+n/Le}var A="sab",Q=null,K=1024,Fe=65536,le=3600,Se=4294967295,g=null,M=null,S=null,P=null,Te=null,Ae=null,B={},a=null,F=null,Ee=null,W=150,C=(e,t)=>{a&&(A==="sab"?Atomics.store(a,e,t):a[e]=t)},V=e=>a?A==="sab"?Atomics.load(a,e):a[e]:0,U=(e,t)=>{a&&(A==="sab"?Atomics.add(a,e,t):a[e]+=t)},H=C,ue=V,He=()=>{if(A!=="postMessage"||Ee!==null)return;let e=()=>{F&&a&&self.postMessage({type:"preschedulerMetrics",metrics:new Uint32Array(F.slice(0))}),Ee=setTimeout(e,W)};e(),f("[PreScheduler] Started metrics sending (every "+W+"ms)")};var o=[],d=null,m=1/0,be=0,ae=!1,i=[],Y=!1,L=65536,q=.5,f=(...e)=>{},$=se,we=e=>re(e),Ge=()=>{if(!g||!S){console.error("[PreScheduler] Cannot init - missing buffer or constants");return}P=new Int32Array(g),Te=new DataView(g),Ae=new Uint8Array(g),B=te(M,S.CONTROL_START);let e=M+S.METRICS_START;a=new Uint32Array(g,e,S.METRICS_SIZE/4),f("[PreScheduler] SharedArrayBuffer initialized with direct ring buffer writing and metrics")},b=()=>{if(!a)return;C(9,o.length);let e=o.length,t=V(10);e>t&&C(10,e)},I=(e,t,s=0,n=!1)=>{if(A==="postMessage")return Q?(Q.postMessage({type:"osc",oscData:e,sourceId:s}),U(12,1),!0):(console.error("[PreScheduler] No worklet port available"),!1);if(!g||!P)return console.error("[PreScheduler] Not initialized for ring buffer writing"),!1;let r=e.length,c=S.MESSAGE_HEADER_SIZE+r;return c>S.IN_BUFFER_SIZE-S.MESSAGE_HEADER_SIZE?(console.error("[PreScheduler] Message too large:",c),!1):ee({atomicView:P,dataView:Te,uint8View:Ae,bufferConstants:S,ringBufferBase:M,controlIndices:B,oscMessage:e,sourceId:s,maxSpins:10,useWait:n})?(U(12,1),!0):!1},x=(e,t,s=0)=>{let n=o.length+i.length;if(n>=L){console.error("[PreScheduler] Backpressure: dropping retry ("+n+" pending)"),U(17,1);return}i.push({oscData:e,context:t||"unknown",queuedAt:performance.now(),sourceId:s}),C(18,i.length);let r=V(19);i.length>r&&C(19,i.length),f("[PreScheduler] Queued message for retry:",t,"queue size:",i.length),Ue()},Ue=()=>{if(Y||i.length===0||A!=="sab")return;Y=!0;let e=Atomics.load(P,B.IN_TAIL),t=Atomics.waitAsync(P,B.IN_TAIL,e),s=()=>{Y=!1,ke(),i.length>0&&Ue()};t.async?t.value.then(s):queueMicrotask(s)},ke=()=>{if(i.length===0)return;let e=0;for(let t=0;t<i.length;t++){let s=i[t];if(I(s.oscData,!0,s.sourceId,!0))e++,U(16,1);else break}e>0&&(i.splice(0,e),C(18,i.length))},fe=(e,t,s,n=0)=>{let r=o.length+i.length;if(r>=L){let E=`Prescheduler queue full (${r} >= ${L} max)`;return console.error("[PreScheduler]",E),self.postMessage({type:"error",error:E,code:"PRESCHEDULER_QUEUE_FULL"}),!1}let c=we(e);if(c===null)return f("[PreScheduler] Non-bundle message, dispatching immediately"),I(e,!1,n,!0)||x(e,"immediate message",n),!0;let l=$(),u=c-l;if(e.length>K){let E=`Bundle too large for scheduler (${e.length} > ${K} bytes)`;return console.error("[PreScheduler]",E),self.postMessage({type:"error",error:E,code:"BUNDLE_TOO_LARGE"}),!1}if(u>le){let E=`Bundle scheduled too far in future (${u.toFixed(0)}s > ${le}s max)`;return console.error("[PreScheduler]",E),self.postMessage({type:"error",error:E,code:"BUNDLE_TOO_FAR_FUTURE"}),!1}let T={ntpTime:c,seq:be++,sessionId:t||0,runTag:s||"",oscData:e,sourceId:n};return Ye(T),U(11,1),b(),f("[PreScheduler] Scheduled bundle:","NTP="+c.toFixed(3),"current="+l.toFixed(3),"wait="+(u*1e3).toFixed(1)+"ms","pending="+o.length),O(),!0},Ye=e=>{o.push(e),Xe(o.length-1)},de=()=>o.length>0?o[0]:null,ve=()=>{if(o.length===0)return null;let e=o[0],t=o.pop();return o.length>0&&(o[0]=t,Re(0)),e},Xe=e=>{for(;e>0;){let t=Math.floor((e-1)/2);if(Z(o[e],o[t])>=0)break;ge(e,t),e=t}},Re=e=>{let t=o.length;for(;;){let s=2*e+1,n=2*e+2,r=e;if(s<t&&Z(o[s],o[r])<0&&(r=s),n<t&&Z(o[n],o[r])<0&&(r=n),r===e)break;ge(e,r),e=r}},Z=(e,t)=>e.ntpTime===t.ntpTime?e.seq-t.seq:e.ntpTime-t.ntpTime,ge=(e,t)=>{let s=o[e];o[e]=o[t],o[t]=s},O=()=>{if(o.length===0){d!==null&&(clearTimeout(d),d=null,m=1/0);return}let e=de().ntpTime-q,t=$();if(e<m){d!==null&&clearTimeout(d);let s=Math.max(0,(e-t)*1e3);m=e,d=setTimeout(Ke,s)}},Qe=()=>{d===null&&(f("[PreScheduler] Starting demand-driven dispatching"),O())};var Ke=()=>{ae=!0;let e=$(),t=e+q,s=0;for(;o.length>0;){let n=de();if(n.ntpTime<=t){ve(),b();let r=n.ntpTime-e;if(U(21,1),r<0){let l=Math.round(-r*1e3);U(15,1);let u=ue(23);l>u&&H(23,l)}else{let l=Math.round(r*1e3),u=ue(14);(u===Se||l<u)&&H(14,l)}f("[PreScheduler] Dispatching bundle:","NTP="+n.ntpTime.toFixed(3),"current="+e.toFixed(3),"early="+(r*1e3).toFixed(1)+"ms","remaining="+o.length),I(n.oscData,!1,n.sourceId,!0)||x(n.oscData,"scheduled bundle NTP="+n.ntpTime.toFixed(3),n.sourceId),s++}else break}(s>0||o.length>0||i.length>0)&&f("[PreScheduler] Dispatch cycle complete:","dispatched="+s,"pending="+o.length,"retrying="+i.length),ae=!1,d=null,m=1/0,O()},J=e=>{if(o.length===0)return;let t=o.length,s=[];for(let r=0;r<o.length;r++){let c=o[r];e(c)||s.push(c)}let n=t-s.length;n>0&&(o=s,We(),U(13,n),b(),f("[PreScheduler] Cancelled "+n+" events, "+o.length+" remaining"),O())},We=()=>{for(let e=Math.floor(o.length/2)-1;e>=0;e--)Re(e)},Ze=(e,t)=>{J(s=>s.sessionId===e&&s.runTag===t)},ze=e=>{J(t=>t.sessionId===e)},Ve=e=>{J(t=>t.runTag===e)},qe=()=>{let e=o.length,t=i.length;e===0&&t===0||(U(13,e+t),o=[],i=[],C(18,0),b(),f("[PreScheduler] Cancelled all "+e+" events, "+t+" retries"),O())},$e=ne,Je=e=>{let t=[],s=new DataView(e.buffer,e.byteOffset,e.byteLength),n=16;for(;n+4<=e.length;){let r=s.getInt32(n,!1);if(n+=4,r<=0||r>Fe||n+r>e.length)break;let c=e.slice(n,n+r);for(t.push(c),n+=r;n%4!==0&&n<e.length;)n++}return t},je=(e,t=0)=>{if($e(e)){let s=Je(e);for(let n=0;n<s.length;n++)I(s[n],!1,t,!0)||x(s[n],"immediate bundle message "+n,t)}else I(e,!1,t,!0)||x(e,"immediate message",t)};self.addEventListener("message",e=>{let{data:t}=e;try{switch(t.type){case"init":if(A=t.mode||"sab",t.maxPendingMessages&&(L=t.maxPendingMessages),t.snapshotIntervalMs&&(W=t.snapshotIntervalMs),t.bypassLookaheadS!==void 0&&(q=t.bypassLookaheadS),A==="sab")g=t.sharedBuffer,M=t.ringBufferBase,S=t.bufferConstants,Ge(),S&&S.scheduler_slot_size&&(K=S.scheduler_slot_size);else{Q=t.workletPort;let n=184;F=new ArrayBuffer(n),a=new Uint32Array(F),He()}H(14,Se),H(23,0),Qe(),f("[OSCPreSchedulerWorker] Initialized with NTP-based scheduling, mode="+A+", capacity="+L),self.postMessage({type:"initialized"});break;case"addOscSource":let s=e.ports[0];s&&(s.onmessage=n=>{n.data.type==="osc"&&n.data.oscData&&fe(n.data.oscData,0,"",n.data.sourceId||0)},f("[OSCPreSchedulerWorker] Added external OSC source"));break;case"send":fe(t.oscData,t.sessionId||0,t.runTag||"",t.sourceId||0);break;case"sendImmediate":je(t.oscData,t.sourceId||0);break;case"directDispatch":I(t.oscData,!1,t.sourceId||0,!0)||x(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":ze(t.sessionId||0);break;case"cancelTag":t.runTag!==void 0&&t.runTag!==null&&t.runTag!==""&&Ve(t.runTag);break;case"cancelAll":qe(),t.ack&&self.postMessage({type:"cancelAllAck"});break;default:}}catch(s){console.error("[OSCPreSchedulerWorker] Error:",s),self.postMessage({type:"error",error:s.message})}});f("[OSCPreSchedulerWorker] Script loaded");})();
|
|
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");})();
|
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
|
|
@@ -54,6 +49,7 @@ export type OscArg =
|
|
|
54
49
|
| boolean
|
|
55
50
|
| Uint8Array
|
|
56
51
|
| ArrayBuffer
|
|
52
|
+
| OscArg[]
|
|
57
53
|
| { type: 'int'; value: number }
|
|
58
54
|
| { type: 'float'; value: number }
|
|
59
55
|
| { type: 'string'; value: string }
|
|
@@ -61,8 +57,7 @@ export type OscArg =
|
|
|
61
57
|
| { type: 'bool'; value: boolean }
|
|
62
58
|
| { type: 'int64'; value: number | bigint }
|
|
63
59
|
| { type: 'double'; value: number }
|
|
64
|
-
| { type: 'timetag'; value: number }
|
|
65
|
-
| { type: 'uuid'; value: UUID };
|
|
60
|
+
| { type: 'timetag'; value: number };
|
|
66
61
|
|
|
67
62
|
/**
|
|
68
63
|
* Decoded OSC message as a plain array.
|
|
@@ -552,6 +547,8 @@ export interface RawTreeNode {
|
|
|
552
547
|
headId: NodeID;
|
|
553
548
|
/** SynthDef name (synths only, empty string for groups). */
|
|
554
549
|
defName: string;
|
|
550
|
+
/** UUID if the node was created with a UUID node ID, null otherwise. */
|
|
551
|
+
uuid: UUID | null;
|
|
555
552
|
}
|
|
556
553
|
|
|
557
554
|
/**
|
|
@@ -723,6 +720,83 @@ export interface BootStats {
|
|
|
723
720
|
initDuration: number | null;
|
|
724
721
|
}
|
|
725
722
|
|
|
723
|
+
// ============================================================================
|
|
724
|
+
// SuperClock — session-timeline service
|
|
725
|
+
// ============================================================================
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Engine session-timeline service. Tempo, beat origin, transport, and
|
|
729
|
+
* NTP-derived "now." Accessed via {@link SuperSonic.superClock}.
|
|
730
|
+
*
|
|
731
|
+
* Each field is read/written independently — no multi-field coherence
|
|
732
|
+
* guarantee. Link-specific methods are no-ops on builds without a Link
|
|
733
|
+
* backing (see individual method docs).
|
|
734
|
+
*/
|
|
735
|
+
export interface SuperClock {
|
|
736
|
+
// ── Time / drift ─────────────────────────────────────────────────────
|
|
737
|
+
|
|
738
|
+
initialize(): Promise<void>;
|
|
739
|
+
resync(): void;
|
|
740
|
+
startDriftTimer(): void;
|
|
741
|
+
stopDriftTimer(): void;
|
|
742
|
+
updateDriftOffset(): void;
|
|
743
|
+
getDriftOffset(): number;
|
|
744
|
+
getNTPStartTime(): number;
|
|
745
|
+
getClockOffset(): number;
|
|
746
|
+
setClockOffset(offsetS: number): void;
|
|
747
|
+
reset(): void;
|
|
748
|
+
|
|
749
|
+
// ── Session mutators ─────────────────────────────────────────────────
|
|
750
|
+
|
|
751
|
+
/** @param atNtpSeconds honoured by a Link backing; takes effect now in session-of-one. */
|
|
752
|
+
setBpm(bpm: number, atNtpSeconds?: number): void;
|
|
753
|
+
setIsPlaying(playing: boolean, atNtpSeconds?: number): void;
|
|
754
|
+
/** No-op without a Link backing. */
|
|
755
|
+
setLinkEnabled(enabled: boolean): void;
|
|
756
|
+
requestBeatAtTime(beat: number, atNtpSeconds: number, quantum: number): void;
|
|
757
|
+
/** Identical to {@link requestBeatAtTime} in session-of-one. */
|
|
758
|
+
forceBeatAtTime(beat: number, atNtpSeconds: number, quantum: number): void;
|
|
759
|
+
|
|
760
|
+
// ── Session getters ──────────────────────────────────────────────────
|
|
761
|
+
|
|
762
|
+
getBpm(): number;
|
|
763
|
+
isPlaying(): boolean;
|
|
764
|
+
getBeatOriginNtp(): number;
|
|
765
|
+
getIsPlayingAtNtp(): number;
|
|
766
|
+
/** Always `false` on no-Link builds. */
|
|
767
|
+
isLinkEnabled(): boolean;
|
|
768
|
+
/** Always `0` on no-Link builds. */
|
|
769
|
+
numPeers(): number;
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Current NTP time as seen by the audio thread. Use this for scheduling:
|
|
773
|
+
* `sonic.superClock.now() + 0.05` gives a timestamp 50ms in audio-clock
|
|
774
|
+
* future, which the audio thread reaches in 50ms of audio time —
|
|
775
|
+
* independent of any wall-clock-vs-audio-clock skew.
|
|
776
|
+
*/
|
|
777
|
+
now(): number;
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Compute audio-thread NTP for a specific `AudioContext.currentTime`.
|
|
781
|
+
* Lower-level than {@link now} — pass a value obtained from
|
|
782
|
+
* `audioContext.getOutputTimestamp()` for sample-aligned scheduling.
|
|
783
|
+
*/
|
|
784
|
+
nowAt(audioCurrentTime: number): number;
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Current NTP time from the system wall clock. Use only when matching
|
|
788
|
+
* against external wall-clock events; prefer {@link now} for scheduling
|
|
789
|
+
* engine events.
|
|
790
|
+
*/
|
|
791
|
+
wallNow(): number;
|
|
792
|
+
|
|
793
|
+
// ── Beat math ────────────────────────────────────────────────────────
|
|
794
|
+
|
|
795
|
+
beatAtTime(ntpSeconds: number, quantum: number): number;
|
|
796
|
+
phaseAtTime(ntpSeconds: number, quantum: number): number;
|
|
797
|
+
timeAtBeat(beat: number, quantum: number): number;
|
|
798
|
+
}
|
|
799
|
+
|
|
726
800
|
// ============================================================================
|
|
727
801
|
// Event Types
|
|
728
802
|
// ============================================================================
|
|
@@ -955,6 +1029,73 @@ export class OscChannel {
|
|
|
955
1029
|
*/
|
|
956
1030
|
nextNodeId(): number;
|
|
957
1031
|
|
|
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
|
+
|
|
958
1099
|
/** Close the channel and release its ports. */
|
|
959
1100
|
close(): void;
|
|
960
1101
|
|
|
@@ -1233,6 +1374,12 @@ export class SuperSonic {
|
|
|
1233
1374
|
/** NTP time (seconds since 1900) when the AudioContext started. Use to compute relative times: `event.timestamp - sonic.initTime`. */
|
|
1234
1375
|
get initTime(): number;
|
|
1235
1376
|
|
|
1377
|
+
/**
|
|
1378
|
+
* Session-timeline service: tempo, beat origin, transport, NTP "now."
|
|
1379
|
+
* See {@link SuperClock} for the full API surface.
|
|
1380
|
+
*/
|
|
1381
|
+
get superClock(): SuperClock;
|
|
1382
|
+
|
|
1236
1383
|
/**
|
|
1237
1384
|
* AudioWorkletNode wrapper for custom audio routing.
|
|
1238
1385
|
*
|
|
@@ -1400,6 +1547,35 @@ export class SuperSonic {
|
|
|
1400
1547
|
*/
|
|
1401
1548
|
reload(): Promise<boolean>;
|
|
1402
1549
|
|
|
1550
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1551
|
+
// State observation
|
|
1552
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* Returns true if the engine has finished booting and is ready to send
|
|
1556
|
+
* and receive messages.
|
|
1557
|
+
*
|
|
1558
|
+
* Mirrors the C++ `SupersonicEngine::isRunning()` accessor; returns the
|
|
1559
|
+
* same value as the existing `initialized` getter, exposed as a method
|
|
1560
|
+
* to match the C++ API shape.
|
|
1561
|
+
*/
|
|
1562
|
+
isRunning(): boolean;
|
|
1563
|
+
|
|
1564
|
+
/**
|
|
1565
|
+
* Returns the current engine lifecycle state.
|
|
1566
|
+
*
|
|
1567
|
+
* One of:
|
|
1568
|
+
* - `'stopped'` — before `init()` or after `shutdown()`/`destroy()`.
|
|
1569
|
+
* - `'booting'` — while `init()` is in progress.
|
|
1570
|
+
* - `'running'` — after `init()` resolves and before any teardown.
|
|
1571
|
+
*
|
|
1572
|
+
* Mirrors the C++ `SupersonicEngine::engineState()` accessor. The C++
|
|
1573
|
+
* enum also has `'restarting'` and `'error'` states that the web runtime
|
|
1574
|
+
* does not currently distinguish — `recover()` and `resume()` do not
|
|
1575
|
+
* surface a `'restarting'` state from JS.
|
|
1576
|
+
*/
|
|
1577
|
+
getEngineState(): 'stopped' | 'booting' | 'running';
|
|
1578
|
+
|
|
1403
1579
|
// ──────────────────────────────────────────────────────────────────────────
|
|
1404
1580
|
// OSC Messaging
|
|
1405
1581
|
// ──────────────────────────────────────────────────────────────────────────
|