supersonic-scsynth 0.63.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- (()=>{function P({uint8View:o,dataView:e,bufferStart:t,bufferSize:s,head:N,tail:R,messageMagic:a,paddingMagic:A,headerSize:p,maxMessages:i=1/0,onMessage:_,onCorruption:S}){let E=R,x=0,O=B=>{let T=B;if(T+4<=s)return e.getUint32(t+T,!0);let C=0;for(let I=0;I<4;I++)C|=o[t+(T+I)%s]<<I*8;return C};for(;E!==N&&x<i;){let B=s-E,T;if(B>=4?T=e.getUint32(t+E,!0):T=O(E),T===A){E=0;continue}if(T!==a){S&&S(E),E=(E+1)%s;continue}let C=O((E+4)%s),I=O((E+8)%s),G=O((E+12)%s);if(C<p||C>s){S&&S(E),E=(E+1)%s;continue}let d=C-p,g=t+(E+p)%s;_(g,d,I,G),E=(E+C)%s,x++}return{newTail:E,messagesRead:x}}function f(o,e){let t=o+e;return{OUT_HEAD:(t+8)/4,OUT_TAIL:(t+12)/4}}var W=2208988800,X=()=>(performance.timeOrigin+performance.now())/1e3+W,D=null,l=null,n=null,M=null,F=null,c=null,U={},r=null,L=!1,Q=(...o)=>{},H=-1,K=(o,e,t)=>{D=o,l=e,c=t,n=new Int32Array(D),M=new DataView(D),F=new Uint8Array(D),U=f(l,c.CONTROL_START);let s=l+c.METRICS_START;r=new Uint32Array(D,s,c.METRICS_SIZE/4)},k=()=>{let o=Atomics.load(n,U.OUT_HEAD),e=Atomics.load(n,U.OUT_TAIL);if(o===e)return[];let t=[],{newTail:s,messagesRead:N}=P({uint8View:F,dataView:M,bufferStart:l+c.OUT_BUFFER_START,bufferSize:c.OUT_BUFFER_SIZE,head:o,tail:e,messageMagic:c.MESSAGE_MAGIC,paddingMagic:c.PADDING_MAGIC,headerSize:c.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(R,a,A,p)=>{if(H>=0){let _=H+1&4294967295;if(A!==_){let S=A-_+4294967296&4294967295;S<1e3&&(console.error("[OSCInWorker] Detected",S,"dropped messages (expected seq",_,"got",A,")"),r&&Atomics.add(r,28,S))}}H=A;let i=new Uint8Array(a);for(let _=0;_<a;_++)i[_]=F[R+_];t.push({oscData:i,sequence:A,timestamp:X()}),r&&(Atomics.add(r,26,1),Atomics.add(r,27,a))},onCorruption:R=>{console.error("[OSCInWorker] Corrupted message at position",R),r&&(Atomics.add(r,28,1),Atomics.add(r,29,1))}});return N>0&&Atomics.store(n,U.OUT_TAIL,s),t},V=()=>{for(;L;)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=k();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)}},Z=()=>{if(!D){console.error("[OSCInWorker] Cannot start - not initialized");return}L||(L=!0,V())},b=()=>{L=!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":Z();break;case"stop":b();break;default:}}catch(t){console.error("[OSCInWorker] Error:",t),self.postMessage({type:"error",error:t.message})}});Q("[OSCInWorker] Script loaded");})();
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 +1 @@
1
- (()=>{function m({uint8View:t,dataView:o,bufferStart:e,bufferSize:r,head:l,tail:d,messageMagic:g,paddingMagic:i,headerSize:u,maxMessages:N=1/0,onMessage:p,onCorruption:a}){let s=d,O=0,C=G=>{let E=G;if(E+4<=r)return o.getUint32(e+E,!0);let A=0;for(let U=0;U<4;U++)A|=t[e+(E+U)%r]<<U*8;return A};for(;s!==l&&O<N;){let G=r-s,E;if(G>=4?E=o.getUint32(e+s,!0):E=C(s),E===i){s=0;continue}if(E!==g){a&&a(s),s=(s+1)%r;continue}let A=C((s+4)%r),U=C((s+8)%r),x=C((s+12)%r);if(A<u||A>r){a&&a(s),s=(s+1)%r;continue}let R=A-u,H=e+(s+u)%r;p(H,R,U,x),s=(s+A)%r,O++}return{newTail:s,messagesRead:O}}function B(t,o){let e=t+o;return{IN_HEAD:(e+0)/4,IN_TAIL:(e+4)/4,IN_SEQUENCE:(e+24)/4,IN_WRITE_LOCK:(e+40)/4,IN_LOG_TAIL:(e+44)/4}}var F=2208988800,M=()=>(performance.timeOrigin+performance.now())/1e3+F,f=null,L=null,c=null,S=null,I=null,n=null,_={},D=!1,y=(...t)=>{},w=(t,o,e)=>{f=t,L=o,n=e,c=new Int32Array(f),S=new DataView(f),I=new Uint8Array(f),_=B(L,n.CONTROL_START);let r=Atomics.load(c,_.IN_HEAD);Atomics.store(c,_.IN_LOG_TAIL,r),y("Initialized, IN_LOG_TAIL set to",r)},$=t=>{let o=L+n.IN_BUFFER_START,e=n.IN_BUFFER_SIZE;if(t+4<=e)return S.getUint32(o+t,!0);let r=0;for(let l=0;l<4;l++)r|=I[o+(t+l)%e]<<l*8;return r},T=()=>{let t=Atomics.load(c,_.IN_HEAD),o=Atomics.load(c,_.IN_LOG_TAIL);if(t===o)return[];if($(o)!==n.MESSAGE_MAGIC)return Atomics.store(c,_.IN_LOG_TAIL,t),[];let r=[],{newTail:l,messagesRead:d}=m({uint8View:I,dataView:S,bufferStart:L+n.IN_BUFFER_START,bufferSize:n.IN_BUFFER_SIZE,head:t,tail:o,messageMagic:n.MESSAGE_MAGIC,paddingMagic:n.PADDING_MAGIC,headerSize:n.MESSAGE_HEADER_SIZE,maxMessages:100,onMessage:(g,i,u,N)=>{let p=new Uint8Array(i);for(let a=0;a<i;a++)p[a]=I[g+a];r.push({sourceId:N,oscData:p,sequence:u,timestamp:M()})},onCorruption:g=>{if(T._corruptCount||(T._corruptCount=0),T._corruptCount++,T._corruptCount<=3){let i=L+n.IN_BUFFER_START+g,u=S.getUint32(i,!0),N=I[i],p=I[i+1],a=I[i+2],s=I[i+3],O=Atomics.load(c,_.IN_TAIL);console.error(`[OSCOutLogWorker] Corrupted message at position ${g}: head=${t} logTail=${o} inTail=${O} got=0x${(u>>>0).toString(16).padStart(8,"0")} expected=0x${(n.MESSAGE_MAGIC>>>0).toString(16).padStart(8,"0")} bytes=[${N},${p},${a},${s}] bufStart=${L+n.IN_BUFFER_START} bufSize=${n.IN_BUFFER_SIZE}`)}else T._corruptCount===4&&console.error(`[OSCOutLogWorker] Suppressing further corruption logs (${T._corruptCount}+ total)`)}});return d>0&&Atomics.store(c,_.IN_LOG_TAIL,l),r},b=()=>{for(;D;)try{let t=Atomics.load(c,_.IN_HEAD),o=Atomics.load(c,_.IN_LOG_TAIL);t===o&&Atomics.wait(c,_.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(c,0,c[0],10)}},k=()=>{if(!f){console.error("[OSCOutLogWorker] Cannot start - not initialized");return}D||(D=!0,b())},W=()=>{D=!1};self.addEventListener("message",t=>{let{data:o}=t;try{switch(o.type){case"init":w(o.sharedBuffer,o.ringBufferBase,o.bufferConstants),self.postMessage({type:"initialized"});break;case"start":k();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 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 +1 @@
1
- (()=>{function j(e,t,r){return(r-1-e+t)%r}function ee({uint8View:e,dataView:t,bufferStart:r,bufferSize:s,head:n,payload:c,sequence:i,messageMagic:a,headerSize:f,sourceId:_=0,headerScratch:Y=null,headerScratchView:M=null}){let m=c.length,I=f+m+3&-4,U=s-n;if(I>U){let u=Y||new Uint8Array(f),C=M||new DataView(u.buffer);C.setUint32(0,a,!0),C.setUint32(4,I,!0),C.setUint32(8,i,!0),C.setUint32(12,_,!0);let b=r+n,w=r;if(U>=f){e.set(u,b);let L=U-f;L>0&&e.set(c.subarray(0,L),b+f),e.set(c.subarray(L),w)}else{e.set(u.subarray(0,U),b),e.set(u.subarray(U),w);let L=f-U;e.set(c,w+L)}}else{let u=r+n;t.setUint32(u,a,!0),t.setUint32(u+4,I,!0),t.setUint32(u+8,i,!0),t.setUint32(u+12,_,!0),e.set(c,u+f)}return(n+I)%s}function Ie(e,t,r=0,s=!1){for(let n=0;n<=r;n++)if(Atomics.compareExchange(e,t,0,1)===0)return!0;if(s){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 Ce(e,t){Atomics.store(e,t,0),Atomics.notify(e,t,1)}function te({atomicView:e,dataView:t,uint8View:r,bufferConstants:s,ringBufferBase:n,controlIndices:c,oscMessage:i,sourceId:a=0,maxSpins:f=0,useWait:_=!1}){let Y=i.length,M=s.MESSAGE_HEADER_SIZE+Y;if(M>s.IN_BUFFER_SIZE-s.MESSAGE_HEADER_SIZE||!Ie(e,c.IN_WRITE_LOCK,f,_))return!1;try{let m=Atomics.load(e,c.IN_HEAD),J=Atomics.load(e,c.IN_TAIL),I=M+3&-4;if(j(m,J,s.IN_BUFFER_SIZE)<I)return!1;let u=Atomics.add(e,c.IN_SEQUENCE,1),C=ee({uint8View:r,dataView:t,bufferStart:n+s.IN_BUFFER_START,bufferSize:s.IN_BUFFER_SIZE,head:m,payload:i,sequence:u,messageMagic:s.MESSAGE_MAGIC,headerSize:s.MESSAGE_HEADER_SIZE,sourceId:a});return Atomics.load(e,c.IN_HEAD),Atomics.store(e,c.IN_HEAD,C),Atomics.notify(e,c.IN_HEAD,1),!0}finally{Ce(e,c.IN_WRITE_LOCK)}}function se(e,t){let r=e+t;return{IN_HEAD:(r+0)/4,IN_TAIL:(r+4)/4,IN_SEQUENCE:(r+24)/4,IN_WRITE_LOCK:(r+40)/4,IN_LOG_TAIL:(r+44)/4}}var A="sab",v=null,K=1024,Oe=65536,ce=3600,Se=4294967295,D=null,H=null,T=null,P=null,ue=null,Te=null,B={},E=null,F=null,Ee=null,W=150,g=(e,t)=>{E&&(A==="sab"?Atomics.store(E,e,t):E[e]=t)},fe=e=>E?A==="sab"?Atomics.load(E,e):E[e]:0,p=(e,t)=>{E&&(A==="sab"?Atomics.add(E,e,t):E[e]+=t)},y=(e,t)=>{E&&(A==="sab"?Atomics.store(E,e,t):E[e]=t)},le=e=>E?A==="sab"?Atomics.load(E,e):E[e]:0,Me=()=>{if(A!=="postMessage"||Ee!==null)return;let e=()=>{F&&E&&self.postMessage({type:"preschedulerMetrics",metrics:new Uint32Array(F.slice(0))}),Ee=setTimeout(e,W)};e(),S("[PreScheduler] Started metrics sending (every "+W+"ms)")};var o=[],R=null,x=1/0,me=0,ie=!1,l=[],k=!1,h=65536,xe=2208988800,$=.5,S=(...e)=>{},V=()=>(performance.timeOrigin+performance.now())/1e3+xe,He=e=>{if(e.length>=16&&e[0]===35){let t=new DataView(e.buffer,e.byteOffset),r=t.getUint32(8,!1),s=t.getUint32(12,!1);return r+s/4294967296}return null},Be=()=>{if(!D||!T){console.error("[PreScheduler] Cannot init - missing buffer or constants");return}P=new Int32Array(D),ue=new DataView(D),Te=new Uint8Array(D),B=se(H,T.CONTROL_START);let e=H+T.METRICS_START;E=new Uint32Array(D,e,T.METRICS_SIZE/4),S("[PreScheduler] SharedArrayBuffer initialized with direct ring buffer writing and metrics")},G=()=>{if(!E)return;g(9,o.length);let e=o.length,t=fe(10);e>t&&g(10,e)},d=(e,t,r=0,s=!1)=>{if(A==="postMessage")return v?(v.postMessage({type:"osc",oscData:e,sourceId:r}),p(12,1),!0):(console.error("[PreScheduler] No worklet port available"),!1);if(!D||!P)return console.error("[PreScheduler] Not initialized for ring buffer writing"),!1;let n=e.length,c=T.MESSAGE_HEADER_SIZE+n;return c>T.IN_BUFFER_SIZE-T.MESSAGE_HEADER_SIZE?(console.error("[PreScheduler] Message too large:",c),!1):te({atomicView:P,dataView:ue,uint8View:Te,bufferConstants:T,ringBufferBase:H,controlIndices:B,oscMessage:e,sourceId:r,maxSpins:10,useWait:s})?(p(12,1),!0):!1},N=(e,t,r=0)=>{let s=o.length+l.length;if(s>=h){console.error("[PreScheduler] Backpressure: dropping retry ("+s+" pending)"),p(17,1);return}l.push({oscData:e,context:t||"unknown",queuedAt:performance.now(),sourceId:r}),g(18,l.length);let n=fe(19);l.length>n&&g(19,l.length),S("[PreScheduler] Queued message for retry:",t,"queue size:",l.length),Ae()},Ae=()=>{if(k||l.length===0||A!=="sab")return;k=!0;let e=Atomics.load(P,B.IN_TAIL),t=Atomics.waitAsync(P,B.IN_TAIL,e),r=()=>{k=!1,Fe(),l.length>0&&Ae()};t.async?t.value.then(r):queueMicrotask(r)},Fe=()=>{if(l.length===0)return;let e=0;for(;e<l.length;){let t=l[e];if(d(t.oscData,!0,t.sourceId,!0))l.splice(e,1),p(16,1),g(18,l.length);else break}},_e=(e,t,r,s=0)=>{let n=o.length+l.length;if(n>=h){let _=`Prescheduler queue full (${n} >= ${h} max)`;return console.error("[PreScheduler]",_),self.postMessage({type:"error",error:_,code:"PRESCHEDULER_QUEUE_FULL"}),!1}let c=He(e);if(c===null)return S("[PreScheduler] Non-bundle message, dispatching immediately"),d(e,!1,s,!0)||N(e,"immediate message",s),!0;let i=V(),a=c-i;if(e.length>K){let _=`Bundle too large for scheduler (${e.length} > ${K} bytes)`;return console.error("[PreScheduler]",_),self.postMessage({type:"error",error:_,code:"BUNDLE_TOO_LARGE"}),!1}if(a>ce){let _=`Bundle scheduled too far in future (${a.toFixed(0)}s > ${ce}s max)`;return console.error("[PreScheduler]",_),self.postMessage({type:"error",error:_,code:"BUNDLE_TOO_FAR_FUTURE"}),!1}let f={ntpTime:c,seq:me++,sessionId:t||0,runTag:r||"",oscData:e,sourceId:s};return ye(f),p(11,1),G(),S("[PreScheduler] Scheduled bundle:","NTP="+c.toFixed(3),"current="+i.toFixed(3),"wait="+(a*1e3).toFixed(1)+"ms","pending="+o.length),O(),!0},ye=e=>{o.push(e),Ye(o.length-1)},pe=()=>o.length>0?o[0]:null,Ge=()=>{if(o.length===0)return null;let e=o[0],t=o.pop();return o.length>0&&(o[0]=t,Re(0)),e},Ye=e=>{for(;e>0;){let t=Math.floor((e-1)/2);if(Z(o[e],o[t])>=0)break;Ue(e,t),e=t}},Re=e=>{let t=o.length;for(;;){let r=2*e+1,s=2*e+2,n=e;if(r<t&&Z(o[r],o[n])<0&&(n=r),s<t&&Z(o[s],o[n])<0&&(n=s),n===e)break;Ue(e,n),e=n}},Z=(e,t)=>e.ntpTime===t.ntpTime?e.seq-t.seq:e.ntpTime-t.ntpTime,Ue=(e,t)=>{let r=o[e];o[e]=o[t],o[t]=r},O=()=>{if(o.length===0){R!==null&&(clearTimeout(R),R=null,x=1/0);return}let e=pe().ntpTime-$,t=V();if(e<x){R!==null&&clearTimeout(R);let r=Math.max(0,(e-t)*1e3);x=e,R=setTimeout(we,r)}},be=()=>{R===null&&(S("[PreScheduler] Starting demand-driven dispatching"),O())};var we=()=>{ie=!0;let e=V(),t=e+$,r=0;for(;o.length>0;){let s=pe();if(s.ntpTime<=t){Ge(),G();let n=s.ntpTime-e;if(p(21,1),n<0){let i=Math.round(-n*1e3);p(15,1);let a=le(23);i>a&&y(23,i)}else{let i=Math.round(n*1e3),a=le(14);(a===Se||i<a)&&y(14,i)}S("[PreScheduler] Dispatching bundle:","NTP="+s.ntpTime.toFixed(3),"current="+e.toFixed(3),"early="+(n*1e3).toFixed(1)+"ms","remaining="+o.length),d(s.oscData,!1,s.sourceId,!0)||N(s.oscData,"scheduled bundle NTP="+s.ntpTime.toFixed(3),s.sourceId),r++}else break}(r>0||o.length>0||l.length>0)&&S("[PreScheduler] Dispatch cycle complete:","dispatched="+r,"pending="+o.length,"retrying="+l.length),ie=!1,R=null,x=1/0,O()},z=e=>{if(o.length===0)return;let t=o.length,r=[];for(let n=0;n<o.length;n++){let c=o[n];e(c)||r.push(c)}let s=t-r.length;s>0&&(o=r,ke(),p(13,s),G(),S("[PreScheduler] Cancelled "+s+" events, "+o.length+" remaining"),O())},ke=()=>{for(let e=Math.floor(o.length/2)-1;e>=0;e--)Re(e)},Qe=(e,t)=>{z(r=>r.sessionId===e&&r.runTag===t)},Xe=e=>{z(t=>t.sessionId===e)},ve=e=>{z(t=>t.runTag===e)},Ke=()=>{let e=o.length,t=l.length;e===0&&t===0||(p(13,e+t),o=[],l=[],g(18,0),G(),S("[PreScheduler] Cancelled all "+e+" events, "+t+" retries"),O())},We=e=>!e||e.length<8?!1:e[0]===35&&e[1]===98&&e[2]===117&&e[3]===110&&e[4]===100&&e[5]===108&&e[6]===101&&e[7]===0,Ze=e=>{let t=[],r=new DataView(e.buffer,e.byteOffset,e.byteLength),s=16;for(;s+4<=e.length;){let n=r.getInt32(s,!1);if(s+=4,n<=0||n>Oe||s+n>e.length)break;let c=e.slice(s,s+n);for(t.push(c),s+=n;s%4!==0&&s<e.length;)s++}return t},qe=(e,t=0)=>{if(We(e)){let r=Ze(e);for(let s=0;s<r.length;s++)d(r[s],!1,t,!0)||N(r[s],"immediate bundle message "+s,t)}else d(e,!1,t,!0)||N(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&&(h=t.maxPendingMessages),t.snapshotIntervalMs&&(W=t.snapshotIntervalMs),t.bypassLookaheadS!==void 0&&($=t.bypassLookaheadS),A==="sab")D=t.sharedBuffer,H=t.ringBufferBase,T=t.bufferConstants,Be(),T&&T.scheduler_slot_size&&(K=T.scheduler_slot_size);else{v=t.workletPort;let s=184;F=new ArrayBuffer(s),E=new Uint32Array(F),Me()}y(14,Se),y(23,0),be(),S("[OSCPreSchedulerWorker] Initialized with NTP-based scheduling, mode="+A+", capacity="+h),self.postMessage({type:"initialized"});break;case"addOscSource":let r=e.ports[0];r&&(r.onmessage=s=>{s.data.type==="osc"&&s.data.oscData&&_e(s.data.oscData,0,"",s.data.sourceId||0)},S("[OSCPreSchedulerWorker] Added external OSC source"));break;case"send":_e(t.oscData,t.sessionId||0,t.runTag||"",t.sourceId||0);break;case"sendImmediate":qe(t.oscData,t.sourceId||0);break;case"directDispatch":d(t.oscData,!1,t.sourceId||0,!0)||N(t.oscData,"directDispatch fallback",t.sourceId||0);break;case"cancelSessionTag":t.runTag!==void 0&&t.runTag!==null&&t.runTag!==""&&Qe(t.sessionId||0,t.runTag);break;case"cancelSession":Xe(t.sessionId||0);break;case"cancelTag":t.runTag!==void 0&&t.runTag!==null&&t.runTag!==""&&ve(t.runTag);break;case"cancelAll":Ke(),t.ack&&self.postMessage({type:"cancelAllAck"});break;default:}}catch(r){console.error("[OSCPreSchedulerWorker] Error:",r),self.postMessage({type:"error",error:r.message})}});S("[OSCPreSchedulerWorker] Script loaded");})();
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");})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supersonic-scsynth",
3
- "version": "0.63.0",
3
+ "version": "0.65.0",
4
4
  "description": "SuperCollider scsynth WebAssembly port for AudioWorklet - Run SuperCollider synthesis in the browser",
5
5
  "main": "dist/supersonic.js",
6
6
  "types": "supersonic.d.ts",
package/supersonic.d.ts CHANGED
@@ -262,6 +262,11 @@ export interface SuperSonicOptions {
262
262
  /** Line length limits for activity events emitted to listeners. */
263
263
  activityEvent?: ActivityLineConfig;
264
264
 
265
+ /** Maximum buffer pool capacity in bytes. Pool grows on demand up to this limit. Default: 256MB. */
266
+ maxBufferMemory?: number;
267
+ /** Bytes to grow the buffer pool per growth event. Default: 32MB. */
268
+ bufferGrowIncrement?: number;
269
+
265
270
  /** Max fetch retries when loading assets. Default: 3. */
266
271
  fetchMaxRetries?: number;
267
272
  /** Base delay between retries in ms (exponential backoff). Default: 1000. */
@@ -402,6 +407,14 @@ export interface SuperSonicMetrics {
402
407
  bufferPoolAvailableBytes: number;
403
408
  /** Total buffer pool allocations. */
404
409
  bufferPoolAllocations: number;
410
+ /** Buffer pool committed capacity in bytes (grows on demand). */
411
+ bufferPoolTotalCapacity: number;
412
+ /** Buffer pool hard ceiling in bytes. */
413
+ bufferPoolMaxCapacity: number;
414
+ /** Number of buffer pool growth events. */
415
+ bufferPoolGrowthCount: number;
416
+ /** Number of buffer pool segments (1 = no growth yet). */
417
+ bufferPoolPoolCount: number;
405
418
  /** Number of loaded synthdefs. */
406
419
  loadedSynthDefs: number;
407
420
  /** Maximum scsynth scheduler queue size (compile-time constant). */
@@ -416,6 +429,18 @@ export interface SuperSonicMetrics {
416
429
  debugBufferCapacity: number;
417
430
  /** Transport mode as enum index: 0=sab, 1=postMessage. */
418
431
  mode: number;
432
+
433
+ // Audio diagnostics (main thread only)
434
+ /** Audio underrun/glitch events (Chrome playbackStats, 0 on other browsers). */
435
+ glitchCount: number;
436
+ /** Total silence from audio underruns in ms (Chrome playbackStats, 0 on other browsers). */
437
+ glitchDurationMs: number;
438
+ /** Average audio output latency in microseconds (Chrome playbackStats, 0 on other browsers). */
439
+ averageLatencyUs: number;
440
+ /** Maximum audio output latency in microseconds (Chrome playbackStats, 0 on other browsers). */
441
+ maxLatencyUs: number;
442
+ /** Audio health: fraction of expected audio frames delivered, 0-100 (cross-browser). */
443
+ audioHealthPct: number;
419
444
  }
420
445
 
421
446
  /** Schema entry describing a single metric field. */
@@ -571,6 +596,7 @@ export interface SuperSonicInfo {
571
596
  crossOriginIsolated: boolean;
572
597
  atomics: boolean;
573
598
  webWorker: boolean;
599
+ playbackStats: boolean;
574
600
  };
575
601
  /** scsynth WASM version string, or null if not yet initialised. */
576
602
  version: string | null;
@@ -597,6 +623,56 @@ export interface Snapshot {
597
623
  } | null;
598
624
  }
599
625
 
626
+ /**
627
+ * System performance report returned by {@link SuperSonic.getSystemReport}.
628
+ *
629
+ * Includes hardware info, audio configuration, Chrome playbackStats (if available),
630
+ * a cross-browser audio health percentage, and a human-readable health assessment.
631
+ * Useful for diagnosing audio crackling on constrained hardware.
632
+ */
633
+ export interface SystemReport {
634
+ /** ISO 8601 timestamp when the report was generated. */
635
+ timestamp: string;
636
+ /** Hardware and browser info. */
637
+ system: {
638
+ userAgent: string;
639
+ hardwareConcurrency: number | null;
640
+ deviceMemory: number | null;
641
+ platform: string;
642
+ };
643
+ /** AudioContext configuration and state. */
644
+ audio: {
645
+ sampleRate: number;
646
+ baseLatency: number | null;
647
+ outputLatency: number | null;
648
+ state: string;
649
+ channelCount: number;
650
+ };
651
+ /** Chrome playbackStats (null on browsers without support). */
652
+ playbackStats: {
653
+ glitchCount: number;
654
+ glitchDurationS: number;
655
+ totalDurationS: number;
656
+ averageLatencyS: number;
657
+ minimumLatencyS: number;
658
+ maximumLatencyS: number;
659
+ } | null;
660
+ /** Engine configuration. */
661
+ engine: {
662
+ mode: TransportMode;
663
+ version: string | null;
664
+ bootTimeMs: number | null;
665
+ };
666
+ /** Health assessment with issues and human-readable summary. */
667
+ health: {
668
+ audioHealthPct: number;
669
+ issues: Array<{ severity: 'warning' | 'error' | 'critical'; message: string }>;
670
+ summary: string;
671
+ };
672
+ /** Full metrics snapshot at time of report. */
673
+ metrics: SuperSonicMetrics;
674
+ }
675
+
600
676
  /**
601
677
  * Metadata about decoded audio content.
602
678
  *
@@ -750,6 +826,9 @@ export interface SuperSonicEventMap {
750
826
 
751
827
  /** An asset finished loading. Size is in bytes. */
752
828
  'loading:complete': (data: { type: string; name: string; size: number }) => void;
829
+
830
+ /** Buffer pool grew on demand. Fired when the initial pool was exhausted and a new segment was added. */
831
+ 'buffer:pool:grown': (data: { poolIndex: number; newBytes: number; totalCapacity: number }) => void;
753
832
  }
754
833
 
755
834
  /** Union of all event names. */
@@ -1790,6 +1869,22 @@ export class SuperSonic {
1790
1869
  */
1791
1870
  getSnapshot(): Snapshot;
1792
1871
 
1872
+ /**
1873
+ * Get a comprehensive system performance report.
1874
+ *
1875
+ * Includes hardware info, audio configuration, Chrome playbackStats (if available),
1876
+ * a cross-browser audio health percentage, and a human-readable health assessment.
1877
+ * Useful for diagnosing audio crackling on constrained hardware.
1878
+ *
1879
+ * @example
1880
+ * const report = sonic.getSystemReport();
1881
+ * console.log(report.health.summary);
1882
+ * if (report.health.audioHealthPct < 95) {
1883
+ * console.warn('Audio thread struggling:', report.health.issues);
1884
+ * }
1885
+ */
1886
+ getSystemReport(): SystemReport;
1887
+
1793
1888
  // ──────────────────────────────────────────────────────────────────────────
1794
1889
  // Node Tree
1795
1890
  // ──────────────────────────────────────────────────────────────────────────
@@ -1842,7 +1937,13 @@ export class SuperSonic {
1842
1937
  startCapture(): void;
1843
1938
 
1844
1939
  /** Stop capturing and return the captured audio data. */
1845
- stopCapture(): Float32Array;
1940
+ stopCapture(): {
1941
+ sampleRate: number;
1942
+ channels: number;
1943
+ frames: number;
1944
+ left: Float32Array;
1945
+ right: Float32Array | null;
1946
+ };
1846
1947
 
1847
1948
  /** Check if audio capture is currently enabled. */
1848
1949
  isCaptureEnabled(): boolean;