mellon 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mellon.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Jt="0.0.6",Kt=[1,1,149,64],Yt=`https://cdn.jsdelivr.net/npm/mellon@${Jt}/dist/assets`,st={assetsPath:`${Yt}`};let I=null,q=null,tt=null;function Qt({assetsPath:i}={}){i!==void 0&&(st.assetsPath=i),I=null,q=null,tt=null}async function Vt(i){return I?(i==null||i(1),I):q||(q=(async()=>{const n=st.assetsPath.endsWith("/")?st.assetsPath:st.assetsPath+"/",t=n+"ort.all.min.mjs",e=n+"model.onnx";tt=await new Function("url","return import(url)")(t),tt.env.wasm.wasmPaths=n;const s=await fetch(e);if(!s.ok)throw new Error(`Failed to fetch model: ${s.status}`);const a=parseInt(s.headers.get("content-length")||"0",10),r=s.body.getReader(),c=[];let l=0;for(;;){const{done:m,value:_}=await r.read();if(m)break;c.push(_),l+=_.byteLength,a>0&&(i==null||i(l/a))}const h=new Uint8Array(l);let d=0;for(const m of c)h.set(m,d),d+=m.byteLength;return I=await tt.InferenceSession.create(h.buffer,{executionProviders:["wasm"],graphOptimizationLevel:"all"}),i==null||i(1),I})(),q)}async function St(i){if(!I)throw new Error("Model not loaded — call loadModel() first");const n=new tt.Tensor("float32",i,Kt),t=await I.run({input:n}),e=Object.keys(t)[0];return t[e].data}function Xt(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var mt,Et;function Zt(){if(Et)return mt;Et=1;function i(n){if(this.size=n|0,this.size<=1||(this.size&this.size-1)!==0)throw new Error("FFT size must be a power of two and bigger than 1");this._csize=n<<1;for(var t=new Array(this.size*2),e=0;e<t.length;e+=2){const l=Math.PI*e/this.size;t[e]=Math.cos(l),t[e+1]=-Math.sin(l)}this.table=t;for(var o=0,s=1;this.size>s;s<<=1)o++;this._width=o%2===0?o-1:o,this._bitrev=new Array(1<<this._width);for(var a=0;a<this._bitrev.length;a++){this._bitrev[a]=0;for(var r=0;r<this._width;r+=2){var c=this._width-r-2;this._bitrev[a]|=(a>>>r&3)<<c}}this._out=null,this._data=null,this._inv=0}return mt=i,i.prototype.fromComplexArray=function(t,e){for(var o=e||new Array(t.length>>>1),s=0;s<t.length;s+=2)o[s>>>1]=t[s];return o},i.prototype.createComplexArray=function(){const t=new Array(this._csize);for(var e=0;e<t.length;e++)t[e]=0;return t},i.prototype.toComplexArray=function(t,e){for(var o=e||this.createComplexArray(),s=0;s<o.length;s+=2)o[s]=t[s>>>1],o[s+1]=0;return o},i.prototype.completeSpectrum=function(t){for(var e=this._csize,o=e>>>1,s=2;s<o;s+=2)t[e-s]=t[s],t[e-s+1]=-t[s+1]},i.prototype.transform=function(t,e){if(t===e)throw new Error("Input and output buffers must be different");this._out=t,this._data=e,this._inv=0,this._transform4(),this._out=null,this._data=null},i.prototype.realTransform=function(t,e){if(t===e)throw new Error("Input and output buffers must be different");this._out=t,this._data=e,this._inv=0,this._realTransform4(),this._out=null,this._data=null},i.prototype.inverseTransform=function(t,e){if(t===e)throw new Error("Input and output buffers must be different");this._out=t,this._data=e,this._inv=1,this._transform4();for(var o=0;o<t.length;o++)t[o]/=this.size;this._out=null,this._data=null},i.prototype._transform4=function(){var t=this._out,e=this._csize,o=this._width,s=1<<o,a=e/s<<1,r,c,l=this._bitrev;if(a===4)for(r=0,c=0;r<e;r+=a,c++){const u=l[c];this._singleTransform2(r,u,s)}else for(r=0,c=0;r<e;r+=a,c++){const u=l[c];this._singleTransform4(r,u,s)}var h=this._inv?-1:1,d=this.table;for(s>>=2;s>=2;s>>=2){a=e/s<<1;var m=a>>>2;for(r=0;r<e;r+=a)for(var _=r+m,g=r,f=0;g<_;g+=2,f+=s){const u=g,p=u+m,v=p+m,w=v+m,b=t[u],A=t[u+1],E=t[p],y=t[p+1],F=t[v],M=t[v+1],C=t[w],T=t[w+1],x=b,R=A,z=d[f],S=h*d[f+1],N=E*z-y*S,k=E*S+y*z,P=d[2*f],L=h*d[2*f+1],G=F*P-M*L,H=F*L+M*P,J=d[3*f],K=h*d[3*f+1],Y=C*J-T*K,Q=C*K+T*J,V=x+G,W=R+H,B=x-G,X=R-H,Z=N+Y,U=k+Q,$=h*(N-Y),O=h*(k-Q),et=V+Z,ot=W+U,at=V-Z,it=W-U,ct=B+O,lt=X-$,ht=B-O,dt=X+$;t[u]=et,t[u+1]=ot,t[p]=ct,t[p+1]=lt,t[v]=at,t[v+1]=it,t[w]=ht,t[w+1]=dt}}},i.prototype._singleTransform2=function(t,e,o){const s=this._out,a=this._data,r=a[e],c=a[e+1],l=a[e+o],h=a[e+o+1],d=r+l,m=c+h,_=r-l,g=c-h;s[t]=d,s[t+1]=m,s[t+2]=_,s[t+3]=g},i.prototype._singleTransform4=function(t,e,o){const s=this._out,a=this._data,r=this._inv?-1:1,c=o*2,l=o*3,h=a[e],d=a[e+1],m=a[e+o],_=a[e+o+1],g=a[e+c],f=a[e+c+1],u=a[e+l],p=a[e+l+1],v=h+g,w=d+f,b=h-g,A=d-f,E=m+u,y=_+p,F=r*(m-u),M=r*(_-p),C=v+E,T=w+y,x=b+M,R=A-F,z=v-E,S=w-y,N=b-M,k=A+F;s[t]=C,s[t+1]=T,s[t+2]=x,s[t+3]=R,s[t+4]=z,s[t+5]=S,s[t+6]=N,s[t+7]=k},i.prototype._realTransform4=function(){var t=this._out,e=this._csize,o=this._width,s=1<<o,a=e/s<<1,r,c,l=this._bitrev;if(a===4)for(r=0,c=0;r<e;r+=a,c++){const ut=l[c];this._singleRealTransform2(r,ut>>>1,s>>>1)}else for(r=0,c=0;r<e;r+=a,c++){const ut=l[c];this._singleRealTransform4(r,ut>>>1,s>>>1)}var h=this._inv?-1:1,d=this.table;for(s>>=2;s>=2;s>>=2){a=e/s<<1;var m=a>>>1,_=m>>>1,g=_>>>1;for(r=0;r<e;r+=a)for(var f=0,u=0;f<=g;f+=2,u+=s){var p=r+f,v=p+_,w=v+_,b=w+_,A=t[p],E=t[p+1],y=t[v],F=t[v+1],M=t[w],C=t[w+1],T=t[b],x=t[b+1],R=A,z=E,S=d[u],N=h*d[u+1],k=y*S-F*N,P=y*N+F*S,L=d[2*u],G=h*d[2*u+1],H=M*L-C*G,J=M*G+C*L,K=d[3*u],Y=h*d[3*u+1],Q=T*K-x*Y,V=T*Y+x*K,W=R+H,B=z+J,X=R-H,Z=z-J,U=k+Q,$=P+V,O=h*(k-Q),et=h*(P-V),ot=W+U,at=B+$,it=X+et,ct=Z-O;if(t[p]=ot,t[p+1]=at,t[v]=it,t[v+1]=ct,f===0){var lt=W-U,ht=B-$;t[w]=lt,t[w+1]=ht;continue}if(f!==g){var dt=X,It=-Z,Dt=W,jt=-B,Wt=-h*et,Bt=-h*O,Ut=-h*$,$t=-h*U,Pt=dt+Wt,Lt=It+Bt,Gt=Dt+$t,Ht=jt-Ut,yt=r+_-f,bt=r+m-f;t[yt]=Pt,t[yt+1]=Lt,t[bt]=Gt,t[bt+1]=Ht}}}},i.prototype._singleRealTransform2=function(t,e,o){const s=this._out,a=this._data,r=a[e],c=a[e+o],l=r+c,h=r-c;s[t]=l,s[t+1]=0,s[t+2]=h,s[t+3]=0},i.prototype._singleRealTransform4=function(t,e,o){const s=this._out,a=this._data,r=this._inv?-1:1,c=o*2,l=o*3,h=a[e],d=a[e+o],m=a[e+c],_=a[e+l],g=h+m,f=h-m,u=d+_,p=r*(d-_),v=g+u,w=f,b=-p,A=g-u,E=f,y=p;s[t]=v,s[t+1]=0,s[t+2]=w,s[t+3]=b,s[t+4]=A,s[t+5]=0,s[t+6]=E,s[t+7]=y},mt}var Ot=Zt();const qt=Xt(Ot),nt=16e3,j=512,D=64,At=Math.floor(.025*nt),Ft=Math.floor(.01*nt);function Mt(i){return 2595*Math.log10(1+i/700)}function te(i){return 700*(10**(i/2595)-1)}function ee(){const i=Mt(0),n=Mt(nt/2),t=new Float64Array(D+2);for(let r=0;r<D+2;r++)t[r]=i+r*(n-i)/(D+1);const o=t.map(r=>te(r)).map(r=>Math.floor((j+1)*r/nt)),s=[],a=Math.floor(j/2)+1;for(let r=0;r<D;r++){const c=new Float32Array(a);for(let l=o[r];l<o[r+1];l++)c[l]=(l-o[r])/(o[r+1]-o[r]);for(let l=o[r+1];l<o[r+2];l++)c[l]=(o[r+2]-l)/(o[r+2]-o[r+1]);s.push(c)}return s}const se=ee(),rt=new qt(j),ft=new Float32Array(j),Ct=rt.createComplexArray(),_t=rt.createComplexArray(),Tt=new Float32Array(Math.floor(j/2)+1);function Nt(i){const n=1+Math.ceil((i.length-At)/Ft),t=new Float32Array(n*D),e=Math.floor(j/2)+1;for(let o=0;o<n;o++){const s=o*Ft;ft.fill(0);for(let a=0;a<At&&s+a<i.length;a++)ft[a]=i[s+a];rt.toComplexArray(ft,Ct),rt.transform(_t,Ct);for(let a=0;a<e;a++){const r=_t[2*a],c=_t[2*a+1],l=(r*r+c*c)/j;Tt[a]=l===0?1e-30:l}for(let a=0;a<D;a++){const r=se[a];let c=0;for(let l=0;l<e;l++)c+=Tt[l]*r[l];t[o*D+a]=Math.log(c===0?1e-30:c)}}return t}function ne(i,n){let t=0;for(let e=0;e<i.length;e++)t+=i[e]*n[e];return(t+1)/2}function re(i,n){let t=0;for(const e of n){const o=ne(i,e);o>t&&(t=o)}return t}class xt extends EventTarget{constructor({name:n,refEmbeddings:t,threshold:e=.65,relaxationMs:o=2e3,inferenceGapMs:s=300}){super(),this.name=n,this.refEmbeddings=t,this.threshold=e,this.relaxationMs=o,this.inferenceGapMs=s,this._lastDetectionAt=0,this._lastInferenceAt=0,this._lastScore=0}get lastScore(){return this._lastScore}async scoreFrame(n){const t=Date.now();if(t-this._lastInferenceAt<this.inferenceGapMs)return null;this._lastInferenceAt=t;const e=Nt(n),o=await St(e),s=re(o,this.refEmbeddings);return this._lastScore=s,s>=this.threshold&&t-this._lastDetectionAt>=this.relaxationMs&&(this._lastDetectionAt=t,this.dispatchEvent(new CustomEvent("match",{detail:{name:this.name,confidence:s,timestamp:t}}))),s}}const Rt=16e3,oe=1500,pt=24e3;function zt(i){if(i.length===pt)return i;const n=new Float32Array(pt);return n.set(i.subarray(0,pt)),n}class kt extends EventTarget{constructor(n){super(),this.wordName=n.trim().toLowerCase(),this.samples=[]}get sampleCount(){return this.samples.length}async recordSample(){const n=await navigator.mediaDevices.getUserMedia({audio:!0});return new Promise((t,e)=>{const o=new AudioContext({sampleRate:Rt}),s=new MediaRecorder(n),a=[];this.dispatchEvent(new CustomEvent("recording-start")),s.ondataavailable=r=>{r.data.size>0&&a.push(r.data)},s.onstop=async()=>{n.getTracks().forEach(r=>r.stop());try{const c=await new Blob(a,{type:"audio/webm"}).arrayBuffer(),l=await o.decodeAudioData(c);await o.close();const h=l.getChannelData(0),d=zt(new Float32Array(h)),m=this._push(d,`Recorded #${this.samples.length}`);t(m)}catch(r){await o.close().catch(()=>{}),e(r)}},s.start(),setTimeout(()=>s.stop(),oe)})}async addAudioFile(n){const t=await n.arrayBuffer(),e=new AudioContext({sampleRate:Rt}),o=await e.decodeAudioData(t);await e.close();const s=o.getChannelData(0),a=zt(new Float32Array(s));return this._push(a,n.name)}removeSample(n){this.samples.splice(n,1),this.dispatchEvent(new CustomEvent("samples-changed",{detail:{count:this.samples.length}}))}clearSamples(){this.samples=[],this.dispatchEvent(new CustomEvent("samples-changed",{detail:{count:0}}))}async generateRef(){if(this.samples.length<3)throw new Error(`Need at least 3 samples (currently have ${this.samples.length})`);this.dispatchEvent(new CustomEvent("generating",{detail:{total:this.samples.length}}));const n=[];for(let t=0;t<this.samples.length;t++){const e=Nt(this.samples[t].audioBuffer),o=await St(e);n.push(Array.from(o)),this.dispatchEvent(new CustomEvent("progress",{detail:{done:t+1,total:this.samples.length}}))}return{word_name:this.wordName,model_type:"resnet_50_arc",embeddings:n}}_push(n,t){this.samples.push({audioBuffer:n,name:t});const e=this.samples.length;return this.dispatchEvent(new CustomEvent("sample-added",{detail:{count:e,name:t}})),e}}const ae=`/**
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Kt="0.0.6",Yt=[1,1,149,64],Qt=`https://cdn.jsdelivr.net/npm/mellon@${Kt}/dist/assets`,st={assetsPath:`${Qt}`};let I=null,q=null,tt=null;function Vt({assetsPath:i}={}){i!==void 0&&(st.assetsPath=i),I=null,q=null,tt=null}async function Xt(i){return I?(i==null||i(1),I):q||(q=(async()=>{const n=st.assetsPath.endsWith("/")?st.assetsPath:st.assetsPath+"/",t=n+"ort.all.min.mjs",e=n+"model.onnx";tt=await new Function("url","return import(url)")(t),tt.env.wasm.wasmPaths=n;const s=await fetch(e);if(!s.ok)throw new Error(`Failed to fetch model: ${s.status}`);const a=parseInt(s.headers.get("content-length")||"0",10),r=s.body.getReader(),c=[];let l=0;for(;;){const{done:m,value:_}=await r.read();if(m)break;c.push(_),l+=_.byteLength,a>0&&(i==null||i(l/a))}const h=new Uint8Array(l);let d=0;for(const m of c)h.set(m,d),d+=m.byteLength;return I=await tt.InferenceSession.create(h.buffer,{executionProviders:["wasm"],graphOptimizationLevel:"all"}),i==null||i(1),I})(),q)}async function Nt(i){if(!I)throw new Error("Model not loaded — call loadModel() first");const n=new tt.Tensor("float32",i,Yt),t=await I.run({input:n}),e=Object.keys(t)[0];return t[e].data}function Zt(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var mt,At;function Ot(){if(At)return mt;At=1;function i(n){if(this.size=n|0,this.size<=1||(this.size&this.size-1)!==0)throw new Error("FFT size must be a power of two and bigger than 1");this._csize=n<<1;for(var t=new Array(this.size*2),e=0;e<t.length;e+=2){const l=Math.PI*e/this.size;t[e]=Math.cos(l),t[e+1]=-Math.sin(l)}this.table=t;for(var o=0,s=1;this.size>s;s<<=1)o++;this._width=o%2===0?o-1:o,this._bitrev=new Array(1<<this._width);for(var a=0;a<this._bitrev.length;a++){this._bitrev[a]=0;for(var r=0;r<this._width;r+=2){var c=this._width-r-2;this._bitrev[a]|=(a>>>r&3)<<c}}this._out=null,this._data=null,this._inv=0}return mt=i,i.prototype.fromComplexArray=function(t,e){for(var o=e||new Array(t.length>>>1),s=0;s<t.length;s+=2)o[s>>>1]=t[s];return o},i.prototype.createComplexArray=function(){const t=new Array(this._csize);for(var e=0;e<t.length;e++)t[e]=0;return t},i.prototype.toComplexArray=function(t,e){for(var o=e||this.createComplexArray(),s=0;s<o.length;s+=2)o[s]=t[s>>>1],o[s+1]=0;return o},i.prototype.completeSpectrum=function(t){for(var e=this._csize,o=e>>>1,s=2;s<o;s+=2)t[e-s]=t[s],t[e-s+1]=-t[s+1]},i.prototype.transform=function(t,e){if(t===e)throw new Error("Input and output buffers must be different");this._out=t,this._data=e,this._inv=0,this._transform4(),this._out=null,this._data=null},i.prototype.realTransform=function(t,e){if(t===e)throw new Error("Input and output buffers must be different");this._out=t,this._data=e,this._inv=0,this._realTransform4(),this._out=null,this._data=null},i.prototype.inverseTransform=function(t,e){if(t===e)throw new Error("Input and output buffers must be different");this._out=t,this._data=e,this._inv=1,this._transform4();for(var o=0;o<t.length;o++)t[o]/=this.size;this._out=null,this._data=null},i.prototype._transform4=function(){var t=this._out,e=this._csize,o=this._width,s=1<<o,a=e/s<<1,r,c,l=this._bitrev;if(a===4)for(r=0,c=0;r<e;r+=a,c++){const u=l[c];this._singleTransform2(r,u,s)}else for(r=0,c=0;r<e;r+=a,c++){const u=l[c];this._singleTransform4(r,u,s)}var h=this._inv?-1:1,d=this.table;for(s>>=2;s>=2;s>>=2){a=e/s<<1;var m=a>>>2;for(r=0;r<e;r+=a)for(var _=r+m,g=r,f=0;g<_;g+=2,f+=s){const u=g,p=u+m,v=p+m,w=v+m,b=t[u],A=t[u+1],E=t[p],y=t[p+1],F=t[v],C=t[v+1],M=t[w],T=t[w+1],x=b,R=A,z=d[f],S=h*d[f+1],N=E*z-y*S,k=E*S+y*z,P=d[2*f],L=h*d[2*f+1],G=F*P-C*L,H=F*L+C*P,J=d[3*f],K=h*d[3*f+1],Y=M*J-T*K,Q=M*K+T*J,V=x+G,j=R+H,B=x-G,X=R-H,Z=N+Y,U=k+Q,$=h*(N-Y),O=h*(k-Q),et=V+Z,ot=j+U,at=V-Z,it=j-U,ct=B+O,lt=X-$,ht=B-O,dt=X+$;t[u]=et,t[u+1]=ot,t[p]=ct,t[p+1]=lt,t[v]=at,t[v+1]=it,t[w]=ht,t[w+1]=dt}}},i.prototype._singleTransform2=function(t,e,o){const s=this._out,a=this._data,r=a[e],c=a[e+1],l=a[e+o],h=a[e+o+1],d=r+l,m=c+h,_=r-l,g=c-h;s[t]=d,s[t+1]=m,s[t+2]=_,s[t+3]=g},i.prototype._singleTransform4=function(t,e,o){const s=this._out,a=this._data,r=this._inv?-1:1,c=o*2,l=o*3,h=a[e],d=a[e+1],m=a[e+o],_=a[e+o+1],g=a[e+c],f=a[e+c+1],u=a[e+l],p=a[e+l+1],v=h+g,w=d+f,b=h-g,A=d-f,E=m+u,y=_+p,F=r*(m-u),C=r*(_-p),M=v+E,T=w+y,x=b+C,R=A-F,z=v-E,S=w-y,N=b-C,k=A+F;s[t]=M,s[t+1]=T,s[t+2]=x,s[t+3]=R,s[t+4]=z,s[t+5]=S,s[t+6]=N,s[t+7]=k},i.prototype._realTransform4=function(){var t=this._out,e=this._csize,o=this._width,s=1<<o,a=e/s<<1,r,c,l=this._bitrev;if(a===4)for(r=0,c=0;r<e;r+=a,c++){const ut=l[c];this._singleRealTransform2(r,ut>>>1,s>>>1)}else for(r=0,c=0;r<e;r+=a,c++){const ut=l[c];this._singleRealTransform4(r,ut>>>1,s>>>1)}var h=this._inv?-1:1,d=this.table;for(s>>=2;s>=2;s>>=2){a=e/s<<1;var m=a>>>1,_=m>>>1,g=_>>>1;for(r=0;r<e;r+=a)for(var f=0,u=0;f<=g;f+=2,u+=s){var p=r+f,v=p+_,w=v+_,b=w+_,A=t[p],E=t[p+1],y=t[v],F=t[v+1],C=t[w],M=t[w+1],T=t[b],x=t[b+1],R=A,z=E,S=d[u],N=h*d[u+1],k=y*S-F*N,P=y*N+F*S,L=d[2*u],G=h*d[2*u+1],H=C*L-M*G,J=C*G+M*L,K=d[3*u],Y=h*d[3*u+1],Q=T*K-x*Y,V=T*Y+x*K,j=R+H,B=z+J,X=R-H,Z=z-J,U=k+Q,$=P+V,O=h*(k-Q),et=h*(P-V),ot=j+U,at=B+$,it=X+et,ct=Z-O;if(t[p]=ot,t[p+1]=at,t[v]=it,t[v+1]=ct,f===0){var lt=j-U,ht=B-$;t[w]=lt,t[w+1]=ht;continue}if(f!==g){var dt=X,Dt=-Z,Wt=j,jt=-B,Bt=-h*et,Ut=-h*O,$t=-h*$,Pt=-h*U,Lt=dt+Bt,Gt=Dt+Ut,Ht=Wt+Pt,Jt=jt-$t,bt=r+_-f,Et=r+m-f;t[bt]=Lt,t[bt+1]=Gt,t[Et]=Ht,t[Et+1]=Jt}}}},i.prototype._singleRealTransform2=function(t,e,o){const s=this._out,a=this._data,r=a[e],c=a[e+o],l=r+c,h=r-c;s[t]=l,s[t+1]=0,s[t+2]=h,s[t+3]=0},i.prototype._singleRealTransform4=function(t,e,o){const s=this._out,a=this._data,r=this._inv?-1:1,c=o*2,l=o*3,h=a[e],d=a[e+o],m=a[e+c],_=a[e+l],g=h+m,f=h-m,u=d+_,p=r*(d-_),v=g+u,w=f,b=-p,A=g-u,E=f,y=p;s[t]=v,s[t+1]=0,s[t+2]=w,s[t+3]=b,s[t+4]=A,s[t+5]=0,s[t+6]=E,s[t+7]=y},mt}var qt=Ot();const te=Zt(qt),nt=16e3,W=512,D=64,Ft=Math.floor(.025*nt),Ct=Math.floor(.01*nt);function Mt(i){return 2595*Math.log10(1+i/700)}function ee(i){return 700*(10**(i/2595)-1)}function se(){const i=Mt(0),n=Mt(nt/2),t=new Float64Array(D+2);for(let r=0;r<D+2;r++)t[r]=i+r*(n-i)/(D+1);const o=t.map(r=>ee(r)).map(r=>Math.floor((W+1)*r/nt)),s=[],a=Math.floor(W/2)+1;for(let r=0;r<D;r++){const c=new Float32Array(a);for(let l=o[r];l<o[r+1];l++)c[l]=(l-o[r])/(o[r+1]-o[r]);for(let l=o[r+1];l<o[r+2];l++)c[l]=(o[r+2]-l)/(o[r+2]-o[r+1]);s.push(c)}return s}const ne=se(),rt=new te(W),ft=new Float32Array(W),Tt=rt.createComplexArray(),_t=rt.createComplexArray(),xt=new Float32Array(Math.floor(W/2)+1);function kt(i){const n=1+Math.ceil((i.length-Ft)/Ct),t=new Float32Array(n*D),e=Math.floor(W/2)+1;for(let o=0;o<n;o++){const s=o*Ct;ft.fill(0);for(let a=0;a<Ft&&s+a<i.length;a++)ft[a]=i[s+a];rt.toComplexArray(ft,Tt),rt.transform(_t,Tt);for(let a=0;a<e;a++){const r=_t[2*a],c=_t[2*a+1],l=(r*r+c*c)/W;xt[a]=l===0?1e-30:l}for(let a=0;a<D;a++){const r=ne[a];let c=0;for(let l=0;l<e;l++)c+=xt[l]*r[l];t[o*D+a]=Math.log(c===0?1e-30:c)}}return t}function re(i,n){let t=0;for(let e=0;e<i.length;e++)t+=i[e]*n[e];return(t+1)/2}function oe(i,n){let t=0;for(const e of n){const o=re(i,e);o>t&&(t=o)}return t}class Rt extends EventTarget{constructor({name:n,refEmbeddings:t,threshold:e=.65,relaxationMs:o=2e3,inferenceGapMs:s=300}){super(),this.name=n,this.refEmbeddings=t,this.threshold=e,this.relaxationMs=o,this.inferenceGapMs=s,this._lastDetectionAt=0,this._lastInferenceAt=0,this._lastScore=0}get lastScore(){return this._lastScore}async scoreFrame(n){const t=Date.now();if(t-this._lastInferenceAt<this.inferenceGapMs)return null;this._lastInferenceAt=t;const e=kt(n),o=await Nt(e),s=oe(o,this.refEmbeddings);return this._lastScore=s,s>=this.threshold&&t-this._lastDetectionAt>=this.relaxationMs&&(this._lastDetectionAt=t,this.dispatchEvent(new CustomEvent("match",{detail:{name:this.name,confidence:s,timestamp:t}}))),s}}const zt=16e3,ae=1500,pt=24e3;function St(i){if(i.length===pt)return i;const n=new Float32Array(pt);return n.set(i.subarray(0,pt)),n}class It extends EventTarget{constructor(n){super(),this.wordName=n.trim().toLowerCase(),this.samples=[]}get sampleCount(){return this.samples.length}async recordSample(){const n=await navigator.mediaDevices.getUserMedia({audio:!0});return new Promise((t,e)=>{const o=new AudioContext({sampleRate:zt}),s=new MediaRecorder(n),a=[];this.dispatchEvent(new CustomEvent("recording-start")),s.ondataavailable=r=>{r.data.size>0&&a.push(r.data)},s.onstop=async()=>{n.getTracks().forEach(r=>r.stop());try{const c=await new Blob(a,{type:"audio/webm"}).arrayBuffer(),l=await o.decodeAudioData(c);await o.close();const h=l.getChannelData(0),d=St(new Float32Array(h)),m=this._push(d,`Recorded #${this.samples.length}`);t(m)}catch(r){await o.close().catch(()=>{}),e(r)}},s.start(),setTimeout(()=>s.stop(),ae)})}async addAudioFile(n){const t=await n.arrayBuffer(),e=new AudioContext({sampleRate:zt}),o=await e.decodeAudioData(t);await e.close();const s=o.getChannelData(0),a=St(new Float32Array(s));return this._push(a,n.name)}removeSample(n){this.samples.splice(n,1),this.dispatchEvent(new CustomEvent("samples-changed",{detail:{count:this.samples.length}}))}clearSamples(){this.samples=[],this.dispatchEvent(new CustomEvent("samples-changed",{detail:{count:0}}))}async generateRef(){if(this.samples.length<3)throw new Error(`Need at least 3 samples (currently have ${this.samples.length})`);this.dispatchEvent(new CustomEvent("generating",{detail:{total:this.samples.length}}));const n=[];for(let t=0;t<this.samples.length;t++){const e=kt(this.samples[t].audioBuffer),o=await Nt(e);n.push(Array.from(o)),this.dispatchEvent(new CustomEvent("progress",{detail:{done:t+1,total:this.samples.length}}))}return{word_name:this.wordName,model_type:"resnet_50_arc",embeddings:n}}_push(n,t){this.samples.push({audioBuffer:n,name:t});const e=this.samples.length;return this.dispatchEvent(new CustomEvent("sample-added",{detail:{count:e,name:t}})),e}}const ie=`/**
2
2
  * public/audio-processor.js
3
3
  * AudioWorklet that runs at 16 kHz and continuously emits the last
4
4
  * 1.5-second window (24 000 samples) via a circular buffer.
@@ -35,4 +35,4 @@ class AudioProcessor extends AudioWorkletProcessor {
35
35
  }
36
36
 
37
37
  registerProcessor('audio-processor', AudioProcessor)
38
- `;let vt=null;function ie(){if(!vt){const i=new Blob([ae],{type:"application/javascript"});vt=URL.createObjectURL(i)}return vt}const wt="mellon_custom_refs";function gt(){try{const i=localStorage.getItem(wt);return i?JSON.parse(i):[]}catch{return[]}}function ce(i){const n=gt().filter(t=>t.word_name!==i.word_name);n.push(i),localStorage.setItem(wt,JSON.stringify(n))}function le(i){const n=gt().filter(t=>t.word_name!==i);localStorage.setItem(wt,JSON.stringify(n))}function he(i){const n=JSON.stringify(i,null,2),t=new Blob([n],{type:"application/json"}),e=URL.createObjectURL(t),o=Object.assign(document.createElement("a"),{href:e,download:`${i.word_name}_ref.json`});document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(e)}async function de(i){const n=await i.text();let t;try{t=JSON.parse(n)}catch{throw new Error("Invalid JSON")}if(!t.embeddings||!Array.isArray(t.embeddings)||!t.embeddings.length)throw new Error('Missing or empty "embeddings" array');if(!Array.isArray(t.embeddings[0]))throw new Error('"embeddings" must be a 2D array');return t.word_name||(t.word_name=i.name.replace(/_ref\.json$/i,"").replace(/\.json$/i,"")),t}class ue extends EventTarget{constructor(n={}){super(),this._opts={words:n.words??[],refs:n.refs??[],threshold:n.threshold??.65,relaxationMs:n.relaxationMs??2e3,inferenceGapMs:n.inferenceGapMs??300,assetsPath:n.assetsPath},this._refs=new Map,this._detectors=new Map,this._audioCtx=null,this._workletNode=null,this._stream=null,this._initialized=!1,this._running=!1}get isInitialized(){return this._initialized}get isRunning(){return this._running}async init(n){if(this._initialized){n==null||n(1);return}this._opts.assetsPath&&Qt({assetsPath:this._opts.assetsPath});try{await Vt(n)}catch(t){throw this.dispatchEvent(new CustomEvent("error",{detail:{error:t}})),t}for(const t of this._opts.refs)try{let e;if(console.log("fetching ref : ",t),typeof t=="string"){const o=await fetch(t);if(!o.ok)throw new Error(`HTTP ${o.status}`);e=await o.json()}else e=t;this.addCustomWord(e)}catch(e){const o=typeof t=="string"?t:t.word_name;console.warn(`[Mellon] Failed to load ref "${o}": ${e.message}`)}this._initialized=!0,this.dispatchEvent(new CustomEvent("ready"))}async start(n){this._initialized||await this.init();const t=n??this._opts.words;try{this._stream=await navigator.mediaDevices.getUserMedia({audio:!0})}catch(s){const a=new Error(`Microphone access denied: ${s.message}`);throw this.dispatchEvent(new CustomEvent("error",{detail:{error:a}})),a}this._audioCtx=new AudioContext({sampleRate:16e3});const e=ie();await this._audioCtx.audioWorklet.addModule(e);const o=this._audioCtx.createMediaStreamSource(this._stream);this._workletNode=new AudioWorkletNode(this._audioCtx,"audio-processor"),o.connect(this._workletNode),this._workletNode.connect(this._audioCtx.destination);for(const s of t){const a=this._refs.get(s);if(!a){console.warn(`[Mellon] No reference embeddings for "${s}" — skipping. Call addCustomWord() to register custom words before start().`);continue}const r=new xt({name:s,refEmbeddings:a.embeddings,threshold:this._opts.threshold,relaxationMs:this._opts.relaxationMs,inferenceGapMs:this._opts.inferenceGapMs});r.addEventListener("match",c=>{this.dispatchEvent(new CustomEvent("match",{detail:c.detail}))}),this._detectors.set(s,r)}this._workletNode.port.onmessage=async s=>{const a=[];for(const r of this._detectors.values())a.push(r.scoreFrame(s.data));await Promise.allSettled(a)},this._running=!0}stop(){this._workletNode&&(this._workletNode.port.onmessage=null,this._workletNode.disconnect(),this._workletNode=null),this._stream&&(this._stream.getTracks().forEach(n=>n.stop()),this._stream=null),this._audioCtx&&(this._audioCtx.close(),this._audioCtx=null),this._detectors.clear(),this._running=!1}addCustomWord(n){if(this._refs.set(n.word_name,n),this._running&&this._workletNode){const t=new xt({name:n.word_name,refEmbeddings:n.embeddings,threshold:this._opts.threshold,relaxationMs:this._opts.relaxationMs,inferenceGapMs:this._opts.inferenceGapMs});t.addEventListener("match",e=>{this.dispatchEvent(new CustomEvent("match",{detail:e.detail}))}),this._detectors.set(n.word_name,t)}}enrollWord(n){return new kt(n)}static loadWords(){return gt()}static saveWord(n){ce(n)}static deleteWord(n){le(n)}static importWordFile(n){return de(n)}static exportWord(n){he(n)}}exports.EnrollmentSession=kt;exports.Mellon=ue;
38
+ `;let vt=null;function ce(){if(!vt){const i=new Blob([ie],{type:"application/javascript"});vt=URL.createObjectURL(i)}return vt}const wt="mellon_custom_refs";function gt(){try{const i=localStorage.getItem(wt);return i?JSON.parse(i):[]}catch{return[]}}function le(i){const n=gt().filter(t=>t.word_name!==i.word_name);n.push(i),localStorage.setItem(wt,JSON.stringify(n))}function he(i){const n=gt().filter(t=>t.word_name!==i);localStorage.setItem(wt,JSON.stringify(n))}function de(i){const n=JSON.stringify(i,null,2),t=new Blob([n],{type:"application/json"}),e=URL.createObjectURL(t),o=Object.assign(document.createElement("a"),{href:e,download:`${i.word_name}_ref.json`});document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(e)}async function ue(i){const n=await i.text();let t;try{t=JSON.parse(n)}catch{throw new Error("Invalid JSON")}if(!t.embeddings||!Array.isArray(t.embeddings)||!t.embeddings.length)throw new Error('Missing or empty "embeddings" array');if(!Array.isArray(t.embeddings[0]))throw new Error('"embeddings" must be a 2D array');return t.word_name||(t.word_name=i.name.replace(/_ref\.json$/i,"").replace(/\.json$/i,"")),t}class yt extends EventTarget{constructor(n={}){super(),this._opts={words:n.words??[],refs:n.refs??[],threshold:n.threshold??.65,relaxationMs:n.relaxationMs??2e3,inferenceGapMs:n.inferenceGapMs??300,assetsPath:n.assetsPath},this._refs=new Map,this._detectors=new Map,this._audioCtx=null,this._workletNode=null,this._stream=null,this._initialized=!1,this._running=!1}get isInitialized(){return this._initialized}get isRunning(){return this._running}async init(n){if(this._initialized){n==null||n(1);return}this._opts.assetsPath&&Vt({assetsPath:this._opts.assetsPath});try{await Xt(n)}catch(t){throw this.dispatchEvent(new CustomEvent("error",{detail:{error:t}})),t}for(const t of this._opts.refs)try{let e;if(console.log("fetching ref : ",t),typeof t=="string"){const o=await fetch(t);if(!o.ok)throw new Error(`HTTP ${o.status}`);e=await o.json()}else e=t;console.log({refData:e}),yt.saveWord(e),this.addCustomWord(e)}catch(e){const o=typeof t=="string"?t:t.word_name;console.warn(`[Mellon] Failed to load ref "${o}": ${e.message}`)}this._initialized=!0,this.dispatchEvent(new CustomEvent("ready"))}async start(n){this._initialized||await this.init();const t=n??this._opts.words;try{this._stream=await navigator.mediaDevices.getUserMedia({audio:!0})}catch(s){const a=new Error(`Microphone access denied: ${s.message}`);throw this.dispatchEvent(new CustomEvent("error",{detail:{error:a}})),a}this._audioCtx=new AudioContext({sampleRate:16e3});const e=ce();await this._audioCtx.audioWorklet.addModule(e);const o=this._audioCtx.createMediaStreamSource(this._stream);this._workletNode=new AudioWorkletNode(this._audioCtx,"audio-processor"),o.connect(this._workletNode),this._workletNode.connect(this._audioCtx.destination);for(const s of t){const a=this._refs.get(s);if(!a){console.warn(`[Mellon] No reference embeddings for "${s}" — skipping. Call addCustomWord() to register custom words before start().`);continue}const r=new Rt({name:s,refEmbeddings:a.embeddings,threshold:this._opts.threshold,relaxationMs:this._opts.relaxationMs,inferenceGapMs:this._opts.inferenceGapMs});r.addEventListener("match",c=>{this.dispatchEvent(new CustomEvent("match",{detail:c.detail}))}),this._detectors.set(s,r)}this._workletNode.port.onmessage=async s=>{const a=[];for(const r of this._detectors.values())a.push(r.scoreFrame(s.data));await Promise.allSettled(a)},this._running=!0}stop(){this._workletNode&&(this._workletNode.port.onmessage=null,this._workletNode.disconnect(),this._workletNode=null),this._stream&&(this._stream.getTracks().forEach(n=>n.stop()),this._stream=null),this._audioCtx&&(this._audioCtx.close(),this._audioCtx=null),this._detectors.clear(),this._running=!1}addCustomWord(n){if(this._refs.set(n.word_name,n),this._running&&this._workletNode){const t=new Rt({name:n.word_name,refEmbeddings:n.embeddings,threshold:this._opts.threshold,relaxationMs:this._opts.relaxationMs,inferenceGapMs:this._opts.inferenceGapMs});t.addEventListener("match",e=>{this.dispatchEvent(new CustomEvent("match",{detail:e.detail}))}),this._detectors.set(n.word_name,t)}}enrollWord(n){return new It(n)}static loadWords(){return gt()}static saveWord(n){le(n)}static deleteWord(n){he(n)}static importWordFile(n){return ue(n)}static exportWord(n){de(n)}}exports.EnrollmentSession=It;exports.Mellon=yt;
package/dist/mellon.mjs CHANGED
@@ -1,11 +1,11 @@
1
- const Ht = "0.0.6", Jt = [1, 1, 149, 64], Kt = `https://cdn.jsdelivr.net/npm/mellon@${Ht}/dist/assets`, st = {
2
- assetsPath: `${Kt}`
1
+ const Jt = "0.0.6", Kt = [1, 1, 149, 64], Yt = `https://cdn.jsdelivr.net/npm/mellon@${Jt}/dist/assets`, st = {
2
+ assetsPath: `${Yt}`
3
3
  };
4
4
  let I = null, q = null, tt = null;
5
- function Yt({ assetsPath: i } = {}) {
5
+ function Qt({ assetsPath: i } = {}) {
6
6
  i !== void 0 && (st.assetsPath = i), I = null, q = null, tt = null;
7
7
  }
8
- async function Qt(i) {
8
+ async function Vt(i) {
9
9
  return I ? (i == null || i(1), I) : q || (q = (async () => {
10
10
  const n = st.assetsPath.endsWith("/") ? st.assetsPath : st.assetsPath + "/", t = n + "ort.all.min.mjs", e = n + "model.onnx";
11
11
  tt = await new Function("url", "return import(url)")(t), tt.env.wasm.wasmPaths = n;
@@ -30,14 +30,14 @@ async function Qt(i) {
30
30
  }
31
31
  async function St(i) {
32
32
  if (!I) throw new Error("Model not loaded — call loadModel() first");
33
- const n = new tt.Tensor("float32", i, Jt), t = await I.run({ input: n }), e = Object.keys(t)[0];
33
+ const n = new tt.Tensor("float32", i, Kt), t = await I.run({ input: n }), e = Object.keys(t)[0];
34
34
  return t[e].data;
35
35
  }
36
- function Vt(i) {
36
+ function Xt(i) {
37
37
  return i && i.__esModule && Object.prototype.hasOwnProperty.call(i, "default") ? i.default : i;
38
38
  }
39
39
  var mt, Et;
40
- function Xt() {
40
+ function Zt() {
41
41
  if (Et) return mt;
42
42
  Et = 1;
43
43
  function i(n) {
@@ -145,8 +145,8 @@ function Xt() {
145
145
  continue;
146
146
  }
147
147
  if (f !== g) {
148
- var dt = X, kt = -Z, It = j, Dt = -B, Wt = -h * et, jt = -h * O, Bt = -h * $, Ut = -h * U, $t = dt + Wt, Lt = kt + jt, Pt = It + Ut, Gt = Dt - Bt, yt = r + _ - f, bt = r + m - f;
149
- t[yt] = $t, t[yt + 1] = Lt, t[bt] = Pt, t[bt + 1] = Gt;
148
+ var dt = X, It = -Z, Dt = j, Wt = -B, jt = -h * et, Bt = -h * O, Ut = -h * $, $t = -h * U, Lt = dt + jt, Pt = It + Bt, Gt = Dt + $t, Ht = Wt - Ut, yt = r + _ - f, bt = r + m - f;
149
+ t[yt] = Lt, t[yt + 1] = Pt, t[bt] = Gt, t[bt + 1] = Ht;
150
150
  }
151
151
  }
152
152
  }
@@ -158,19 +158,19 @@ function Xt() {
158
158
  s[t] = v, s[t + 1] = 0, s[t + 2] = w, s[t + 3] = b, s[t + 4] = A, s[t + 5] = 0, s[t + 6] = E, s[t + 7] = y;
159
159
  }, mt;
160
160
  }
161
- var Zt = Xt();
162
- const Ot = /* @__PURE__ */ Vt(Zt), nt = 16e3, W = 512, D = 64, At = Math.floor(0.025 * nt), Ft = Math.floor(0.01 * nt);
161
+ var Ot = Zt();
162
+ const qt = /* @__PURE__ */ Xt(Ot), nt = 16e3, W = 512, D = 64, At = Math.floor(0.025 * nt), Ft = Math.floor(0.01 * nt);
163
163
  function Ct(i) {
164
164
  return 2595 * Math.log10(1 + i / 700);
165
165
  }
166
- function qt(i) {
166
+ function te(i) {
167
167
  return 700 * (10 ** (i / 2595) - 1);
168
168
  }
169
- function te() {
169
+ function ee() {
170
170
  const i = Ct(0), n = Ct(nt / 2), t = new Float64Array(D + 2);
171
171
  for (let r = 0; r < D + 2; r++)
172
172
  t[r] = i + r * (n - i) / (D + 1);
173
- const o = t.map((r) => qt(r)).map((r) => Math.floor((W + 1) * r / nt)), s = [], a = Math.floor(W / 2) + 1;
173
+ const o = t.map((r) => te(r)).map((r) => Math.floor((W + 1) * r / nt)), s = [], a = Math.floor(W / 2) + 1;
174
174
  for (let r = 0; r < D; r++) {
175
175
  const c = new Float32Array(a);
176
176
  for (let l = o[r]; l < o[r + 1]; l++) c[l] = (l - o[r]) / (o[r + 1] - o[r]);
@@ -179,7 +179,7 @@ function te() {
179
179
  }
180
180
  return s;
181
181
  }
182
- const ee = te(), rt = new Ot(W), ft = new Float32Array(W), Mt = rt.createComplexArray(), _t = rt.createComplexArray(), Tt = new Float32Array(Math.floor(W / 2) + 1);
182
+ const se = ee(), rt = new qt(W), ft = new Float32Array(W), Mt = rt.createComplexArray(), _t = rt.createComplexArray(), Tt = new Float32Array(Math.floor(W / 2) + 1);
183
183
  function Nt(i) {
184
184
  const n = 1 + Math.ceil((i.length - At) / Ft), t = new Float32Array(n * D), e = Math.floor(W / 2) + 1;
185
185
  for (let o = 0; o < n; o++) {
@@ -193,7 +193,7 @@ function Nt(i) {
193
193
  Tt[a] = l === 0 ? 1e-30 : l;
194
194
  }
195
195
  for (let a = 0; a < D; a++) {
196
- const r = ee[a];
196
+ const r = se[a];
197
197
  let c = 0;
198
198
  for (let l = 0; l < e; l++) c += Tt[l] * r[l];
199
199
  t[o * D + a] = Math.log(c === 0 ? 1e-30 : c);
@@ -201,15 +201,15 @@ function Nt(i) {
201
201
  }
202
202
  return t;
203
203
  }
204
- function se(i, n) {
204
+ function ne(i, n) {
205
205
  let t = 0;
206
206
  for (let e = 0; e < i.length; e++) t += i[e] * n[e];
207
207
  return (t + 1) / 2;
208
208
  }
209
- function ne(i, n) {
209
+ function re(i, n) {
210
210
  let t = 0;
211
211
  for (const e of n) {
212
- const o = se(i, e);
212
+ const o = ne(i, e);
213
213
  o > t && (t = o);
214
214
  }
215
215
  return t;
@@ -239,19 +239,19 @@ class xt extends EventTarget {
239
239
  const t = Date.now();
240
240
  if (t - this._lastInferenceAt < this.inferenceGapMs) return null;
241
241
  this._lastInferenceAt = t;
242
- const e = Nt(n), o = await St(e), s = ne(o, this.refEmbeddings);
242
+ const e = Nt(n), o = await St(e), s = re(o, this.refEmbeddings);
243
243
  return this._lastScore = s, s >= this.threshold && t - this._lastDetectionAt >= this.relaxationMs && (this._lastDetectionAt = t, this.dispatchEvent(new CustomEvent("match", {
244
244
  detail: { name: this.name, confidence: s, timestamp: t }
245
245
  }))), s;
246
246
  }
247
247
  }
248
- const Rt = 16e3, re = 1500, pt = 24e3;
248
+ const Rt = 16e3, oe = 1500, pt = 24e3;
249
249
  function zt(i) {
250
250
  if (i.length === pt) return i;
251
251
  const n = new Float32Array(pt);
252
252
  return n.set(i.subarray(0, pt)), n;
253
253
  }
254
- class oe extends EventTarget {
254
+ class ae extends EventTarget {
255
255
  /** @param {string} wordName — the wake word label */
256
256
  constructor(n) {
257
257
  super(), this.wordName = n.trim().toLowerCase(), this.samples = [];
@@ -283,7 +283,7 @@ class oe extends EventTarget {
283
283
  await o.close().catch(() => {
284
284
  }), e(r);
285
285
  }
286
- }, s.start(), setTimeout(() => s.stop(), re);
286
+ }, s.start(), setTimeout(() => s.stop(), oe);
287
287
  });
288
288
  }
289
289
  // ─── Upload ────────────────────────────────────────────────────────────────
@@ -341,7 +341,7 @@ class oe extends EventTarget {
341
341
  return this.dispatchEvent(new CustomEvent("sample-added", { detail: { count: e, name: t } })), e;
342
342
  }
343
343
  }
344
- const ae = `/**
344
+ const ie = `/**
345
345
  * public/audio-processor.js
346
346
  * AudioWorklet that runs at 16 kHz and continuously emits the last
347
347
  * 1.5-second window (24 000 samples) via a circular buffer.
@@ -380,9 +380,9 @@ class AudioProcessor extends AudioWorkletProcessor {
380
380
  registerProcessor('audio-processor', AudioProcessor)
381
381
  `;
382
382
  let vt = null;
383
- function ie() {
383
+ function ce() {
384
384
  if (!vt) {
385
- const i = new Blob([ae], { type: "application/javascript" });
385
+ const i = new Blob([ie], { type: "application/javascript" });
386
386
  vt = URL.createObjectURL(i);
387
387
  }
388
388
  return vt;
@@ -396,22 +396,22 @@ function gt() {
396
396
  return [];
397
397
  }
398
398
  }
399
- function ce(i) {
399
+ function le(i) {
400
400
  const n = gt().filter((t) => t.word_name !== i.word_name);
401
401
  n.push(i), localStorage.setItem(wt, JSON.stringify(n));
402
402
  }
403
- function le(i) {
403
+ function he(i) {
404
404
  const n = gt().filter((t) => t.word_name !== i);
405
405
  localStorage.setItem(wt, JSON.stringify(n));
406
406
  }
407
- function he(i) {
407
+ function de(i) {
408
408
  const n = JSON.stringify(i, null, 2), t = new Blob([n], { type: "application/json" }), e = URL.createObjectURL(t), o = Object.assign(document.createElement("a"), {
409
409
  href: e,
410
410
  download: `${i.word_name}_ref.json`
411
411
  });
412
412
  document.body.appendChild(o), o.click(), document.body.removeChild(o), URL.revokeObjectURL(e);
413
413
  }
414
- async function de(i) {
414
+ async function ue(i) {
415
415
  const n = await i.text();
416
416
  let t;
417
417
  try {
@@ -425,7 +425,7 @@ async function de(i) {
425
425
  throw new Error('"embeddings" must be a 2D array');
426
426
  return t.word_name || (t.word_name = i.name.replace(/_ref\.json$/i, "").replace(/\.json$/i, "")), t;
427
427
  }
428
- class ue extends EventTarget {
428
+ class kt extends EventTarget {
429
429
  /**
430
430
  * @param {object} [opts]
431
431
  * @param {string[]} [opts.words] Words to detect (must have refs loaded via addCustomWord())
@@ -469,9 +469,9 @@ class ue extends EventTarget {
469
469
  n == null || n(1);
470
470
  return;
471
471
  }
472
- this._opts.assetsPath && Yt({ assetsPath: this._opts.assetsPath });
472
+ this._opts.assetsPath && Qt({ assetsPath: this._opts.assetsPath });
473
473
  try {
474
- await Qt(n);
474
+ await Vt(n);
475
475
  } catch (t) {
476
476
  throw this.dispatchEvent(new CustomEvent("error", { detail: { error: t } })), t;
477
477
  }
@@ -484,7 +484,7 @@ class ue extends EventTarget {
484
484
  e = await o.json();
485
485
  } else
486
486
  e = t;
487
- this.addCustomWord(e);
487
+ console.log({ refData: e }), kt.saveWord(e), this.addCustomWord(e);
488
488
  } catch (e) {
489
489
  const o = typeof t == "string" ? t : t.word_name;
490
490
  console.warn(`[Mellon] Failed to load ref "${o}": ${e.message}`);
@@ -507,7 +507,7 @@ class ue extends EventTarget {
507
507
  throw this.dispatchEvent(new CustomEvent("error", { detail: { error: a } })), a;
508
508
  }
509
509
  this._audioCtx = new AudioContext({ sampleRate: 16e3 });
510
- const e = ie();
510
+ const e = ce();
511
511
  await this._audioCtx.audioWorklet.addModule(e);
512
512
  const o = this._audioCtx.createMediaStreamSource(this._stream);
513
513
  this._workletNode = new AudioWorkletNode(this._audioCtx, "audio-processor"), o.connect(this._workletNode), this._workletNode.connect(this._audioCtx.destination);
@@ -578,7 +578,7 @@ class ue extends EventTarget {
578
578
  * stt.addCustomWord(ref)
579
579
  */
580
580
  enrollWord(n) {
581
- return new oe(n);
581
+ return new ae(n);
582
582
  }
583
583
  // ─── Persistence (static) ────────────────────────────────────────────────
584
584
  /** Return all custom word refs stored in localStorage. */
@@ -587,25 +587,25 @@ class ue extends EventTarget {
587
587
  }
588
588
  /** Persist a word ref to localStorage (replaces any existing entry with the same name). */
589
589
  static saveWord(n) {
590
- ce(n);
590
+ le(n);
591
591
  }
592
592
  /** Delete a word ref from localStorage by name. */
593
593
  static deleteWord(n) {
594
- le(n);
594
+ he(n);
595
595
  }
596
596
  /**
597
597
  * Parse an uploaded ref JSON file and return a RefData object.
598
598
  * @param {File} file
599
599
  */
600
600
  static importWordFile(n) {
601
- return de(n);
601
+ return ue(n);
602
602
  }
603
603
  /** Trigger a browser download of a ref as a JSON file. */
604
604
  static exportWord(n) {
605
- he(n);
605
+ de(n);
606
606
  }
607
607
  }
608
608
  export {
609
- oe as EnrollmentSession,
610
- ue as Mellon
609
+ ae as EnrollmentSession,
610
+ kt as Mellon
611
611
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mellon",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "Offline, in-browser hotword detection powered by EfficientWord-Net (ResNet-50 ArcFace). Works as a standalone app or npm library.",
5
5
  "type": "module",
6
6
  "main": "./dist/mellon.cjs",