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 +28 -5
- package/dist/catalogs.d.ts +4 -0
- package/dist/catalogs.js +0 -0
- package/dist/server.js +1 -0
- package/package.json +1 -1
- package/src/catalogs.ts +0 -0
- package/src/server.ts +1 -0
- package/web/dist/assets/{background.worker-D9ejvk_o.js → background.worker-DH8ag5Q4.js} +1 -1
- package/web/dist/assets/{hls-3VKVEQE3-6OPW0QO6.js → hls-3VKVEQE3-e7zQjVfV.js} +1 -1
- package/web/dist/assets/index-Ckxp8OU8.js +1 -0
- package/web/dist/assets/{index-BgBpDqGR.css → index-biLbPIu_.css} +1 -1
- package/web/dist/assets/{mpegts-B3iuWZCe.js → mpegts-Ci1G5S8S.js} +1 -1
- package/web/dist/assets/{mpegts-LO6RVLD6-DbjJutt4.js → mpegts-LO6RVLD6-cj6SzTIe.js} +1 -1
- package/web/dist/index.html +3 -2
- package/web/dist/sw.js +7 -7
- package/web/dist/assets/index-DzHGkEbU.js +0 -1
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
|
|
576
|
-
|
|
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.
|
|
579
|
-
|
|
580
|
-
|
|
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
|
package/dist/catalogs.d.ts
CHANGED
|
@@ -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
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-
|
|
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};
|