nixamp 0.26.4 → 0.26.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
@@ -286,6 +286,14 @@ to Saturday.
286
286
 
287
287
  ## The directory
288
288
 
289
+ Servers start with an **IPTV-org** catalog using its public
290
+ [main playlist](https://github.com/iptv-org/iptv#playlists). It is added once
291
+ alongside existing catalogs and fetched in the background. Removing it stays
292
+ removed on restart; add `https://iptv-org.github.io/iptv/index.m3u` to restore it.
293
+ Catalog browsing and file navigation apply immediately after a click. Group
294
+ lists are cached until the provider refreshes, and playback updates reuse the
295
+ existing file list without moving focus, scrolling, or the browsing page.
296
+
289
297
  [nixamp.com/directory](https://nixamp.com/directory) lists nixamps that agreed
290
298
  to be listed. In the PWA, **Browse the directory** next to the address field
291
299
  picks one without typing anything.
@@ -572,16 +580,31 @@ interpreter, not a promise of exact lip sync.
572
580
 
573
581
  Translated playback keeps an approximate version of the original background
574
582
  sound. FastEnhancer Web's Tiny model estimates speech locally in a dedicated
575
- browser worker; the player subtracts that estimate from the aligned source in
576
- each stereo channel and mixes the remainder with translated voices. Background
583
+ browser worker. Mono and stereo sources have their speech estimate subtracted
584
+ from the aligned source. Quad, 5.1, and 7.1 files retain their original surround
585
+ channels while dialogue is removed from the front and centre channels; those
586
+ parts are then mixed to stereo beside the translated voices. Background
577
587
  processing adds no API calls or provider charges. It stops with translation;
578
- recognition starts without waiting for it. If the device cannot keep up or load the model, that
579
- background branch is silenced while translated speech continues. Separation can
580
- leave some original speech or remove parts of music and crowd noise; disable
588
+ recognition starts without waiting for it. Brief processing stalls drop stale
589
+ frames and recover automatically, with bounded work and at most about 130 ms of
590
+ background delay. A model or device failure silences that branch while translated
591
+ speech continues. Separation of mixed dialogue and background is approximate;
592
+ speech placed in a surround channel remains in that original track. Disable
581
593
  **Keep background sound** under **Audio options** when needed. This uses
582
594
  [FastEnhancer Web](https://github.com/ryyr-ry/fastenhancer-web), under the MIT
583
595
  license.
584
596
 
597
+ **Background level**, in the same collapsed options, balances the separated
598
+ sound from 0–200% (100% by default). It never mixes the original dialogue back
599
+ in as a fallback. Increasing it also amplifies any speech the model fails to
600
+ remove. This is an approximate local separator, not lossless dialogue removal.
601
+
602
+ Translated audio stays selected for the entire session, including the initial
603
+ wait, gaps between lines, buffering, seeking, and changes of speaker or target
604
+ language. With background sound off, gaps are silent; with it on, the separated
605
+ background continues. The original audio returns when translation is disabled
606
+ (including an announced provider failure), not whenever an utterance finishes.
607
+
585
608
  The account server needs `ELEVENLABS_API_KEY`; `NIXAMP_DUBBING=off` disables
586
609
  this feature. The key stays on the server. Sign-in is required for speaker
587
610
  analysis, voice selection, and short-lived playback grants. Grants expire after
@@ -73,6 +73,7 @@ export interface Group {
73
73
  live: number;
74
74
  vod: number;
75
75
  }
76
+ export declare const DEFAULT_CATALOG_SOURCE = "https://iptv-org.github.io/iptv/index.m3u";
76
77
  /** Read a list, from disk or over the network, within reason. */
77
78
  export declare function readCatalog(source: string, send?: typeof fetch, maxBytes?: number): Promise<string>;
78
79
  /**
@@ -89,6 +90,9 @@ export declare class Catalogs {
89
90
  private readonly stored;
90
91
  private readonly entries;
91
92
  constructor(dir: string, port: number, send?: typeof fetch);
93
+ /** Seed once per server, without holding startup on a network fetch. A later
94
+ * deliberate removal stays removed; adding the same source restores it. */
95
+ ensureDefaults(): void;
92
96
  /** What was here last time, without asking any provider yet. */
93
97
  load(): void;
94
98
  /** Read every catalog that has no entries on disk. For boot. */
package/dist/catalogs.js CHANGED
Binary file
package/dist/server.js CHANGED
@@ -5369,6 +5369,7 @@ export async function serve(argv, version = "0.1.0") {
5369
5369
  // read are fetched in the background so browsing does not wait on a provider.
5370
5370
  const catalogs = new Catalogs(stateDir(), options.port);
5371
5371
  catalogs.load();
5372
+ catalogs.ensureDefaults();
5372
5373
  void catalogs.warm().catch(() => undefined);
5373
5374
  const destinations = parseDestinations(options.rtmp);
5374
5375
  const broadcaster = new Broadcaster(tools.ffmpeg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nixamp",
3
- "version": "0.26.4",
3
+ "version": "0.26.6",
4
4
  "description": "It really whips the terminal's ass. A Winamp-shaped audio player for your terminal.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/catalogs.ts CHANGED
Binary file
package/src/server.ts CHANGED
@@ -5946,6 +5946,7 @@ export async function serve(argv: string[], version = "0.1.0"): Promise<void> {
5946
5946
  // read are fetched in the background so browsing does not wait on a provider.
5947
5947
  const catalogs = new Catalogs(stateDir(), options.port);
5948
5948
  catalogs.load();
5949
+ catalogs.ensureDefaults();
5949
5950
  void catalogs.warm().catch(() => undefined);
5950
5951
 
5951
5952
  const destinations = parseDestinations(options.rtmp);
@@ -544,4 +544,4 @@ class FastEnhancerProcessor extends AudioWorkletProcessor {
544
544
  }
545
545
 
546
546
  registerProcessor('fastenhancer-processor', FastEnhancerProcessor);
547
- `}));function lt(e,t){if(!X.includes(e))throw new c(`Invalid model size for embedded loader: "${e}". Valid values: ${X.join(`, `)}`);if(!yt.includes(t))throw new c(`Invalid variant for embedded loader: "${t}". Valid values: ${yt.join(`, `)}`)}function ut(e){if(!X.includes(e))throw new c(`Invalid model size for embedded loader: "${e}". Valid values: ${X.join(`, `)}`)}async function dt(e,t){lt(e,t);let n=`${e}-${t}`;switch(n){case`tiny-scalar`:return(await Promise.resolve().then(()=>(re(),k))).getWasmBytes();case`tiny-simd`:return(await Promise.resolve().then(()=>(ce(),ie))).getWasmBytes();case`base-scalar`:return(await Promise.resolve().then(()=>(pe(),le))).getWasmBytes();case`base-simd`:return(await Promise.resolve().then(()=>(ve(),me))).getWasmBytes();case`small-scalar`:return(await Promise.resolve().then(()=>(Ce(),ye))).getWasmBytes();case`small-simd`:return(await Promise.resolve().then(()=>(Oe(),we))).getWasmBytes();default:throw new c(`Unknown WASM variant: ${n}`)}}async function ft(e){switch(ut(e),e){case`tiny`:return(await Promise.resolve().then(()=>(Me(),ke))).getWeightData();case`base`:return(await Promise.resolve().then(()=>(Ie(),Ne))).getWeightData();case`small`:return(await Promise.resolve().then(()=>(Ve(),Le))).getWeightData();default:throw new c(`Unknown model: ${e}`)}}async function pt(e,t){lt(e,t);let n=`${e}-${t}`;switch(n){case`tiny-scalar`:return(await Promise.resolve().then(()=>(We(),He))).exportMap;case`tiny-simd`:return(await Promise.resolve().then(()=>(qe(),Ge))).exportMap;case`base-scalar`:return(await Promise.resolve().then(()=>(Xe(),Je))).exportMap;case`base-simd`:return(await Promise.resolve().then(()=>($e(),Ze))).exportMap;case`small-scalar`:return(await Promise.resolve().then(()=>(nt(),et))).exportMap;case`small-simd`:return(await Promise.resolve().then(()=>(at(),rt))).exportMap;default:throw new c(`Unknown export map variant: ${n}`)}}function mt(){if(Z)return Z;if(!Q)throw Error(`getProcessorBlobUrl() requires prior initialization. Call ensureProcessorSourceLoaded() first.`);return ht(Q)}function ht(e){if(typeof Blob<`u`&&typeof URL<`u`&&typeof URL.createObjectURL==`function`){let t=new Blob([e],{type:`application/javascript`});return Z=URL.createObjectURL(t),Z}return Z=`data:application/javascript;base64,${btoa(unescape(encodeURIComponent(e)))}`,Z}async function gt(){if(Q)return;let{processorSource:e}=await Promise.resolve().then(()=>(ct(),ot));Q=e}async function _t(){return Z||(await gt(),mt())}function vt(){Z&&=(Z.startsWith(`blob:`)&&typeof URL<`u`&&typeof URL.revokeObjectURL==`function`&&URL.revokeObjectURL(Z),null)}var X,yt,Z,Q,bt=t((()=>{u(),X=[`tiny`,`base`,`small`],yt=[`scalar`,`simd`],Z=null,Q=null}));async function xt(e){let{inputStream:t,wasmBytes:n,weightBytes:r,exportMap:i,modelSize:a,workletUrl:u,audioContext:d,onWarning:f}=e;if(![0,1,2].includes(a))throw new c(`Invalid modelSize: ${a}. Must be 0 (tiny), 1 (base), or 2 (small).`);let p=e=>{try{f?.(e)}catch{}},m=d===void 0,h=d;if(m)try{h=new AudioContext({sampleRate:St})}catch(e){throw new o(`Failed to create AudioContext: ${e instanceof Error?e.message:String(e)}`)}let g=async(e,t)=>{if(m&&e.state!==`closed`)try{await e.close()}catch(e){p(`${t}: ${e instanceof Error?e.message:String(e)}`)}};if(h.state===`suspended`)try{await h.resume()}catch(e){throw await g(h,`AudioContext.close() failed after resume error`),new o(`AudioContext.resume() failed (possibly due to autoplay restrictions): ${e instanceof Error?e.message:String(e)}`)}h.sampleRate!==St&&p(`AudioContext sample rate is ${h.sampleRate}Hz (expected: ${St}Hz). Audio quality may be reduced.`);let _;try{_=u??await _t()}catch(e){throw await g(h,`AudioContext.close() failed`),e}try{await h.audioWorklet.addModule(_)}catch(e){await g(h,`AudioContext.close() failed`);let t=e instanceof Error?e.message:String(e),n=_.startsWith(`blob:`)||_.startsWith(`data:`)?` If your Content Security Policy blocks blob: or data: URLs, provide a static processor.js URL via the workletUrl option, or add "blob:" to your worker-src CSP directive.`:``;throw new s(`Failed to register AudioWorklet: ${t}${n}`)}let v,y,b;try{v=new AudioWorkletNode(h,`fastenhancer-processor`,{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[1]}),y=h.createMediaStreamSource(t),b=h.createMediaStreamDestination(),y.connect(v),v.connect(b)}catch(e){if(y)try{y.disconnect()}catch{}if(v)try{v.disconnect()}catch{}throw b&&b.stream.getTracks().forEach(e=>{try{e.stop()}catch{}}),g(h,`AudioContext.close() failed`),new s(`Failed to construct AudioWorklet node: ${e instanceof Error?e.message:String(e)}`)}let x=new Promise((e,t)=>{let n=setTimeout(()=>{v.port.removeEventListener(`message`,r),t(new s(`Worklet initialization timed out (${Ct/1e3}s)`))},Ct),r=i=>{i.data?.type===`ready`?(clearTimeout(n),v.port.removeEventListener(`message`,r),e()):i.data?.type===`error`&&(clearTimeout(n),v.port.removeEventListener(`message`,r),t(new s(`Worklet initialization failed: ${i.data.message??`Unknown error`}`)))};v.port.addEventListener(`message`,r),v.port.start()}),S=n.slice(0),C=r.slice(0);v.port.postMessage({type:`init`,wasmBytes:S,weightBytes:C,exportMap:i,modelSize:a},[S,C]);try{await x}catch(e){try{y.disconnect()}catch(e){p(`source.disconnect() failed: ${e instanceof Error?e.message:String(e)}`)}try{v.disconnect()}catch(e){p(`workletNode.disconnect() failed: ${e instanceof Error?e.message:String(e)}`)}throw b.stream.getTracks().forEach(e=>{try{e.stop()}catch(e){p(`MediaStreamTrack.stop() failed during init cleanup: ${e instanceof Error?e.message:String(e)}`)}}),await g(h,`AudioContext.close() failed`),e}let w=`running`,T=!1,E=!1,D=!1,O=!1,k=null,ee=t=>{if(t.data?.type===`process_error`&&w!==`destroyed`){p(`Worklet processing error: ${t.data.message??`Unknown`}`);return}if(t.data?.type===`auto_bypass`&&w!==`destroyed`){E=!!t.data.enabled;try{e.onAutoBypass?.(E)}catch{}}},te=()=>{let t=h.state;if(p(`AudioContext state changed to "${t}".`),t===`suspended`&&w!==`destroyed`&&m){h.resume().catch(e=>{p(`AudioContext.resume() failed after suspension: ${e instanceof Error?e.message:String(e)}`)});return}if(t===`closed`){w=`destroyed`;try{e.onDestroy?.()}catch{}}};return v.port.addEventListener(`message`,ee),h.addEventListener(`statechange`,te),{get outputStream(){return b.stream},get state(){return w},get bypass(){return T},set bypass(e){if(w===`destroyed`)throw new l(`This StreamDenoiser has already been destroyed`);T=e,v.port.postMessage({type:`set_bypass`,enabled:e})},get autoPassthrough(){return E},get agcEnabled(){return D},set agcEnabled(e){if(w===`destroyed`)throw new l(`This StreamDenoiser has already been destroyed`);D=e,v.port.postMessage({type:`set_agc`,enabled:e})},get hpfEnabled(){return O},set hpfEnabled(e){if(w===`destroyed`)throw new l(`This StreamDenoiser has already been destroyed`);O=e,v.port.postMessage({type:`set_hpf`,enabled:e})},destroy(){this.destroyAsync().catch(e=>{p(`destroyAsync() failed: ${e instanceof Error?e.message:String(e)}`)})},async destroyAsync(){return k||(k=(async()=>{w=`destroyed`,h.removeEventListener(`statechange`,te),v.port.removeEventListener(`message`,ee);try{y.disconnect()}catch(e){p(`source.disconnect() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}try{v.disconnect()}catch(e){p(`workletNode.disconnect() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}let e=new Promise(e=>{let t=!1,n=()=>{t||(t=!0,clearTimeout(i),v.port.removeEventListener(`message`,r))},r=t=>{(t.data?.type===`destroyed`||t.data?.type===`error`)&&(n(),e())},i=setTimeout(()=>{n(),e()},2e3);v.port.addEventListener(`message`,r)});try{v.port.postMessage({type:`destroy`})}catch(e){p(`workletNode.port.postMessage() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}b.stream.getTracks().forEach(e=>{try{e.stop()}catch(e){p(`MediaStreamTrack.stop() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}}),await e;try{v.port.close()}catch(e){p(`workletNode.port.close() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}await g(h,`AudioContext.close() failed during destroy`)})(),k)},getWorkletState(){if(w===`destroyed`)return Promise.reject(new l(`This StreamDenoiser has already been destroyed`));let e=`state_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return new Promise((t,n)=>{let r=setTimeout(()=>{v.port.removeEventListener(`message`,i),n(new s(`Worklet state retrieval timed out (${wt/1e3}s)`))},wt),i=n=>{n.data?.type===`state`&&n.data?.requestId===e&&(clearTimeout(r),v.port.removeEventListener(`message`,i),t(n.data))};v.port.addEventListener(`message`,i),v.port.start(),v.port.postMessage({type:`get_state`,requestId:e})})}}}var St,Ct,wt,Tt=t((()=>{u(),bt(),St=48e3,Ct=1e4,wt=500}));function Et(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new i(`Export map JSON must be a plain object`);let t=e;for(let[e,n]of Object.entries(t))if(typeof n!=`string`)throw new i(`Export map entry "${e}" must be a string, got ${typeof n}`);return t}function Dt(e){return e.endsWith(`/`)?e:`${e}/`}function Ot(e,t,n){let r=n?Dt(n):void 0;return`${e}:${t??`auto`}:${r??`default`}`}async function kt(){try{return f()}catch(e){return typeof console<`u`&&console.warn(`[fastenhancer] SIMD detection failed:`,e),!1}}function At(){$.clear(),vt()}function jt(e,t){return $.delete(Ot(e,t?.simd,t?.baseUrl))}function Mt(e,t){if(!Lt.includes(e))throw new c(`Invalid model size: "${e}". Valid values: ${Lt.join(`, `)}`);let n=t?.signal;if(n?.aborted)return Promise.reject(n.reason??Error(`Aborted`));let r=t?.simd,i=t?.baseUrl,a=Ot(e,r,i),o=$.get(a);if(o)return o;let s=Nt(e,r,i,n);return $.set(a,s),s.catch(()=>$.delete(a)),s}async function Nt(e,t,n,r){let i=h(t===void 0?await kt():t);return n?Ft(e,i,n,r):Pt(e,i,r)}async function Pt(e,t,n){let r,a,o;try{[r,a,o]=await Promise.all([dt(e,t),ft(e),pt(e,t)])}catch(e){throw e instanceof c?e:new i(`Failed to load embedded model resources: ${e instanceof Error?e.message:String(e)}`)}if(n?.aborted)throw n.reason??Error(`Aborted`);return It(e,t,r,a,o)}async function Ft(e,t,n,r){let a=Dt(n),o=`fastenhancer-${e}-${t}`,s=`${a}${o}.wasm`,c=`${a}${o}-exports.json`,l=`${a}${zt[e]}`,u,d,f;try{let e=r?{signal:r}:void 0,[t,n,a]=await Promise.all([fetch(s,e),fetch(l,e),fetch(c,e)]);if(!t.ok)throw new i(`Failed to fetch WASM binary: ${t.status} ${t.statusText} (${s})`);if(!n.ok)throw new i(`Failed to fetch weight file: ${n.status} ${n.statusText} (${l})`);if(!a.ok)throw new i(`Failed to fetch export map: ${a.status} ${a.statusText} (${c})`);[u,d,f]=await Promise.all([t.arrayBuffer(),n.arrayBuffer(),a.json().then(Et)])}catch(e){throw e instanceof i?e:new i(`Failed to load model resources: ${e instanceof Error?e.message:String(e)}`)}return It(e,t,u,d,f)}function It(e,t,n,r,i){let a=Rt[e],o=T(e),s=null,c=async()=>(s||(s=v(n,i),s.catch(()=>{s=null})),s),l=Object.freeze({...i});return{size:e,variant:t,sampleRate:o.sampleRate,hopSize:o.hopSize,nFft:o.nFft,modelSizeId:a,get wasmBytes(){return n.slice(0)},get weightData(){return r.slice(0)},exportMap:l,wasmFactory:c,async createDenoiser(){let{createDenoiser:t}=await Promise.resolve().then(()=>(tn(),Yt));return t({model:{size:e,sampleRate:o.sampleRate,nFft:o.nFft,hopSize:o.hopSize,wasmFactory:c},weightData:r,modelSizeId:a})},async createStreamDenoiser(e,t){return xt({inputStream:e,wasmBytes:n,weightBytes:r,exportMap:i,modelSize:a,workletUrl:t?.workletUrl,onWarning:t?.onWarning,audioContext:t?.audioContext,onAutoBypass:t?.onAutoBypass,onDestroy:t?.onDestroy})}}}var Lt,Rt,zt,$,Bt=t((()=>{u(),g(),m(),w(),O(),Tt(),bt(),Lt=[`tiny`,`base`,`small`],Rt={tiny:0,base:1,small:2},zt={tiny:`fe_tiny_48k.bin`,base:`fe_base_48k.bin`,small:`fe_small_48k.bin`},$=new Map}));function Vt(){return Ut.map(e=>({...e}))}function Ht(e){let t=e?.priority??`quality`,n=Wt[t];if(!n){let e=Object.keys(Wt).join(`, `);throw new c(`Invalid priority: '${String(t)}'. Valid values: ${e}`)}return{...Ut[n.index],reason:n.reason}}var Ut,Wt,Gt=t((()=>{u(),O(),Ut=[{id:`tiny`,label:`Tiny`,params:28e3,wasmSizeKB:52,hopSize:D.tiny.hopSize,description:`Lightest model. Suitable for low-latency environments and mobile devices.`},{id:`base`,label:`Base`,params:101e3,wasmSizeKB:65,hopSize:D.base.hopSize,description:`Balanced model. Combines quality and speed.`},{id:`small`,label:`Small`,params:207e3,wasmSizeKB:75,hopSize:D.small.hopSize,description:`Recommended model. Provides the highest-quality noise removal.`}],Wt={speed:{index:0,reason:`Prioritizes processing speed. Runs reliably on mobile and lower-spec environments.`},balanced:{index:1,reason:`Well-balanced between quality and speed, and also suitable for mobile environments.`},quality:{index:2,reason:`Highest-quality noise removal. Recommended for most environments.`}}}));async function Kt(){try{return f()}catch{return!1}}async function qt(){let e=[],t=typeof WebAssembly<`u`;t||e.push(`WebAssembly is not available. A modern browser is required.`);let n=await Kt();t&&!n&&e.push(`WASM SIMD is not available. Chrome 91+, Firefox 89+, or Safari 16.4+ is required. The scalar fallback will still work, but performance will be reduced.`);let r=typeof AudioContext<`u`;r||e.push(`AudioContext is not available. A browser with Web Audio API support is required.`);let i=typeof AudioWorkletNode<`u`&&typeof AudioContext<`u`&&`audioWorklet`in AudioContext.prototype;i||e.push(`AudioWorklet is not available. Chrome 66+, Firefox 76+, or Safari 14.1+ is required.`);let a=t&&r&&i;return{wasm:t,simd:n,audioContext:r,audioWorklet:i,overall:a,recommended:a&&n,issues:e}}var Jt=t((()=>{m()})),Yt=n({AudioContextError:()=>o,DestroyedError:()=>l,FastEnhancerError:()=>r,ModelInitError:()=>a,ValidationError:()=>c,WasmLoadError:()=>i,WorkletError:()=>s,clearCachedModel:()=>jt,clearModelCache:()=>At,createDenoiser:()=>Qt,createStreamDenoiser:()=>xt,diagnose:()=>qt,getModels:()=>Vt,isSupported:()=>$t,loadModel:()=>Mt,recommendModel:()=>Ht});function Xt(e,t){if(t!==void 0)return t;let n=en[e.size];if(n===void 0)throw new c(`Unknown model size: "${e.size}". Valid values: tiny, base, small`);return n}function Zt(e,t,n){let r=`ready`,i=!1,o=!1,s=!1,u=0,d=null,f=e,p=t,m=n;function h(e,t){if(!e||e%4!=0)throw new a(`Invalid WASM ${t} pointer: ${e} (must be non-zero and 4-byte aligned)`);return e/4}let g=h(f._fe_get_input_ptr(p),`input`),_=h(f._fe_get_output_ptr(p),`output`),v=new Map,y=new Map,b=2e3,x=new Float64Array(b),S=0,C=0,w=0,T=0;function E(e,...t){let n=v.get(e);if(n)for(let e of n)try{e(...t)}catch(e){typeof console<`u`&&console.error(`[fastenhancer] Event handler error:`,e)}let r=y.get(e);if(r){for(let e of r)try{e(...t)}catch(e){typeof console<`u`&&console.error(`[fastenhancer] Event handler error:`,e)}r.clear()}}function D(e){if(r===`destroyed`)throw new l(`Cannot ${e}: denoiser is destroyed`)}function O(){return r===`destroyed`}let k={get state(){return r},processFrame(e){if(D(`processFrame`),e.length!==m)throw new c(`Expected ${m} samples, got ${e.length}`);for(let t=0;t<e.length;t++)if(!Number.isFinite(e[t]))throw new c(`Input contains non-finite value at index ${t}: ${e[t]}`);if(i)return new Float32Array(e);let t=performance.now();try{f.HEAPF32.set(e,g),f._fe_process_inplace(p);let n=new Float32Array(m);n.set(f.HEAPF32.subarray(_,_+m));let r=performance.now()-t;return x[S%b]=r,S++,C=Math.min(C+1,b),w++,n}catch(e){throw T++,E(`error`,{code:`PROCESS_ERROR`,message:e instanceof Error?e.message:String(e),original:e}),e}},get bypass(){return i},set bypass(e){D(`set bypass`),i=e},get agcEnabled(){return o},set agcEnabled(e){D(`set agcEnabled`),o=e,f._fe_set_agc(p,+!!e)},get hpfEnabled(){return s},set hpfEnabled(e){D(`set hpfEnabled`),s=e,f._fe_set_hpf(p,+!!e)},destroy(){r!==`destroyed`&&(f._fe_destroy(p),r=`destroyed`,E(`statechange`,`destroyed`),E(`destroy`),v.clear(),y.clear())},on(e,t){v.has(e)||v.set(e,new Set),v.get(e).add(t)},off(e,t){v.get(e)?.delete(t),y.get(e)?.delete(t)},once(e,t){y.has(e)||y.set(e,new Set),y.get(e).add(t)},get performance(){if(w===0)return{avgMs:0,p99Ms:0,droppedFrames:T,totalFrames:0};let e=[];for(let t=0;t<C;t++)e.push(x[t]);let t=e.reduce((e,t)=>e+t,0)/C;return e.sort((e,t)=>e-t),{avgMs:t,p99Ms:e[Math.min(Math.ceil(C*.99)-1,C-1)],droppedFrames:T,totalFrames:w}},get isSwitching(){return u>0},async switchModel(e){D(`switchModel`),u++;let t=async()=>{try{let{model:t,weightData:n,modelSizeId:r}=e,i=Xt(t,r),c=t._wasm??await t.wasmFactory();if(O())return;let l=new Uint8Array(n),u=l.byteLength,d=c._malloc(u);if(d===0)throw new a(`Failed to allocate memory in the WASM heap`);let v;try{new Uint8Array(c.HEAPF32.buffer).set(l,d),v=c._fe_init(i,d,u)}finally{c._free(d)}if(O()){v!==0&&c._fe_destroy(v);return}if(v===0)throw new a(`Failed to initialize the new model`);let y=f,b=p;f=c,p=v,m=c._fe_get_hop_size(v),g=h(c._fe_get_input_ptr(v),`input`),_=h(c._fe_get_output_ptr(v),`output`),o&&c._fe_set_agc(v,1),s&&c._fe_set_hpf(v,1),y._fe_destroy(b),S=0,C=0,w=0,T=0}finally{u--}};d=(d??Promise.resolve()).then(t,t),await d},[Symbol.dispose](){k.destroy()},async[Symbol.asyncDispose](){k.destroy()}};return k}async function Qt(e){let{model:t,weightData:n,modelSizeId:r}=e,i=Xt(t,r),o=t._wasm??await t.wasmFactory(),s=new Uint8Array(n),c=s.byteLength,l=o._malloc(c);if(l===0)throw new a(`Failed to allocate memory in the WASM heap`);let u;try{o.HEAPF32.buffer,new Uint8Array(o.HEAPF32.buffer).set(s,l),u=o._fe_init(i,l,c)}finally{o._free(l)}if(u===0)throw new a(`Failed to initialize the denoiser engine`);let d=o._fe_get_hop_size(u);return Zt(o,u,d)}async function $t(){let e=typeof WebAssembly<`u`&&typeof WebAssembly.instantiate==`function`,t=!1;if(e)try{t=f()}catch{t=!1}let n=typeof globalThis<`u`&&typeof globalThis.AudioWorkletNode==`function`;return{wasm:e,simd:t,audioWorklet:n}}var en,tn=t((()=>{u(),m(),Bt(),Tt(),Gt(),Jt(),en={tiny:0,base:1,small:2}}));tn();var nn=class{speech;previous=[new Float32Array(512),new Float32Array(512)];primed=!1;constructor(e){this.speech=e}process(e){if(e.length!==2||e.some(e=>e.length!==512||e.some(e=>!Number.isFinite(e))))throw Error(`Invalid background audio frame`);let t=e.map((e,t)=>{let n=this.speech[t].processFrame(e);if(n.length!==512||n.some(e=>!Number.isFinite(e)))throw Error(`Speech separation failed`);let r=new Float32Array(512);if(this.primed)for(let e=0;e<512;e++)r[e]=Math.max(-1,Math.min(1,this.previous[t][e]-n[e]));return this.previous[t].set(e),r});return this.primed=!0,t}destroy(){for(let e of this.speech)e.destroy()}};self.onmessage=async e=>{let t=e.data.port;try{let e=await Mt(`tiny`);if(e.sampleRate!==48e3||e.hopSize!==512)throw Error(`Unsupported separation format`);let n=await e.createDenoiser(),r=await e.createDenoiser();n.agcEnabled=r.agcEnabled=!1,n.hpfEnabled=r.hpfEnabled=!1;let i=new nn([n,r]),a=0,o=!1;t.onmessage=e=>{if(!o)try{if(e.data.id!==a++)throw Error(`Background audio fell behind`);let n=i.process(e.data.channels);t.postMessage({channels:n},n.map(e=>e.buffer))}catch{o=!0,i.destroy(),self.postMessage({error:!0})}},t.start(),self.postMessage({ready:!0})}catch{self.postMessage({error:!0})}}})();
547
+ `}));function lt(e,t){if(!X.includes(e))throw new c(`Invalid model size for embedded loader: "${e}". Valid values: ${X.join(`, `)}`);if(!yt.includes(t))throw new c(`Invalid variant for embedded loader: "${t}". Valid values: ${yt.join(`, `)}`)}function ut(e){if(!X.includes(e))throw new c(`Invalid model size for embedded loader: "${e}". Valid values: ${X.join(`, `)}`)}async function dt(e,t){lt(e,t);let n=`${e}-${t}`;switch(n){case`tiny-scalar`:return(await Promise.resolve().then(()=>(re(),k))).getWasmBytes();case`tiny-simd`:return(await Promise.resolve().then(()=>(ce(),ie))).getWasmBytes();case`base-scalar`:return(await Promise.resolve().then(()=>(pe(),le))).getWasmBytes();case`base-simd`:return(await Promise.resolve().then(()=>(ve(),me))).getWasmBytes();case`small-scalar`:return(await Promise.resolve().then(()=>(Ce(),ye))).getWasmBytes();case`small-simd`:return(await Promise.resolve().then(()=>(Oe(),we))).getWasmBytes();default:throw new c(`Unknown WASM variant: ${n}`)}}async function ft(e){switch(ut(e),e){case`tiny`:return(await Promise.resolve().then(()=>(Me(),ke))).getWeightData();case`base`:return(await Promise.resolve().then(()=>(Ie(),Ne))).getWeightData();case`small`:return(await Promise.resolve().then(()=>(Ve(),Le))).getWeightData();default:throw new c(`Unknown model: ${e}`)}}async function pt(e,t){lt(e,t);let n=`${e}-${t}`;switch(n){case`tiny-scalar`:return(await Promise.resolve().then(()=>(We(),He))).exportMap;case`tiny-simd`:return(await Promise.resolve().then(()=>(qe(),Ge))).exportMap;case`base-scalar`:return(await Promise.resolve().then(()=>(Xe(),Je))).exportMap;case`base-simd`:return(await Promise.resolve().then(()=>($e(),Ze))).exportMap;case`small-scalar`:return(await Promise.resolve().then(()=>(nt(),et))).exportMap;case`small-simd`:return(await Promise.resolve().then(()=>(at(),rt))).exportMap;default:throw new c(`Unknown export map variant: ${n}`)}}function mt(){if(Z)return Z;if(!Q)throw Error(`getProcessorBlobUrl() requires prior initialization. Call ensureProcessorSourceLoaded() first.`);return ht(Q)}function ht(e){if(typeof Blob<`u`&&typeof URL<`u`&&typeof URL.createObjectURL==`function`){let t=new Blob([e],{type:`application/javascript`});return Z=URL.createObjectURL(t),Z}return Z=`data:application/javascript;base64,${btoa(unescape(encodeURIComponent(e)))}`,Z}async function gt(){if(Q)return;let{processorSource:e}=await Promise.resolve().then(()=>(ct(),ot));Q=e}async function _t(){return Z||(await gt(),mt())}function vt(){Z&&=(Z.startsWith(`blob:`)&&typeof URL<`u`&&typeof URL.revokeObjectURL==`function`&&URL.revokeObjectURL(Z),null)}var X,yt,Z,Q,bt=t((()=>{u(),X=[`tiny`,`base`,`small`],yt=[`scalar`,`simd`],Z=null,Q=null}));async function xt(e){let{inputStream:t,wasmBytes:n,weightBytes:r,exportMap:i,modelSize:a,workletUrl:u,audioContext:d,onWarning:f}=e;if(![0,1,2].includes(a))throw new c(`Invalid modelSize: ${a}. Must be 0 (tiny), 1 (base), or 2 (small).`);let p=e=>{try{f?.(e)}catch{}},m=d===void 0,h=d;if(m)try{h=new AudioContext({sampleRate:St})}catch(e){throw new o(`Failed to create AudioContext: ${e instanceof Error?e.message:String(e)}`)}let g=async(e,t)=>{if(m&&e.state!==`closed`)try{await e.close()}catch(e){p(`${t}: ${e instanceof Error?e.message:String(e)}`)}};if(h.state===`suspended`)try{await h.resume()}catch(e){throw await g(h,`AudioContext.close() failed after resume error`),new o(`AudioContext.resume() failed (possibly due to autoplay restrictions): ${e instanceof Error?e.message:String(e)}`)}h.sampleRate!==St&&p(`AudioContext sample rate is ${h.sampleRate}Hz (expected: ${St}Hz). Audio quality may be reduced.`);let _;try{_=u??await _t()}catch(e){throw await g(h,`AudioContext.close() failed`),e}try{await h.audioWorklet.addModule(_)}catch(e){await g(h,`AudioContext.close() failed`);let t=e instanceof Error?e.message:String(e),n=_.startsWith(`blob:`)||_.startsWith(`data:`)?` If your Content Security Policy blocks blob: or data: URLs, provide a static processor.js URL via the workletUrl option, or add "blob:" to your worker-src CSP directive.`:``;throw new s(`Failed to register AudioWorklet: ${t}${n}`)}let v,y,b;try{v=new AudioWorkletNode(h,`fastenhancer-processor`,{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[1]}),y=h.createMediaStreamSource(t),b=h.createMediaStreamDestination(),y.connect(v),v.connect(b)}catch(e){if(y)try{y.disconnect()}catch{}if(v)try{v.disconnect()}catch{}throw b&&b.stream.getTracks().forEach(e=>{try{e.stop()}catch{}}),g(h,`AudioContext.close() failed`),new s(`Failed to construct AudioWorklet node: ${e instanceof Error?e.message:String(e)}`)}let x=new Promise((e,t)=>{let n=setTimeout(()=>{v.port.removeEventListener(`message`,r),t(new s(`Worklet initialization timed out (${Ct/1e3}s)`))},Ct),r=i=>{i.data?.type===`ready`?(clearTimeout(n),v.port.removeEventListener(`message`,r),e()):i.data?.type===`error`&&(clearTimeout(n),v.port.removeEventListener(`message`,r),t(new s(`Worklet initialization failed: ${i.data.message??`Unknown error`}`)))};v.port.addEventListener(`message`,r),v.port.start()}),S=n.slice(0),C=r.slice(0);v.port.postMessage({type:`init`,wasmBytes:S,weightBytes:C,exportMap:i,modelSize:a},[S,C]);try{await x}catch(e){try{y.disconnect()}catch(e){p(`source.disconnect() failed: ${e instanceof Error?e.message:String(e)}`)}try{v.disconnect()}catch(e){p(`workletNode.disconnect() failed: ${e instanceof Error?e.message:String(e)}`)}throw b.stream.getTracks().forEach(e=>{try{e.stop()}catch(e){p(`MediaStreamTrack.stop() failed during init cleanup: ${e instanceof Error?e.message:String(e)}`)}}),await g(h,`AudioContext.close() failed`),e}let w=`running`,T=!1,E=!1,D=!1,O=!1,k=null,ee=t=>{if(t.data?.type===`process_error`&&w!==`destroyed`){p(`Worklet processing error: ${t.data.message??`Unknown`}`);return}if(t.data?.type===`auto_bypass`&&w!==`destroyed`){E=!!t.data.enabled;try{e.onAutoBypass?.(E)}catch{}}},te=()=>{let t=h.state;if(p(`AudioContext state changed to "${t}".`),t===`suspended`&&w!==`destroyed`&&m){h.resume().catch(e=>{p(`AudioContext.resume() failed after suspension: ${e instanceof Error?e.message:String(e)}`)});return}if(t===`closed`){w=`destroyed`;try{e.onDestroy?.()}catch{}}};return v.port.addEventListener(`message`,ee),h.addEventListener(`statechange`,te),{get outputStream(){return b.stream},get state(){return w},get bypass(){return T},set bypass(e){if(w===`destroyed`)throw new l(`This StreamDenoiser has already been destroyed`);T=e,v.port.postMessage({type:`set_bypass`,enabled:e})},get autoPassthrough(){return E},get agcEnabled(){return D},set agcEnabled(e){if(w===`destroyed`)throw new l(`This StreamDenoiser has already been destroyed`);D=e,v.port.postMessage({type:`set_agc`,enabled:e})},get hpfEnabled(){return O},set hpfEnabled(e){if(w===`destroyed`)throw new l(`This StreamDenoiser has already been destroyed`);O=e,v.port.postMessage({type:`set_hpf`,enabled:e})},destroy(){this.destroyAsync().catch(e=>{p(`destroyAsync() failed: ${e instanceof Error?e.message:String(e)}`)})},async destroyAsync(){return k||(k=(async()=>{w=`destroyed`,h.removeEventListener(`statechange`,te),v.port.removeEventListener(`message`,ee);try{y.disconnect()}catch(e){p(`source.disconnect() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}try{v.disconnect()}catch(e){p(`workletNode.disconnect() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}let e=new Promise(e=>{let t=!1,n=()=>{t||(t=!0,clearTimeout(i),v.port.removeEventListener(`message`,r))},r=t=>{(t.data?.type===`destroyed`||t.data?.type===`error`)&&(n(),e())},i=setTimeout(()=>{n(),e()},2e3);v.port.addEventListener(`message`,r)});try{v.port.postMessage({type:`destroy`})}catch(e){p(`workletNode.port.postMessage() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}b.stream.getTracks().forEach(e=>{try{e.stop()}catch(e){p(`MediaStreamTrack.stop() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}}),await e;try{v.port.close()}catch(e){p(`workletNode.port.close() failed during destroy: ${e instanceof Error?e.message:String(e)}`)}await g(h,`AudioContext.close() failed during destroy`)})(),k)},getWorkletState(){if(w===`destroyed`)return Promise.reject(new l(`This StreamDenoiser has already been destroyed`));let e=`state_${Date.now()}_${Math.random().toString(36).slice(2,8)}`;return new Promise((t,n)=>{let r=setTimeout(()=>{v.port.removeEventListener(`message`,i),n(new s(`Worklet state retrieval timed out (${wt/1e3}s)`))},wt),i=n=>{n.data?.type===`state`&&n.data?.requestId===e&&(clearTimeout(r),v.port.removeEventListener(`message`,i),t(n.data))};v.port.addEventListener(`message`,i),v.port.start(),v.port.postMessage({type:`get_state`,requestId:e})})}}}var St,Ct,wt,Tt=t((()=>{u(),bt(),St=48e3,Ct=1e4,wt=500}));function Et(e){if(typeof e!=`object`||!e||Array.isArray(e))throw new i(`Export map JSON must be a plain object`);let t=e;for(let[e,n]of Object.entries(t))if(typeof n!=`string`)throw new i(`Export map entry "${e}" must be a string, got ${typeof n}`);return t}function Dt(e){return e.endsWith(`/`)?e:`${e}/`}function Ot(e,t,n){let r=n?Dt(n):void 0;return`${e}:${t??`auto`}:${r??`default`}`}async function kt(){try{return f()}catch(e){return typeof console<`u`&&console.warn(`[fastenhancer] SIMD detection failed:`,e),!1}}function At(){$.clear(),vt()}function jt(e,t){return $.delete(Ot(e,t?.simd,t?.baseUrl))}function Mt(e,t){if(!Lt.includes(e))throw new c(`Invalid model size: "${e}". Valid values: ${Lt.join(`, `)}`);let n=t?.signal;if(n?.aborted)return Promise.reject(n.reason??Error(`Aborted`));let r=t?.simd,i=t?.baseUrl,a=Ot(e,r,i),o=$.get(a);if(o)return o;let s=Nt(e,r,i,n);return $.set(a,s),s.catch(()=>$.delete(a)),s}async function Nt(e,t,n,r){let i=h(t===void 0?await kt():t);return n?Ft(e,i,n,r):Pt(e,i,r)}async function Pt(e,t,n){let r,a,o;try{[r,a,o]=await Promise.all([dt(e,t),ft(e),pt(e,t)])}catch(e){throw e instanceof c?e:new i(`Failed to load embedded model resources: ${e instanceof Error?e.message:String(e)}`)}if(n?.aborted)throw n.reason??Error(`Aborted`);return It(e,t,r,a,o)}async function Ft(e,t,n,r){let a=Dt(n),o=`fastenhancer-${e}-${t}`,s=`${a}${o}.wasm`,c=`${a}${o}-exports.json`,l=`${a}${zt[e]}`,u,d,f;try{let e=r?{signal:r}:void 0,[t,n,a]=await Promise.all([fetch(s,e),fetch(l,e),fetch(c,e)]);if(!t.ok)throw new i(`Failed to fetch WASM binary: ${t.status} ${t.statusText} (${s})`);if(!n.ok)throw new i(`Failed to fetch weight file: ${n.status} ${n.statusText} (${l})`);if(!a.ok)throw new i(`Failed to fetch export map: ${a.status} ${a.statusText} (${c})`);[u,d,f]=await Promise.all([t.arrayBuffer(),n.arrayBuffer(),a.json().then(Et)])}catch(e){throw e instanceof i?e:new i(`Failed to load model resources: ${e instanceof Error?e.message:String(e)}`)}return It(e,t,u,d,f)}function It(e,t,n,r,i){let a=Rt[e],o=T(e),s=null,c=async()=>(s||(s=v(n,i),s.catch(()=>{s=null})),s),l=Object.freeze({...i});return{size:e,variant:t,sampleRate:o.sampleRate,hopSize:o.hopSize,nFft:o.nFft,modelSizeId:a,get wasmBytes(){return n.slice(0)},get weightData(){return r.slice(0)},exportMap:l,wasmFactory:c,async createDenoiser(){let{createDenoiser:t}=await Promise.resolve().then(()=>(tn(),Yt));return t({model:{size:e,sampleRate:o.sampleRate,nFft:o.nFft,hopSize:o.hopSize,wasmFactory:c},weightData:r,modelSizeId:a})},async createStreamDenoiser(e,t){return xt({inputStream:e,wasmBytes:n,weightBytes:r,exportMap:i,modelSize:a,workletUrl:t?.workletUrl,onWarning:t?.onWarning,audioContext:t?.audioContext,onAutoBypass:t?.onAutoBypass,onDestroy:t?.onDestroy})}}}var Lt,Rt,zt,$,Bt=t((()=>{u(),g(),m(),w(),O(),Tt(),bt(),Lt=[`tiny`,`base`,`small`],Rt={tiny:0,base:1,small:2},zt={tiny:`fe_tiny_48k.bin`,base:`fe_base_48k.bin`,small:`fe_small_48k.bin`},$=new Map}));function Vt(){return Ut.map(e=>({...e}))}function Ht(e){let t=e?.priority??`quality`,n=Wt[t];if(!n){let e=Object.keys(Wt).join(`, `);throw new c(`Invalid priority: '${String(t)}'. Valid values: ${e}`)}return{...Ut[n.index],reason:n.reason}}var Ut,Wt,Gt=t((()=>{u(),O(),Ut=[{id:`tiny`,label:`Tiny`,params:28e3,wasmSizeKB:52,hopSize:D.tiny.hopSize,description:`Lightest model. Suitable for low-latency environments and mobile devices.`},{id:`base`,label:`Base`,params:101e3,wasmSizeKB:65,hopSize:D.base.hopSize,description:`Balanced model. Combines quality and speed.`},{id:`small`,label:`Small`,params:207e3,wasmSizeKB:75,hopSize:D.small.hopSize,description:`Recommended model. Provides the highest-quality noise removal.`}],Wt={speed:{index:0,reason:`Prioritizes processing speed. Runs reliably on mobile and lower-spec environments.`},balanced:{index:1,reason:`Well-balanced between quality and speed, and also suitable for mobile environments.`},quality:{index:2,reason:`Highest-quality noise removal. Recommended for most environments.`}}}));async function Kt(){try{return f()}catch{return!1}}async function qt(){let e=[],t=typeof WebAssembly<`u`;t||e.push(`WebAssembly is not available. A modern browser is required.`);let n=await Kt();t&&!n&&e.push(`WASM SIMD is not available. Chrome 91+, Firefox 89+, or Safari 16.4+ is required. The scalar fallback will still work, but performance will be reduced.`);let r=typeof AudioContext<`u`;r||e.push(`AudioContext is not available. A browser with Web Audio API support is required.`);let i=typeof AudioWorkletNode<`u`&&typeof AudioContext<`u`&&`audioWorklet`in AudioContext.prototype;i||e.push(`AudioWorklet is not available. Chrome 66+, Firefox 76+, or Safari 14.1+ is required.`);let a=t&&r&&i;return{wasm:t,simd:n,audioContext:r,audioWorklet:i,overall:a,recommended:a&&n,issues:e}}var Jt=t((()=>{m()})),Yt=n({AudioContextError:()=>o,DestroyedError:()=>l,FastEnhancerError:()=>r,ModelInitError:()=>a,ValidationError:()=>c,WasmLoadError:()=>i,WorkletError:()=>s,clearCachedModel:()=>jt,clearModelCache:()=>At,createDenoiser:()=>Qt,createStreamDenoiser:()=>xt,diagnose:()=>qt,getModels:()=>Vt,isSupported:()=>$t,loadModel:()=>Mt,recommendModel:()=>Ht});function Xt(e,t){if(t!==void 0)return t;let n=en[e.size];if(n===void 0)throw new c(`Unknown model size: "${e.size}". Valid values: tiny, base, small`);return n}function Zt(e,t,n){let r=`ready`,i=!1,o=!1,s=!1,u=0,d=null,f=e,p=t,m=n;function h(e,t){if(!e||e%4!=0)throw new a(`Invalid WASM ${t} pointer: ${e} (must be non-zero and 4-byte aligned)`);return e/4}let g=h(f._fe_get_input_ptr(p),`input`),_=h(f._fe_get_output_ptr(p),`output`),v=new Map,y=new Map,b=2e3,x=new Float64Array(b),S=0,C=0,w=0,T=0;function E(e,...t){let n=v.get(e);if(n)for(let e of n)try{e(...t)}catch(e){typeof console<`u`&&console.error(`[fastenhancer] Event handler error:`,e)}let r=y.get(e);if(r){for(let e of r)try{e(...t)}catch(e){typeof console<`u`&&console.error(`[fastenhancer] Event handler error:`,e)}r.clear()}}function D(e){if(r===`destroyed`)throw new l(`Cannot ${e}: denoiser is destroyed`)}function O(){return r===`destroyed`}let k={get state(){return r},processFrame(e){if(D(`processFrame`),e.length!==m)throw new c(`Expected ${m} samples, got ${e.length}`);for(let t=0;t<e.length;t++)if(!Number.isFinite(e[t]))throw new c(`Input contains non-finite value at index ${t}: ${e[t]}`);if(i)return new Float32Array(e);let t=performance.now();try{f.HEAPF32.set(e,g),f._fe_process_inplace(p);let n=new Float32Array(m);n.set(f.HEAPF32.subarray(_,_+m));let r=performance.now()-t;return x[S%b]=r,S++,C=Math.min(C+1,b),w++,n}catch(e){throw T++,E(`error`,{code:`PROCESS_ERROR`,message:e instanceof Error?e.message:String(e),original:e}),e}},get bypass(){return i},set bypass(e){D(`set bypass`),i=e},get agcEnabled(){return o},set agcEnabled(e){D(`set agcEnabled`),o=e,f._fe_set_agc(p,+!!e)},get hpfEnabled(){return s},set hpfEnabled(e){D(`set hpfEnabled`),s=e,f._fe_set_hpf(p,+!!e)},destroy(){r!==`destroyed`&&(f._fe_destroy(p),r=`destroyed`,E(`statechange`,`destroyed`),E(`destroy`),v.clear(),y.clear())},on(e,t){v.has(e)||v.set(e,new Set),v.get(e).add(t)},off(e,t){v.get(e)?.delete(t),y.get(e)?.delete(t)},once(e,t){y.has(e)||y.set(e,new Set),y.get(e).add(t)},get performance(){if(w===0)return{avgMs:0,p99Ms:0,droppedFrames:T,totalFrames:0};let e=[];for(let t=0;t<C;t++)e.push(x[t]);let t=e.reduce((e,t)=>e+t,0)/C;return e.sort((e,t)=>e-t),{avgMs:t,p99Ms:e[Math.min(Math.ceil(C*.99)-1,C-1)],droppedFrames:T,totalFrames:w}},get isSwitching(){return u>0},async switchModel(e){D(`switchModel`),u++;let t=async()=>{try{let{model:t,weightData:n,modelSizeId:r}=e,i=Xt(t,r),c=t._wasm??await t.wasmFactory();if(O())return;let l=new Uint8Array(n),u=l.byteLength,d=c._malloc(u);if(d===0)throw new a(`Failed to allocate memory in the WASM heap`);let v;try{new Uint8Array(c.HEAPF32.buffer).set(l,d),v=c._fe_init(i,d,u)}finally{c._free(d)}if(O()){v!==0&&c._fe_destroy(v);return}if(v===0)throw new a(`Failed to initialize the new model`);let y=f,b=p;f=c,p=v,m=c._fe_get_hop_size(v),g=h(c._fe_get_input_ptr(v),`input`),_=h(c._fe_get_output_ptr(v),`output`),o&&c._fe_set_agc(v,1),s&&c._fe_set_hpf(v,1),y._fe_destroy(b),S=0,C=0,w=0,T=0}finally{u--}};d=(d??Promise.resolve()).then(t,t),await d},[Symbol.dispose](){k.destroy()},async[Symbol.asyncDispose](){k.destroy()}};return k}async function Qt(e){let{model:t,weightData:n,modelSizeId:r}=e,i=Xt(t,r),o=t._wasm??await t.wasmFactory(),s=new Uint8Array(n),c=s.byteLength,l=o._malloc(c);if(l===0)throw new a(`Failed to allocate memory in the WASM heap`);let u;try{o.HEAPF32.buffer,new Uint8Array(o.HEAPF32.buffer).set(s,l),u=o._fe_init(i,l,c)}finally{o._free(l)}if(u===0)throw new a(`Failed to initialize the denoiser engine`);let d=o._fe_get_hop_size(u);return Zt(o,u,d)}async function $t(){let e=typeof WebAssembly<`u`&&typeof WebAssembly.instantiate==`function`,t=!1;if(e)try{t=f()}catch{t=!1}let n=typeof globalThis<`u`&&typeof globalThis.AudioWorkletNode==`function`;return{wasm:e,simd:t,audioWorklet:n}}var en,tn=t((()=>{u(),m(),Bt(),Tt(),Gt(),Jt(),en={tiny:0,base:1,small:2}}));tn();var nn=class{speech;previous=[];primed=!1;constructor(e){this.speech=e}discontinuity(){this.primed=!1}process(e){let t=e.length;if(![1,2,4,6,8].includes(t)||e.some(e=>e.length!==512||e.some(e=>!Number.isFinite(e))))throw Error(`Invalid background audio frame`);this.previous.length!==t&&(this.previous=e.map(()=>new Float32Array(512)),this.primed=!1);let n=t>=6?3:Math.min(2,t);if(this.speech.length<n)throw Error(`Missing speech separator`);let r=e.slice(0,n).map((e,t)=>{let n=this.speech[t].processFrame(e);if(n.length!==512||n.some(e=>!Number.isFinite(e)))throw Error(`Speech separation failed`);return n}),i=[new Float32Array(512),new Float32Array(512)];if(this.primed)for(let e=0;e<512;e++){let n=this.previous[0][e]-r[0][e],a=t===1?n:this.previous[1][e]-r[1][e];if(t===4)n=.5*(n+this.previous[2][e]),a=.5*(a+this.previous[3][e]);else if(t>=6){let i=Math.SQRT1_2*(this.previous[2][e]-r[2][e]),o=t===6?Math.SQRT1_2:.5;n+=i+o*(this.previous[4][e]+(t===8?this.previous[6][e]:0)),a+=i+o*(this.previous[5][e]+(t===8?this.previous[7][e]:0))}i[0][e]=Math.max(-1,Math.min(1,n)),i[1][e]=Math.max(-1,Math.min(1,a))}return e.forEach((e,t)=>this.previous[t].set(e)),this.primed=!0,i}destroy(){for(let e of this.speech)e.destroy()}};async function rn(){let e=[];try{let t=await Mt(`tiny`);if(t.sampleRate!==48e3||t.hopSize!==512)throw Error(`Unsupported separation format`);jt(`tiny`);let n=await Mt(`tiny`);for(let r of[t,t,n]){let t=await r.createDenoiser();t.agcEnabled=t.hpfEnabled=!1,e.push(t)}return new nn(e)}catch(t){for(let t of e)t.destroy();throw t}}self.onmessage=async e=>{let t=e.data.port;try{let e=await rn(),n=-1,r=!1;t.onmessage=i=>{if(!r)try{let r=i.data.id;if(!Number.isSafeInteger(r)||r<=n)throw Error(`Invalid background audio order`);r!==n+1&&e.discontinuity();let a=e.process(i.data.channels);t.postMessage({id:n,channels:a},a.map(e=>e.buffer)),n=r}catch{r=!0,e.destroy(),self.postMessage({error:!0})}},t.start(),self.postMessage({ready:!0})}catch(e){self.postMessage({error:!0,reason:e instanceof Error?e.message:`Background model unavailable`})}}})();
@@ -1 +1 @@
1
- import{t as e}from"./index-DzHGkEbU.js";var t=3;async function n(n){let{media:r,src:i,isTv:a}=n,{default:o}=await e(async()=>{let{default:e}=await import(`./hls-n74Cnh8A.js`);return{default:e}},[]);if(!o.isSupported())return n.onError(`This browser cannot play HLS streams.`),{destroy:()=>void 0,levels:()=>[]};let s=new o({...a?{maxBufferLength:60,maxMaxBufferLength:120,backBufferLength:30,liveSyncDurationCount:4}:{backBufferLength:90},enableWorker:!0}),c=0,l=!1;s.on(o.Events.ERROR,(e,r)=>{if(!l&&r.fatal){if(c>=t){n.onError(`This stream kept failing and has been stopped.`),s.destroy();return}switch(c+=1,r.type){case o.ErrorTypes.NETWORK_ERROR:n.onNotice(`Reconnecting…`),s.startLoad();break;case o.ErrorTypes.MEDIA_ERROR:n.onNotice(`Recovering…`),s.recoverMediaError();break;default:n.onError(`This stream could not be played.`),s.destroy()}}}),s.on(o.Events.MANIFEST_PARSED,()=>{l||(n.onNotice(null),n.onReady?.({live:s.levels.length>0&&!Number.isFinite(r.duration),levels:u()}))}),s.on(o.Events.LEVEL_LOADED,(e,t)=>{l||n.onReady?.({live:t.details.live,levels:u()})}),s.on(o.Events.FRAG_BUFFERED,()=>{l||n.onNotice(null)});function u(){return s.levels.map((e,t)=>({index:t,height:e.height||null,bitrate:e.bitrate||null,label:e.height?`${String(e.height)}p`:`${String(Math.round((e.bitrate||0)/1e3))}k`}))}return s.loadSource(i),s.attachMedia(r),{destroy(){l=!0,s.destroy()},levels:u,setLevel(e){s.currentLevel=e},currentLevel:()=>s.autoLevelEnabled?-1:s.currentLevel}}export{n as createHlsEngine};
1
+ import{t as e}from"./index-Ckxp8OU8.js";var t=3;async function n(n){let{media:r,src:i,isTv:a}=n,{default:o}=await e(async()=>{let{default:e}=await import(`./hls-n74Cnh8A.js`);return{default:e}},[]);if(!o.isSupported())return n.onError(`This browser cannot play HLS streams.`),{destroy:()=>void 0,levels:()=>[]};let s=new o({...a?{maxBufferLength:60,maxMaxBufferLength:120,backBufferLength:30,liveSyncDurationCount:4}:{backBufferLength:90},enableWorker:!0}),c=0,l=!1;s.on(o.Events.ERROR,(e,r)=>{if(!l&&r.fatal){if(c>=t){n.onError(`This stream kept failing and has been stopped.`),s.destroy();return}switch(c+=1,r.type){case o.ErrorTypes.NETWORK_ERROR:n.onNotice(`Reconnecting…`),s.startLoad();break;case o.ErrorTypes.MEDIA_ERROR:n.onNotice(`Recovering…`),s.recoverMediaError();break;default:n.onError(`This stream could not be played.`),s.destroy()}}}),s.on(o.Events.MANIFEST_PARSED,()=>{l||(n.onNotice(null),n.onReady?.({live:s.levels.length>0&&!Number.isFinite(r.duration),levels:u()}))}),s.on(o.Events.LEVEL_LOADED,(e,t)=>{l||n.onReady?.({live:t.details.live,levels:u()})}),s.on(o.Events.FRAG_BUFFERED,()=>{l||n.onNotice(null)});function u(){return s.levels.map((e,t)=>({index:t,height:e.height||null,bitrate:e.bitrate||null,label:e.height?`${String(e.height)}p`:`${String(Math.round((e.bitrate||0)/1e3))}k`}))}return s.loadSource(i),s.attachMedia(r),{destroy(){l=!0,s.destroy()},levels:u,setLevel(e){s.currentLevel=e},currentLevel:()=>s.autoLevelEnabled?-1:s.currentLevel}}export{n as createHlsEngine};