Spaces:
Running
Running
Update index.html
Browse files- index.html +131 -18
index.html
CHANGED
|
@@ -1,19 +1,132 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="hi">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 6 |
+
<title>Hindi Text to Voice</title>
|
| 7 |
+
<style>
|
| 8 |
+
body{font-family:system-ui,sans-serif;max-width:640px;margin:0 auto;padding:16px;background:#f6f7f9;color:#111}
|
| 9 |
+
h1{font-size:1.4rem}
|
| 10 |
+
label{display:block;margin:14px 0 6px;font-weight:600}
|
| 11 |
+
textarea,select,button{width:100%;font-size:1rem;padding:12px;border-radius:10px;border:1px solid #ccc;box-sizing:border-box}
|
| 12 |
+
textarea{min-height:140px}
|
| 13 |
+
button{background:#0a7d5a;color:#fff;border:0;margin-top:14px;font-weight:600}
|
| 14 |
+
button.secondary{background:#444}
|
| 15 |
+
.row{display:flex;gap:10px}
|
| 16 |
+
#status{margin-top:12px;color:#555;font-size:.9rem}
|
| 17 |
+
audio{width:100%;margin-top:12px}
|
| 18 |
+
</style>
|
| 19 |
+
</head>
|
| 20 |
+
<body>
|
| 21 |
+
<h1>Hindi Text to Voice</h1>
|
| 22 |
+
|
| 23 |
+
<label for="engine">Engine</label>
|
| 24 |
+
<select id="engine">
|
| 25 |
+
<option value="device">Device voices (turant, koi download nahi)</option>
|
| 26 |
+
<option value="ai">AI Hindi voice (pehli baar ~30-100MB download)</option>
|
| 27 |
+
</select>
|
| 28 |
+
|
| 29 |
+
<div id="voiceBox">
|
| 30 |
+
<label for="voice">Voice</label>
|
| 31 |
+
<select id="voice"></select>
|
| 32 |
+
<label for="rate">Speed</label>
|
| 33 |
+
<select id="rate">
|
| 34 |
+
<option value="0.8">Dheere</option>
|
| 35 |
+
<option value="1" selected>Normal</option>
|
| 36 |
+
<option value="1.25">Tez</option>
|
| 37 |
+
</select>
|
| 38 |
+
</div>
|
| 39 |
+
|
| 40 |
+
<label for="text">Text</label>
|
| 41 |
+
<textarea id="text" placeholder="यहाँ हिंदी टेक्स्ट लिखो...">नमस्ते, आप कैसे हैं? यह मेरी अपनी टेक्स्ट टू वॉइस वेबसाइट है।</textarea>
|
| 42 |
+
|
| 43 |
+
<div class="row">
|
| 44 |
+
<button id="play">▶ Bolo</button>
|
| 45 |
+
<button id="stop" class="secondary">■ Stop</button>
|
| 46 |
+
</div>
|
| 47 |
+
<div id="status"></div>
|
| 48 |
+
<audio id="player" controls hidden></audio>
|
| 49 |
+
|
| 50 |
+
<script type="module">
|
| 51 |
+
const $ = id => document.getElementById(id);
|
| 52 |
+
const engine = $("engine"), voiceSel = $("voice"), statusEl = $("status");
|
| 53 |
+
|
| 54 |
+
// ---------- Device voices (Web Speech API) ----------
|
| 55 |
+
function loadVoices() {
|
| 56 |
+
const all = speechSynthesis.getVoices();
|
| 57 |
+
const hi = all.filter(v => v.lang.toLowerCase().startsWith("hi"));
|
| 58 |
+
const list = hi.length ? hi : all;
|
| 59 |
+
voiceSel.innerHTML = "";
|
| 60 |
+
list.forEach(v => {
|
| 61 |
+
const o = document.createElement("option");
|
| 62 |
+
o.value = v.name;
|
| 63 |
+
o.textContent = `${v.name} (${v.lang})`;
|
| 64 |
+
voiceSel.appendChild(o);
|
| 65 |
+
});
|
| 66 |
+
if (!hi.length) statusEl.textContent =
|
| 67 |
+
"Is device par Hindi voice nahi mili. Phone Settings > Text-to-speech mein Hindi voice install karo, ya AI engine chuno.";
|
| 68 |
+
}
|
| 69 |
+
if ("speechSynthesis" in window) {
|
| 70 |
+
loadVoices();
|
| 71 |
+
speechSynthesis.onvoiceschanged = loadVoices;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
// ---------- AI voice (transformers.js, MMS Hindi) ----------
|
| 75 |
+
let ttsPipe = null;
|
| 76 |
+
async function loadAI() {
|
| 77 |
+
if (ttsPipe) return ttsPipe;
|
| 78 |
+
statusEl.textContent = "Model download ho raha hai, thoda ruko...";
|
| 79 |
+
const { pipeline } = await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3");
|
| 80 |
+
ttsPipe = await pipeline("text-to-speech", "Xenova/mms-tts-hin");
|
| 81 |
+
return ttsPipe;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
function wavUrl(float32, sr) {
|
| 85 |
+
const n = float32.length, buf = new ArrayBuffer(44 + n * 2), v = new DataView(buf);
|
| 86 |
+
const w = (o, s) => [...s].forEach((c, i) => v.setUint8(o + i, c.charCodeAt(0)));
|
| 87 |
+
w(0, "RIFF"); v.setUint32(4, 36 + n * 2, true); w(8, "WAVE"); w(12, "fmt ");
|
| 88 |
+
v.setUint32(16, 16, true); v.setUint16(20, 1, true); v.setUint16(22, 1, true);
|
| 89 |
+
v.setUint32(24, sr, true); v.setUint32(28, sr * 2, true); v.setUint16(32, 2, true);
|
| 90 |
+
v.setUint16(34, 16, true); w(36, "data"); v.setUint32(40, n * 2, true);
|
| 91 |
+
for (let i = 0; i < n; i++) {
|
| 92 |
+
const s = Math.max(-1, Math.min(1, float32[i]));
|
| 93 |
+
v.setInt16(44 + i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true);
|
| 94 |
+
}
|
| 95 |
+
return URL.createObjectURL(new Blob([buf], { type: "audio/wav" }));
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// ---------- UI ----------
|
| 99 |
+
engine.onchange = () => { $("voiceBox").hidden = engine.value === "ai"; };
|
| 100 |
+
|
| 101 |
+
$("play").onclick = async () => {
|
| 102 |
+
const text = $("text").value.trim();
|
| 103 |
+
if (!text) { statusEl.textContent = "Pehle text likho"; return; }
|
| 104 |
+
$("player").hidden = true;
|
| 105 |
+
try {
|
| 106 |
+
if (engine.value === "device") {
|
| 107 |
+
speechSynthesis.cancel();
|
| 108 |
+
const u = new SpeechSynthesisUtterance(text);
|
| 109 |
+
const v = speechSynthesis.getVoices().find(x => x.name === voiceSel.value);
|
| 110 |
+
if (v) { u.voice = v; u.lang = v.lang; } else u.lang = "hi-IN";
|
| 111 |
+
u.rate = parseFloat($("rate").value);
|
| 112 |
+
u.onend = () => statusEl.textContent = "Ho gaya";
|
| 113 |
+
statusEl.textContent = "Bol raha hai...";
|
| 114 |
+
speechSynthesis.speak(u);
|
| 115 |
+
} else {
|
| 116 |
+
const pipe = await loadAI();
|
| 117 |
+
statusEl.textContent = "Awaaz ban rahi hai...";
|
| 118 |
+
const out = await pipe(text);
|
| 119 |
+
const p = $("player");
|
| 120 |
+
p.src = wavUrl(out.audio, out.sampling_rate);
|
| 121 |
+
p.hidden = false;
|
| 122 |
+
p.play();
|
| 123 |
+
statusEl.textContent = "Ho gaya (neeche download bhi kar sakte ho)";
|
| 124 |
+
}
|
| 125 |
+
} catch (e) {
|
| 126 |
+
statusEl.textContent = "Error: " + e.message;
|
| 127 |
+
}
|
| 128 |
+
};
|
| 129 |
+
$("stop").onclick = () => { speechSynthesis.cancel(); $("player").pause(); statusEl.textContent = "Ruk gaya"; };
|
| 130 |
+
</script>
|
| 131 |
+
</body>
|
| 132 |
</html>
|