mellon 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -1,12 +1,11 @@
1
1
  # mellon
2
2
 
3
- Offline, fully in-browser **hotword / wake-word detection** powered by [EfficientWord-Net](https://github.com/Ant-Brain/EfficientWord-Net) (ResNet-50 ArcFace). Works as a zero-dependency npm library or as a standalone PWA.
3
+ Offline, fully in-browser **hotword / wake-word detection** powered by [EfficientWord-Net](https://github.com/Ant-Brain/EfficientWord-Net) (ResNet-50 ArcFace).
4
4
 
5
5
  - **100% offline** — ONNX inference runs in the browser via WebAssembly; no server, no cloud.
6
6
  - **Speaker-independent** — the model generalises across voices out of the box.
7
- - **Custom words** — enroll any phrase with ≥ 3 audio samples; no retraining.
7
+ - **Custom words** — enroll any phrase with ≥ 3 audio samples.
8
8
  - **TypeScript-ready** — ships with full `.d.ts` declarations.
9
- - **Tiny API surface** — one class, zero config.
10
9
 
11
10
  ---
12
11
 
@@ -81,16 +80,14 @@ Refs are fetched automatically during `start()`. You can enroll your own words
81
80
  By default, the WASM runtime and model load from the jsDelivr CDN — no setup needed. For air-gapped or private-network deployments, copy the assets locally and tell the library where to find them:
82
81
 
83
82
  ```bash
84
- cp -r node_modules/mellon/dist/wasm public/mellon-assets/wasm
85
- cp node_modules/mellon/dist/models/model.onnx public/mellon-assets/model.onnx
83
+ cp -r node_modules/mellon/dist/assets public/mellon-assets/
86
84
  ```
87
85
 
88
86
  Then pass the paths to the constructor:
89
87
 
90
88
  ```js
91
89
  new Mellon({
92
- wasmBasePath: '/mellon-assets/wasm/', // trailing slash required
93
- modelUrl: '/mellon-assets/model.onnx',
90
+ assetsPath: '/mellon-assets', // trailing slash required
94
91
  })
95
92
  ```
96
93
 
@@ -104,8 +101,7 @@ export default {
104
101
  plugins: [
105
102
  viteStaticCopy({
106
103
  targets: [
107
- { src: 'node_modules/mellon/dist/wasm/*', dest: 'mellon-assets/wasm' },
108
- { src: 'node_modules/mellon/dist/models/model.onnx', dest: 'mellon-assets' },
104
+ { src: 'node_modules/mellon/dist/assets/*', dest: 'mellon-assets' }
109
105
  ],
110
106
  }),
111
107
  ],
package/dist/mellon.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Jt="0.0.4",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:f}=await r.read();if(m)break;c.push(f),l+=f.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 f=r+m,g=r,_=0;g<f;g+=2,_+=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[_],S=h*d[_+1],N=E*z-y*S,k=E*S+y*z,P=d[2*_],L=h*d[2*_+1],G=F*P-M*L,H=F*L+M*P,J=d[3*_],K=h*d[3*_+1],Y=C*J-T*K,Q=C*K+T*J,V=x+G,U=R+H,j=x-G,X=R-H,Z=N+Y,W=k+Q,$=h*(N-Y),O=h*(k-Q),et=V+Z,ot=U+W,at=V-Z,it=U-W,ct=j+O,lt=X-$,ht=j-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,f=r-l,g=c-h;s[t]=d,s[t+1]=m,s[t+2]=f,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],f=a[e+o+1],g=a[e+c],_=a[e+c+1],u=a[e+l],p=a[e+l+1],v=h+g,w=d+_,b=h-g,A=d-_,E=m+u,y=f+p,F=r*(m-u),M=r*(f-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,f=m>>>1,g=f>>>1;for(r=0;r<e;r+=a)for(var _=0,u=0;_<=g;_+=2,u+=s){var p=r+_,v=p+f,w=v+f,b=w+f,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,U=R+H,j=z+J,X=R-H,Z=z-J,W=k+Q,$=P+V,O=h*(k-Q),et=h*(P-V),ot=U+W,at=j+$,it=X+et,ct=Z-O;if(t[p]=ot,t[p+1]=at,t[v]=it,t[v+1]=ct,_===0){var lt=U-W,ht=j-$;t[w]=lt,t[w+1]=ht;continue}if(_!==g){var dt=X,It=-Z,Bt=U,Dt=-j,Ut=-h*et,jt=-h*O,Wt=-h*$,$t=-h*W,Pt=dt+Ut,Lt=It+jt,Gt=Bt+$t,Ht=Dt-Wt,yt=r+f-_,bt=r+m-_;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],f=a[e+l],g=h+m,_=h-m,u=d+f,p=r*(d-f),v=g+u,w=_,b=-p,A=g-u,E=_,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,D=512,B=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(B+2);for(let r=0;r<B+2;r++)t[r]=i+r*(n-i)/(B+1);const o=t.map(r=>te(r)).map(r=>Math.floor((D+1)*r/nt)),s=[],a=Math.floor(D/2)+1;for(let r=0;r<B;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(D),_t=new Float32Array(D),Ct=rt.createComplexArray(),ft=rt.createComplexArray(),Tt=new Float32Array(Math.floor(D/2)+1);function Nt(i){const n=1+Math.ceil((i.length-At)/Ft),t=new Float32Array(n*B),e=Math.floor(D/2)+1;for(let o=0;o<n;o++){const s=o*Ft;_t.fill(0);for(let a=0;a<At&&s+a<i.length;a++)_t[a]=i[s+a];rt.toComplexArray(_t,Ct),rt.transform(ft,Ct);for(let a=0;a<e;a++){const r=ft[2*a],c=ft[2*a+1],l=(r*r+c*c)/D;Tt[a]=l===0?1e-30:l}for(let a=0;a<B;a++){const r=se[a];let c=0;for(let l=0;l<e;l++)c+=Tt[l]*r[l];t[o*B+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 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=`/**
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,wasmBasePath:n.wasmBasePath,modelUrl:n.modelUrl},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.wasmBasePath||this._opts.modelUrl)&&Qt({wasmBasePath:this._opts.wasmBasePath,modelUrl:this._opts.modelUrl});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(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 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;
package/dist/mellon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- const Ht = "0.0.4", Jt = [1, 1, 149, 64], Kt = `https://cdn.jsdelivr.net/npm/mellon@${Ht}/dist/assets`, st = {
1
+ const Ht = "0.0.6", Jt = [1, 1, 149, 64], Kt = `https://cdn.jsdelivr.net/npm/mellon@${Ht}/dist/assets`, st = {
2
2
  assetsPath: `${Kt}`
3
3
  };
4
4
  let I = null, q = null, tt = null;
@@ -14,9 +14,9 @@ async function Qt(i) {
14
14
  const a = parseInt(s.headers.get("content-length") || "0", 10), r = s.body.getReader(), c = [];
15
15
  let l = 0;
16
16
  for (; ; ) {
17
- const { done: m, value: f } = await r.read();
17
+ const { done: m, value: _ } = await r.read();
18
18
  if (m) break;
19
- c.push(f), l += f.byteLength, a > 0 && (i == null || i(l / a));
19
+ c.push(_), l += _.byteLength, a > 0 && (i == null || i(l / a));
20
20
  }
21
21
  const h = new Uint8Array(l);
22
22
  let d = 0;
@@ -109,16 +109,16 @@ function Xt() {
109
109
  a = e / s << 1;
110
110
  var m = a >>> 2;
111
111
  for (r = 0; r < e; r += a)
112
- for (var f = r + m, g = r, _ = 0; g < f; g += 2, _ += s) {
113
- 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[_], S = h * d[_ + 1], N = E * z - y * S, k = E * S + y * z, L = d[2 * _], P = h * d[2 * _ + 1], G = F * L - C * P, H = F * P + C * L, J = d[3 * _], K = h * d[3 * _ + 1], Y = M * J - T * K, Q = M * K + T * J, V = x + G, U = R + H, W = x - G, X = R - H, Z = N + Y, j = k + Q, $ = h * (N - Y), O = h * (k - Q), et = V + Z, ot = U + j, at = V - Z, it = U - j, ct = W + O, lt = X - $, ht = W - O, dt = X + $;
112
+ for (var _ = r + m, g = r, f = 0; g < _; g += 2, f += s) {
113
+ 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, L = d[2 * f], P = h * d[2 * f + 1], G = F * L - C * P, H = F * P + C * L, 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 + $;
114
114
  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;
115
115
  }
116
116
  }
117
117
  }, i.prototype._singleTransform2 = function(t, e, o) {
118
- 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, f = r - l, g = c - h;
119
- s[t] = d, s[t + 1] = m, s[t + 2] = f, s[t + 3] = g;
118
+ 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;
119
+ s[t] = d, s[t + 1] = m, s[t + 2] = _, s[t + 3] = g;
120
120
  }, i.prototype._singleTransform4 = function(t, e, o) {
121
- 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], f = a[e + o + 1], g = a[e + c], _ = a[e + c + 1], u = a[e + l], p = a[e + l + 1], v = h + g, w = d + _, b = h - g, A = d - _, E = m + u, y = f + p, F = r * (m - u), C = r * (f - 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;
121
+ 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;
122
122
  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;
123
123
  }, i.prototype._realTransform4 = function() {
124
124
  var t = this._out, e = this._csize, o = this._width, s = 1 << o, a = e / s << 1, r, c, l = this._bitrev;
@@ -135,17 +135,17 @@ function Xt() {
135
135
  var h = this._inv ? -1 : 1, d = this.table;
136
136
  for (s >>= 2; s >= 2; s >>= 2) {
137
137
  a = e / s << 1;
138
- var m = a >>> 1, f = m >>> 1, g = f >>> 1;
138
+ var m = a >>> 1, _ = m >>> 1, g = _ >>> 1;
139
139
  for (r = 0; r < e; r += a)
140
- for (var _ = 0, u = 0; _ <= g; _ += 2, u += s) {
141
- var p = r + _, v = p + f, w = v + f, b = w + f, 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, L = y * N + F * S, P = d[2 * u], G = h * d[2 * u + 1], H = C * P - M * G, J = C * G + M * P, K = d[3 * u], Y = h * d[3 * u + 1], Q = T * K - x * Y, V = T * Y + x * K, U = R + H, W = z + J, X = R - H, Z = z - J, j = k + Q, $ = L + V, O = h * (k - Q), et = h * (L - V), ot = U + j, at = W + $, it = X + et, ct = Z - O;
142
- if (t[p] = ot, t[p + 1] = at, t[v] = it, t[v + 1] = ct, _ === 0) {
143
- var lt = U - j, ht = W - $;
140
+ for (var f = 0, u = 0; f <= g; f += 2, u += s) {
141
+ 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, L = y * N + F * S, P = d[2 * u], G = h * d[2 * u + 1], H = C * P - M * G, J = C * G + M * P, 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, $ = L + V, O = h * (k - Q), et = h * (L - V), ot = j + U, at = B + $, it = X + et, ct = Z - O;
142
+ if (t[p] = ot, t[p + 1] = at, t[v] = it, t[v + 1] = ct, f === 0) {
143
+ var lt = j - U, ht = B - $;
144
144
  t[w] = lt, t[w + 1] = ht;
145
145
  continue;
146
146
  }
147
- if (_ !== g) {
148
- var dt = X, kt = -Z, It = U, Bt = -W, Dt = -h * et, Ut = -h * O, Wt = -h * $, jt = -h * j, $t = dt + Dt, Lt = kt + Ut, Pt = It + jt, Gt = Bt - Wt, yt = r + f - _, bt = r + m - _;
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
149
  t[yt] = $t, t[yt + 1] = Lt, t[bt] = Pt, t[bt + 1] = Gt;
150
150
  }
151
151
  }
@@ -154,12 +154,12 @@ function Xt() {
154
154
  const s = this._out, a = this._data, r = a[e], c = a[e + o], l = r + c, h = r - c;
155
155
  s[t] = l, s[t + 1] = 0, s[t + 2] = h, s[t + 3] = 0;
156
156
  }, i.prototype._singleRealTransform4 = function(t, e, o) {
157
- 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], f = a[e + l], g = h + m, _ = h - m, u = d + f, p = r * (d - f), v = g + u, w = _, b = -p, A = g - u, E = _, y = p;
157
+ 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;
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
161
  var Zt = Xt();
162
- const Ot = /* @__PURE__ */ Vt(Zt), nt = 16e3, D = 512, B = 64, At = Math.floor(0.025 * nt), Ft = Math.floor(0.01 * nt);
162
+ const Ot = /* @__PURE__ */ Vt(Zt), 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
  }
@@ -167,11 +167,11 @@ function qt(i) {
167
167
  return 700 * (10 ** (i / 2595) - 1);
168
168
  }
169
169
  function te() {
170
- const i = Ct(0), n = Ct(nt / 2), t = new Float64Array(B + 2);
171
- for (let r = 0; r < B + 2; r++)
172
- t[r] = i + r * (n - i) / (B + 1);
173
- const o = t.map((r) => qt(r)).map((r) => Math.floor((D + 1) * r / nt)), s = [], a = Math.floor(D / 2) + 1;
174
- for (let r = 0; r < B; r++) {
170
+ const i = Ct(0), n = Ct(nt / 2), t = new Float64Array(D + 2);
171
+ for (let r = 0; r < D + 2; r++)
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;
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]);
177
177
  for (let l = o[r + 1]; l < o[r + 2]; l++) c[l] = (o[r + 2] - l) / (o[r + 2] - o[r + 1]);
@@ -179,24 +179,24 @@ function te() {
179
179
  }
180
180
  return s;
181
181
  }
182
- const ee = te(), rt = new Ot(D), _t = new Float32Array(D), Mt = rt.createComplexArray(), ft = rt.createComplexArray(), Tt = new Float32Array(Math.floor(D / 2) + 1);
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);
183
183
  function Nt(i) {
184
- const n = 1 + Math.ceil((i.length - At) / Ft), t = new Float32Array(n * B), e = Math.floor(D / 2) + 1;
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++) {
186
186
  const s = o * Ft;
187
- _t.fill(0);
187
+ ft.fill(0);
188
188
  for (let a = 0; a < At && s + a < i.length; a++)
189
- _t[a] = i[s + a];
190
- rt.toComplexArray(_t, Mt), rt.transform(ft, Mt);
189
+ ft[a] = i[s + a];
190
+ rt.toComplexArray(ft, Mt), rt.transform(_t, Mt);
191
191
  for (let a = 0; a < e; a++) {
192
- const r = ft[2 * a], c = ft[2 * a + 1], l = (r * r + c * c) / D;
192
+ const r = _t[2 * a], c = _t[2 * a + 1], l = (r * r + c * c) / W;
193
193
  Tt[a] = l === 0 ? 1e-30 : l;
194
194
  }
195
- for (let a = 0; a < B; a++) {
195
+ for (let a = 0; a < D; a++) {
196
196
  const r = ee[a];
197
197
  let c = 0;
198
198
  for (let l = 0; l < e; l++) c += Tt[l] * r[l];
199
- t[o * B + a] = Math.log(c === 0 ? 1e-30 : c);
199
+ t[o * D + a] = Math.log(c === 0 ? 1e-30 : c);
200
200
  }
201
201
  }
202
202
  return t;
@@ -436,8 +436,7 @@ class ue extends EventTarget {
436
436
  * @param {number} [opts.threshold=0.65] Detection threshold [0, 1]
437
437
  * @param {number} [opts.relaxationMs=2000] Min ms between successive match events
438
438
  * @param {number} [opts.inferenceGapMs=300] Min ms between inference runs
439
- * @param {string} [opts.wasmBasePath] Override ORT WASM base URL (offline use)
440
- * @param {string} [opts.modelUrl] Override model.onnx URL (offline use)
439
+ * @param {string} [opts.assetsPath]
441
440
  */
442
441
  constructor(n = {}) {
443
442
  super(), this._opts = {
@@ -446,8 +445,7 @@ class ue extends EventTarget {
446
445
  threshold: n.threshold ?? 0.65,
447
446
  relaxationMs: n.relaxationMs ?? 2e3,
448
447
  inferenceGapMs: n.inferenceGapMs ?? 300,
449
- wasmBasePath: n.wasmBasePath,
450
- modelUrl: n.modelUrl
448
+ assetsPath: n.assetsPath
451
449
  }, this._refs = /* @__PURE__ */ new Map(), this._detectors = /* @__PURE__ */ new Map(), this._audioCtx = null, this._workletNode = null, this._stream = null, this._initialized = !1, this._running = !1;
452
450
  }
453
451
  /** Whether init() has completed successfully. */
@@ -471,10 +469,7 @@ class ue extends EventTarget {
471
469
  n == null || n(1);
472
470
  return;
473
471
  }
474
- (this._opts.wasmBasePath || this._opts.modelUrl) && Yt({
475
- wasmBasePath: this._opts.wasmBasePath,
476
- modelUrl: this._opts.modelUrl
477
- });
472
+ this._opts.assetsPath && Yt({ assetsPath: this._opts.assetsPath });
478
473
  try {
479
474
  await Qt(n);
480
475
  } catch (t) {
@@ -483,7 +478,7 @@ class ue extends EventTarget {
483
478
  for (const t of this._opts.refs)
484
479
  try {
485
480
  let e;
486
- if (typeof t == "string") {
481
+ if (console.log("fetching ref : ", t), typeof t == "string") {
487
482
  const o = await fetch(t);
488
483
  if (!o.ok) throw new Error(`HTTP ${o.status}`);
489
484
  e = await o.json();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mellon",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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",