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.
@@ -1,3 +1,3 @@
1
- (()=>{function f({uint8View:o,dataView:t,bufferStart:e,bufferSize:E,head:T,tail:C,messageMagic:I,paddingMagic:O,headerSize:R,maxMessages:l=1/0,onMessage:U,onCorruption:r}){let s=C,u=0,p=B=>{let S=B;if(S+4<=E)return t.getUint32(e+S,!0);let A=0;for(let D=0;D<4;D++)A|=o[e+(S+D)%E]<<D*8;return A};for(;s!==T&&u<l;){let B=E-s,S;if(B>=4?S=t.getUint32(e+s,!0):S=p(s),S===O){s=0;continue}if(S!==I){r&&r(s),s=(s+1)%E;continue}let A=p((s+4)%E),D=p((s+8)%E),G=p((s+12)%E);if(A<R||A>E){r&&r(s),s=(s+1)%E;continue}let M=A-R,d=e+(s+R)%E;U(d,M,D,G),s=(s+A)%E,u++}return{newTail:s,messagesRead:u}}function P(o,t){let e=o+t;return{DEBUG_HEAD:(e+16)/4,DEBUG_TAIL:(e+20)/4}}var i="sab",a=null,L=null,_=null,g=null,H=null,n=null,c={},N=null,x=!1,F=new TextDecoder("utf-8"),X=(...o)=>{},b=(o,t,e)=>{a=o,L=t,n=e,_=new Int32Array(a),g=new DataView(a),H=new Uint8Array(a),c=P(L,n.CONTROL_START);let E=L+n.METRICS_START;N=new Uint32Array(a,E,n.METRICS_SIZE/4)},W=()=>{let o=Atomics.load(_,c.DEBUG_HEAD),t=Atomics.load(_,c.DEBUG_TAIL);if(o===t)return null;let e=[],{newTail:E,messagesRead:T}=f({uint8View:H,dataView:g,bufferStart:L+n.DEBUG_BUFFER_START,bufferSize:n.DEBUG_BUFFER_SIZE,head:o,tail:t,messageMagic:n.MESSAGE_MAGIC,paddingMagic:n.PADDING_MAGIC,headerSize:n.MESSAGE_HEADER_SIZE,maxMessages:1e3,onMessage:(C,I,O,R)=>{let l=new Uint8Array(I);for(let r=0;r<I;r++)l[r]=H[C+r];let U=F.decode(l);U.endsWith(`
2
- `)&&(U=U.slice(0,-1)),e.push({text:U,timestamp:performance.now(),sequence:O}),N&&(Atomics.add(N,30,1),Atomics.add(N,31,I))},onCorruption:C=>{console.error("[DebugWorker] Corrupted message at position",C)}});return T>0&&Atomics.store(_,c.DEBUG_TAIL,E),e.length>0?e:null},w=()=>{for(;x;)try{let o=Atomics.load(_,c.DEBUG_HEAD),t=Atomics.load(_,c.DEBUG_TAIL);o===t&&Atomics.wait(_,c.DEBUG_HEAD,o);let e=W();e&&e.length>0&&self.postMessage({type:"debug",messages:e})}catch(o){console.error("[DebugWorker] Error in wait loop:",o),self.postMessage({type:"error",error:o.message}),Atomics.wait(_,0,_[0],10)}},k=()=>{if(!a){console.error("[DebugWorker] Cannot start - not initialized");return}x||(x=!0,w())},K=()=>{x=!1},Q=()=>{a&&(Atomics.store(_,c.DEBUG_HEAD,0),Atomics.store(_,c.DEBUG_TAIL,0))},h=o=>{let t=[];for(let e of o)try{let E=new Uint8Array(e.bytes),T=F.decode(E);T.endsWith(`
3
- `)&&(T=T.slice(0,-1)),t.push({text:T,timestamp:performance.now(),sequence:e.sequence})}catch(E){console.error("[DebugWorker] Failed to decode message:",E)}t.length>0&&self.postMessage({type:"debug",messages:t})};self.addEventListener("message",o=>{let{data:t}=o;try{switch(t.type){case"init":i=t.mode||"sab",i==="sab"&&b(t.sharedBuffer,t.ringBufferBase,t.bufferConstants),self.postMessage({type:"initialized"});break;case"start":i==="sab"&&k();break;case"stop":K();break;case"clear":i==="sab"&&Q();break;case"debugRaw":t.messages&&h(t.messages);break;default:}}catch(e){console.error("[DebugWorker] Error:",e),self.postMessage({type:"error",error:e.message})}});X("[DebugWorker] Script loaded");})();
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 P({uint8View:o,dataView:E,bufferStart:t,bufferSize:r,head:L,tail:a,messageMagic:p,paddingMagic:A,headerSize:R,maxMessages:l=1/0,onMessage:_,onCorruption:c}){let e=a,x=0,O=u=>{let T=u;if(T+4<=r)return E.getUint32(t+T,!0);let C=0;for(let I=0;I<4;I++)C|=o[t+(T+I)%r]<<I*8;return C};for(;e!==L&&x<l;){let u=r-e,T;if(u>=4?T=E.getUint32(t+e,!0):T=O(e),T===A){e=0;continue}if(T!==p){c&&c(e),e=(e+1)%r;continue}let C=O((e+4)%r),I=O((e+8)%r),g=O((e+12)%r);if(C<R||C>r){c&&c(e),e=(e+1)%r;continue}let m=C-R,G=t+(e+R)%r;_(G,m,I,g),e=(e+C)%r,x++}return{newTail:e,messagesRead:x}}function B(o,E){let t=o+E;return{OUT_HEAD:(t+8)/4,OUT_TAIL:(t+12)/4}}function H(){return(performance.timeOrigin+performance.now())/1e3+2208988800}var i=null,D=null,n=null,d=null,F=null,S=null,U={},s=null,N=!1,w=(...o)=>{},f=-1,K=(o,E,t)=>{i=o,D=E,S=t,n=new Int32Array(i),d=new DataView(i),F=new Uint8Array(i),U=B(D,S.CONTROL_START);let r=D+S.METRICS_START;s=new Uint32Array(i,r,S.METRICS_SIZE/4)},Q=()=>{let o=Atomics.load(n,U.OUT_HEAD),E=Atomics.load(n,U.OUT_TAIL);if(o===E)return[];let t=[],{newTail:r,messagesRead:L}=P({uint8View:F,dataView:d,bufferStart:D+S.OUT_BUFFER_START,bufferSize:S.OUT_BUFFER_SIZE,head:o,tail:E,messageMagic:S.MESSAGE_MAGIC,paddingMagic:S.PADDING_MAGIC,headerSize:S.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(a,p,A,R)=>{if(f>=0){let _=f+1&4294967295;if(A!==_){let c=A-_+4294967296&4294967295;c<1e3&&(console.error("[OSCInWorker] Detected",c,"dropped messages (expected seq",_,"got",A,")"),s&&Atomics.add(s,28,c))}}f=A;let l=new Uint8Array(p);for(let _=0;_<p;_++)l[_]=F[a+_];t.push({oscData:l,sequence:A,timestamp:H()}),s&&(Atomics.add(s,26,1),Atomics.add(s,27,p))},onCorruption:a=>{console.error("[OSCInWorker] Corrupted message at position",a),s&&(Atomics.add(s,28,1),Atomics.add(s,29,1))}});return L>0&&Atomics.store(n,U.OUT_TAIL,r),t},k=()=>{for(;N;)try{let o=Atomics.load(n,U.OUT_HEAD),E=Atomics.load(n,U.OUT_TAIL);o===E&&Atomics.wait(n,U.OUT_HEAD,o);let t=Q();t.length>0&&self.postMessage({type:"messages",messages:t})}catch(o){console.error("[OSCInWorker] Error in wait loop:",o),self.postMessage({type:"error",error:o.message}),Atomics.wait(n,0,n[0],10)}},h=()=>{if(!i){console.error("[OSCInWorker] Cannot start - not initialized");return}N||(N=!0,k())},Z=()=>{N=!1};self.addEventListener("message",o=>{let{data:E}=o;try{switch(E.type){case"init":K(E.sharedBuffer,E.ringBufferBase,E.bufferConstants),self.postMessage({type:"initialized"});break;case"start":h();break;case"stop":Z();break;default:}}catch(t){console.error("[OSCInWorker] Error:",t),self.postMessage({type:"error",error:t.message})}});w("[OSCInWorker] Script loaded");})();
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 G({uint8View:t,dataView:r,bufferStart:e,bufferSize:o,head:l,tail:C,messageMagic:f,paddingMagic:_,headerSize:A,maxMessages:S=1/0,onMessage:g,onCorruption:c}){let n=C,L=0,O=B=>{let E=B;if(E+4<=o)return r.getUint32(e+E,!0);let I=0;for(let p=0;p<4;p++)I|=t[e+(E+p)%o]<<p*8;return I};for(;n!==l&&L<S;){let B=o-n,E;if(B>=4?E=r.getUint32(e+n,!0):E=O(n),E===_){n=0;continue}if(E!==f){c&&c(n),n=(n+1)%o;continue}let I=O((n+4)%o),p=O((n+8)%o),F=O((n+12)%o);if(I<A||I>o){c&&c(n),n=(n+1)%o;continue}let R=I-A,H=e+(n+A)%o;g(H,R,p,F),n=(n+I)%o,L++}return{newTail:n,messagesRead:L}}function x(t,r){let e=t+r;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 D(){return(performance.timeOrigin+performance.now())/1e3+2208988800}var U=null,N=null,a=null,d=null,u=null,s=null,i={},m=!1,y=(...t)=>{},M=(t,r,e)=>{U=t,N=r,s=e,a=new Int32Array(U),d=new DataView(U),u=new Uint8Array(U),i=x(N,s.CONTROL_START);let o=Atomics.load(a,i.IN_HEAD);Atomics.store(a,i.IN_LOG_TAIL,o),y("Initialized, IN_LOG_TAIL set to",o)},P=t=>{let r=N+s.IN_BUFFER_START,e=s.IN_BUFFER_SIZE;if(t+4<=e)return d.getUint32(r+t,!0);let o=0;for(let l=0;l<4;l++)o|=u[r+(t+l)%e]<<l*8;return o},T=()=>{let t=Atomics.load(a,i.IN_HEAD),r=Atomics.load(a,i.IN_LOG_TAIL);if(t===r)return[];if(P(r)!==s.MESSAGE_MAGIC)return Atomics.store(a,i.IN_LOG_TAIL,t),[];let o=[],{newTail:l,messagesRead:C}=G({uint8View:u,dataView:d,bufferStart:N+s.IN_BUFFER_START,bufferSize:s.IN_BUFFER_SIZE,head:t,tail:r,messageMagic:s.MESSAGE_MAGIC,paddingMagic:s.PADDING_MAGIC,headerSize:s.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(f,_,A,S)=>{let g=new Uint8Array(_);for(let c=0;c<_;c++)g[c]=u[f+c];o.push({sourceId:S,oscData:g,sequence:A,timestamp:D()})},onCorruption:f=>{if(T._corruptCount||(T._corruptCount=0),T._corruptCount++,T._corruptCount<=3){let _=N+s.IN_BUFFER_START+f,A=d.getUint32(_,!0),S=u[_],g=u[_+1],c=u[_+2],n=u[_+3],L=Atomics.load(a,i.IN_TAIL);console.error(`[OSCOutLogWorker] Corrupted message at position ${f}: head=${t} logTail=${r} inTail=${L} got=0x${(A>>>0).toString(16).padStart(8,"0")} expected=0x${(s.MESSAGE_MAGIC>>>0).toString(16).padStart(8,"0")} bytes=[${S},${g},${c},${n}] bufStart=${N+s.IN_BUFFER_START} bufSize=${s.IN_BUFFER_SIZE}`)}else T._corruptCount===4&&console.error(`[OSCOutLogWorker] Suppressing further corruption logs (${T._corruptCount}+ total)`)}});return C>0&&Atomics.store(a,i.IN_LOG_TAIL,l),o},b=()=>{for(;m;)try{let t=Atomics.load(a,i.IN_HEAD),r=Atomics.load(a,i.IN_LOG_TAIL);t===r&&Atomics.wait(a,i.IN_HEAD,t);let e=T();e.length>0&&self.postMessage({type:"oscLog",entries:e})}catch(t){console.error("[OSCOutLogWorker] Error in wait loop:",t),self.postMessage({type:"error",error:t.message}),Atomics.wait(a,0,a[0],10)}},h=()=>{if(!U){console.error("[OSCOutLogWorker] Cannot start - not initialized");return}m||(m=!0,b())},W=()=>{m=!1};self.addEventListener("message",t=>{let{data:r}=t;try{switch(r.type){case"init":M(r.sharedBuffer,r.ringBufferBase,r.bufferConstants),self.postMessage({type:"initialized"});break;case"start":h();break;case"stop":W();break;default:}}catch(e){console.error("[OSCOutLogWorker] Error:",e),self.postMessage({type:"error",error:e.message})}});y("Script loaded");})();
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supersonic-scsynth",
3
- "version": "0.66.0",
3
+ "version": "0.68.1",
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",
package/supersonic.d.ts CHANGED
@@ -14,13 +14,9 @@
14
14
  export type UUID = Uint8Array;
15
15
 
16
16
  /**
17
- * A node identifier — either a classic i32 or a v7 UUID.
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 | UUID;
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, timetag, or UUID types, use the tagged object form:
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
  // ──────────────────────────────────────────────────────────────────────────