Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- assets/index-Bjmxo3sN.js +0 -0
- assets/index-DIsX7k44.css +1 -0
- assets/worker-Bhtqsxdm.js +1 -0
- index.html +2 -2
assets/index-Bjmxo3sN.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/index-DIsX7k44.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
*,:before,:after{box-sizing:border-box;margin:0;padding:0}:root{--bg:#111;--surface:#1a1a1a;--surface-2:#222;--border:#333;--text:#e5e5e5;--text-muted:#888;--accent:#c084fc;--accent-dim:#c084fc26;--radius:8px;--font:system-ui, -apple-system, "Segoe UI", sans-serif;--mono:ui-monospace, "SF Mono", Consolas, monospace}html{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}body{font-family:var(--font);background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;line-height:1.5}.container{flex-direction:column;max-width:720px;min-height:100vh;margin:0 auto;padding:2rem 1.5rem;display:flex}header{text-align:center;margin-bottom:2rem;position:relative}header h1{letter-spacing:-.02em;margin-bottom:.25rem;font-size:1.75rem;font-weight:600}.logo{font-size:1.5rem}.subtitle{color:var(--text-muted);font-size:.9rem}.badge{font-size:.7rem;font-weight:600;font-family:var(--mono);letter-spacing:.05em;border-radius:999px;margin-top:.5rem;padding:.15rem .6rem;display:inline-block}.badge-gpu{color:#4ade80;background:#4ade8026;border:1px solid #4ade804d}.badge-wasm{color:#fbbf24;background:#fbbf2426;border:1px solid #fbbf244d}main{flex-direction:column;flex:1;gap:1.5rem;display:flex}label{color:var(--text-muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:.4rem;font-size:.8rem;font-weight:500;display:block}textarea{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text);font-family:var(--font);resize:vertical;outline:none;padding:.75rem;font-size:.95rem;transition:border-color .15s}textarea:focus{border-color:var(--accent)}textarea::placeholder{color:#555}.controls-row{grid-template-columns:1fr 1fr;gap:.75rem;margin-top:.75rem;display:grid}select{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text);font-family:var(--font);cursor:pointer;appearance:none;background-image:url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23888' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-position:right .75rem center;background-repeat:no-repeat;outline:none;padding:.5rem 2rem .5rem .75rem;font-size:.9rem}select:focus{border-color:var(--accent)}select:disabled{opacity:.5;cursor:not-allowed}.speed-row{margin-top:.75rem}.speed-row label{font-family:var(--mono);font-size:.75rem}input[type=range]{width:100%;accent-color:var(--accent);cursor:pointer;height:4px}.generate-btn{background:var(--accent);color:#111;border-radius:var(--radius);width:100%;font-family:var(--font);cursor:pointer;border:none;margin-top:1rem;padding:.7rem 1.5rem;font-size:.9rem;font-weight:600;transition:opacity .15s}.generate-btn:hover:not(:disabled){opacity:.9}.generate-btn:disabled{opacity:.4;cursor:not-allowed}.output-section{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:1rem}.waveform-player{align-items:center;gap:.75rem;display:flex}.waveform-play{background:var(--accent);color:#111;cursor:pointer;border:none;border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;width:36px;height:36px;transition:opacity .15s;display:flex}.waveform-play:hover{opacity:.85}.waveform-canvas{cursor:pointer;border-radius:4px;flex:1;height:48px}.waveform-time{font-family:var(--mono);color:var(--text-muted);text-align:right;flex-shrink:0;min-width:5.5em;font-size:.7rem}.waveform-gen-time{font-family:var(--mono);color:#555;background:var(--surface-2);border-radius:4px;flex-shrink:0;padding:.15rem .4rem;font-size:.65rem}.audio-placeholder{text-align:center;color:#555;padding:2rem 1rem;font-size:.85rem}.examples-grid{flex-direction:column;gap:.5rem;display:flex}.example-btn{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);width:100%;color:var(--text);font-family:var(--font);cursor:pointer;text-align:left;align-items:baseline;gap:.75rem;padding:.6rem .75rem;font-size:.85rem;transition:border-color .15s;display:flex}.example-btn:hover:not(:disabled){border-color:var(--accent)}.example-btn:disabled{opacity:.4;cursor:not-allowed}.example-voice{font-family:var(--mono);color:var(--accent);background:var(--accent-dim);border-radius:4px;flex-shrink:0;padding:.1rem .4rem;font-size:.75rem;font-weight:600}.example-text{color:var(--text-muted);flex:1}.example-meta{font-family:var(--mono);color:#555;flex-shrink:0;font-size:.7rem}.error-msg{color:#f87171;border-radius:var(--radius);background:#ef44441a;border:1px solid #ef44444d;padding:.75rem 1rem;font-size:.85rem}footer{border-top:1px solid var(--border);text-align:center;color:var(--text-muted);flex-direction:column;gap:.25rem;margin-top:2.5rem;padding-top:1.5rem;font-size:.75rem;display:flex}footer a{color:var(--text-muted);text-underline-offset:2px;text-decoration:underline;transition:color .15s}footer a:hover{color:var(--text)}@media (width<=480px){.container{padding:1.25rem 1rem}.controls-row{grid-template-columns:1fr}header h1{font-size:1.5rem}}
|
assets/worker-Bhtqsxdm.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
const e=[`$`,...`;:,.!?¡¿—…"«»"" `,...`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`,...`ɑɐɒæɓʙβɔɕçɗɖðʤəɘɚɛɜɝɞɟʄɡɠɢʛɦɧħɥʜɨɪʝɭɬɫɮʟɱɯɰŋɳɲɴøɵɸθœɶʘɹɺɾɻʀʁɽʂʃʈʧʉʊʋⱱʌɣɤʍχʎʏʑʐʒʔʡʕʢǀǁǂǃˈˌːˑʼʴʰʱʲʷˠˤ˞↓↑→↗↘'̩'ᵻ`],t={};for(let n=0;n<e.length;n++)t[e[n]]=n;function n(e){let n=[];for(let r of e){let e=t[r];e!==void 0&&n.push(e)}return n}function r(e){let t=n(e);return t.unshift(0),t.push(10),t.push(0),t}function i(e){if(e[0]!==147||String.fromCharCode(e[1],e[2],e[3],e[4],e[5])!==`NUMPY`)throw Error(`Not a valid .npy file`);let t=e[6],n=new DataView(e.buffer,e.byteOffset,e.byteLength),r,i;t===1?(r=n.getUint16(8,!0),i=10):(r=n.getUint32(8,!0),i=12);let a=new TextDecoder().decode(e.slice(i,i+r)),o=a.match(/'descr'\s*:\s*'([^']+)'/),s=a.match(/'shape'\s*:\s*\(([^)]*)\)/);if(!o)throw Error(`Could not parse dtype from .npy header: `+a);return{descr:o[1],shape:s?s[1].split(`,`).map(e=>parseInt(e.trim(),10)).filter(e=>!isNaN(e)):[],dataOffset:i+r}}function a(e){let{descr:t,shape:n,dataOffset:r}=i(e),a=e.slice(r),o=new ArrayBuffer(a.length);new Uint8Array(o).set(a);let s;if(t===`<f4`||t===`float32`)s=new Float32Array(o);else if(t===`<f8`||t===`float64`){let e=new Float64Array(o);s=new Float32Array(e.length);for(let t=0;t<e.length;t++)s[t]=e[t]}else throw Error(`Unsupported npy dtype: `+t);return{data:s,shape:n}}async function o(e){let t=new Uint8Array(e),n=new DataView(e),r=new Map,i=-1;for(let e=t.length-22;e>=0;e--)if(n.getUint32(e,!0)===101010256){i=e;break}if(i===-1)throw Error(`Could not find End of Central Directory`);let a=n.getUint32(i+16,!0),o=n.getUint16(i+10,!0),s=[],c=a;for(let e=0;e<o&&n.getUint32(c,!0)===33639248;e++){let e=n.getUint16(c+10,!0),r=n.getUint32(c+20,!0),i=n.getUint32(c+24,!0),a=n.getUint16(c+28,!0),o=n.getUint16(c+30,!0),l=n.getUint16(c+32,!0),u=n.getUint32(c+42,!0),d=new TextDecoder().decode(t.slice(c+46,c+46+a));s.push({fileName:d,compressedSize:r,uncompressedSize:i,localHeaderOffset:u,compressionMethod:e}),c+=46+a+o+l}for(let e of s){let i=e.localHeaderOffset,a=n.getUint16(i+26,!0),o=n.getUint16(i+28,!0),s=i+30+a+o,c;if(e.compressionMethod===0)c=t.slice(s,s+e.uncompressedSize);else if(e.compressionMethod===8){let n=t.slice(s,s+e.compressedSize),r=new DecompressionStream(`deflate-raw`),i=r.writable.getWriter();i.write(n),i.close();let a=r.readable.getReader(),o=[],l=0;for(;;){let{done:e,value:t}=await a.read();if(e)break;o.push(t),l+=t.length}c=new Uint8Array(l);let u=0;for(let e of o)c.set(e,u),u+=e.length}else{console.warn(`Skipping ${e.fileName}: unsupported compression ${e.compressionMethod}`);continue}r.set(e.fileName,c)}return r}async function s(e){let t=await fetch(e);if(!t.ok)throw Error(`Failed to fetch voices: ${t.status}`);let n=await o(await t.arrayBuffer()),r={};for(let[e,t]of n){if(!e.endsWith(`.npy`))continue;let n=e.replace(/\.npy$/,``),{data:i,shape:o}=a(t);r[n]={data:i,shape:[o[0]||1,o[1]||i.length]}}return r}let c,l;const u=[`Nano`,`nano`,`fp32`];let d=null,f={},p=null,m=`wasm`;function h(e,t){return`https://huggingface.co/${e}/resolve/main/${t}`}async function g(){try{return`gpu`in navigator?!!await navigator.gpu.requestAdapter():!1}catch{return!1}}async function _(e){self.postMessage({type:`status`,message:`Detecting hardware...`});let t=await g();self.postMessage({type:`status`,message:`Loading runtime...`});let[n,r]=await Promise.all([import(`./ort.bundle.min-DL658BJE.js`),import(`./phonemizer-BgK0uh4o.js`)]);l=n,c=r.phonemize,self.postMessage({type:`status`,message:`Loading config...`});let i=await fetch(h(e,`kitten_config.json`));i.ok||(i=await fetch(h(e,`config.json`))),p=await i.json();let a=p.model||e.split(`/`).pop()||``,o=u.some(e=>a.includes(e));m=t&&o?`webgpu`:`wasm`,t&&!o&&console.log(`[KittenTTS] Using WASM for "${a}" (WebGPU only confirmed for nano-fp32)`),self.postMessage({type:`device`,device:m}),self.postMessage({type:`status`,message:`Downloading model & voices...`});let _=h(e,p.model_file||`onnx/model.onnx`),v=(async()=>{let e=await fetch(_);if(!e.ok)throw Error(`Failed to fetch model: ${e.status}`);let t=parseInt(e.headers.get(`content-length`)||`0`,10),n=e.body.getReader(),r=[],i=0;for(;;){let{done:e,value:a}=await n.read();if(e)break;if(r.push(a),i+=a.length,t>0){let e=Math.round(i/t*100),n=(i/1024/1024).toFixed(1);self.postMessage({type:`status`,message:`Downloading model... ${e}% (${n} MB)`})}}let a=new Uint8Array(i),o=0;for(let e of r)a.set(e,o),o+=e.length;return a.buffer})(),y=s(h(e,p.voices)),[b,x]=await Promise.all([v,y]);f=x,self.postMessage({type:`status`,message:`Initializing ${m.toUpperCase()} session...`});let S={executionProviders:m===`webgpu`?[`webgpu`]:[`wasm`]};m===`wasm`&&(l.env.wasm.numThreads=1),d=await l.InferenceSession.create(b,S);let C=p.voice_aliases?Object.keys(p.voice_aliases):Object.keys(f);self.postMessage({type:`ready`,voices:C,device:m,modelName:p.name})}function v(e){return e=e.trim(),e&&(`.!?,;:`.includes(e[e.length-1])||(e+=`,`),e)}function y(e,t=400){let n=e.match(/[^.!?]*[.!?]+|[^.!?]+$/g)||[e],r=[];for(let e of n)if(e=e.trim(),e)if(e.length<=t)r.push(v(e));else{let n=e.split(/\s+/),i=``;for(let e of n)i.length+e.length+1<=t?i+=(i?` `:``)+e:(i&&r.push(v(i)),i=e);i&&r.push(v(i))}return r}function b(e){return e.match(/[\p{L}\p{N}_]+|[^\p{L}\p{N}_\s]/gu)||[]}async function x(e,t,n){if(!d||!p)throw Error(`Model not loaded`);let i=t;p.voice_aliases?.[t]&&(i=p.voice_aliases[t]);let a=f[i];if(!a)throw Error(`Voice "${t}" not found`);p.speed_priors?.[i]&&(n*=p.speed_priors[i]);let o=r(b((await c(e,`en-us`))[0]||``).join(` `)),s=Math.min(e.length,a.shape[0]-1),u=a.shape[1],h=a.data.slice(s*u,(s+1)*u),g=new l.Tensor(`int64`,BigInt64Array.from(o.map(BigInt)),[1,o.length]),_=new l.Tensor(`float32`,h,[1,u]),v=new l.Tensor(`float32`,new Float32Array([n]),[1]),y=(await d.run({input_ids:g,style:_,speed:v}))[d.outputNames[0]].data;return y.length>0&&isNaN(y[0])&&console.warn(`[KittenTTS] Model produced NaN audio — this model may not be compatible with ${m.toUpperCase()}`),y.slice(0,Math.max(0,y.length-5e3))}async function S(e,t,n){try{let r=y(e);self.postMessage({type:`status`,message:`Generating (${r.length} chunk${r.length>1?`s`:``})...`});let i=[];for(let e=0;e<r.length;e++){self.postMessage({type:`progress`,current:e+1,total:r.length});let a=await x(r[e],t,n);i.push(a)}let a=i.reduce((e,t)=>e+t.length,0),o=new Float32Array(a),s=0;for(let e of i)o.set(e,s),s+=e.length;self.postMessage({type:`audio`,audio:o.buffer,sampleRate:24e3},{transfer:[o.buffer]})}catch(e){self.postMessage({type:`error`,error:e.message||String(e)})}}self.addEventListener(`message`,async e=>{let{action:t,...n}=e.data;switch(t){case`load`:try{await _(n.repoId)}catch(e){console.error(`[KittenTTS Worker] Load error:`,e),self.postMessage({type:`error`,error:e.message||String(e)})}break;case`generate`:await S(n.text,n.voice,n.speed);break}}),self.addEventListener(`error`,e=>{self.postMessage({type:`error`,error:e.message||`Unknown worker error`})}),self.addEventListener(`unhandledrejection`,e=>{self.postMessage({type:`error`,error:e.reason?.message||String(e.reason)})});
|
index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>KittenTTS — Browser TTS with WebGPU</title>
|
| 7 |
<meta name="description" content="Text-to-speech running entirely in your browser via WebGPU. Powered by KittenML models and Transformers.js v4." />
|
| 8 |
-
<script type="module" crossorigin src="/assets/index-
|
| 9 |
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<div id="root"></div>
|
|
|
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
<title>KittenTTS — Browser TTS with WebGPU</title>
|
| 7 |
<meta name="description" content="Text-to-speech running entirely in your browser via WebGPU. Powered by KittenML models and Transformers.js v4." />
|
| 8 |
+
<script type="module" crossorigin src="/assets/index-Bjmxo3sN.js"></script>
|
| 9 |
+
<link rel="stylesheet" crossorigin href="/assets/index-DIsX7k44.css">
|
| 10 |
</head>
|
| 11 |
<body>
|
| 12 |
<div id="root"></div>
|