| <!DOCTYPE html> |
| <html lang="fa" dir="rtl"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>شبیهساز اختصاصی صدا - OmniVoice Clone API</title> |
| |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;700;900&display=swap" rel="stylesheet"> |
| <style> |
| body { |
| font-family: 'Vazirmatn', sans-serif; |
| } |
| </style> |
| </head> |
| <body class="bg-slate-900 text-slate-100 min-h-screen py-10 px-4"> |
|
|
| <div class="max-w-2xl mx-auto bg-slate-800 rounded-2xl shadow-2xl border border-slate-700 overflow-hidden"> |
| |
| <div class="bg-gradient-to-r from-teal-600 to-emerald-600 p-6 text-white text-center"> |
| <h1 class="text-2xl font-black">پنل شبیهسازی صدا (OmniVoice Clone)</h1> |
| <p class="text-xs opacity-90 mt-1">تولید گفتار با کپیبرداری دقیق از فایل صوتی مرجع شما</p> |
| </div> |
|
|
| <div class="p-6 space-y-6"> |
| |
| <div> |
| <label class="block text-sm font-bold text-slate-300 mb-2">متن مورد نظر برای تبدیل به گفتار:</label> |
| <textarea id="inputText" rows="4" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-3 text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-teal-500" placeholder="متنی را که میخواهید با صدای شبیهسازیشده خوانده شود، بنویسید..."></textarea> |
| </div> |
|
|
| |
| <div class="bg-slate-900/40 p-4 rounded-xl border border-slate-700 space-y-4"> |
| <div> |
| <label class="block text-sm font-bold text-slate-300 mb-2">فایل صوتی مرجع (۳ تا ۱۰ ثانیه):</label> |
| <input type="file" id="refAudio" accept="audio/*" class="w-full text-sm text-slate-400 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-xs file:font-semibold file:bg-teal-600 file:text-white hover:file:bg-teal-500 cursor-pointer"> |
| <p class="text-[10px] text-slate-500 mt-1">یک فایل صوتی کوتاه و باکیفیت از گوینده مورد نظر آپلود کنید (فرمت mp3، wav و غیره).</p> |
| </div> |
| <div> |
| <label class="block text-xs font-bold text-slate-400 mb-1">متن فایل صوتی مرجع (اختیاری):</label> |
| <input type="text" id="refText" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-2 text-sm text-white focus:outline-none focus:ring-1 focus:ring-teal-500" placeholder="جملهای که گوینده در فایل مرجع بالا میگوید..."> |
| </div> |
| </div> |
|
|
| |
| <div> |
| <label class="block text-sm font-bold text-slate-300 mb-2">زبان خروجی:</label> |
| <select id="language" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-2.5 text-white text-sm focus:ring-teal-500 focus:border-teal-500"> |
| <option value="Auto">تشخیص خودکار (Auto)</option> |
| <option value="English">انگلیسی (English)</option> |
| <option value="Persian">فارسی (Persian)</option> |
| <option value="Spanish">اسپانیایی (Spanish)</option> |
| <option value="French">فرانسوی (French)</option> |
| <option value="Chinese">چینی (Chinese)</option> |
| </select> |
| </div> |
|
|
| |
| <div class="bg-slate-950/20 p-3 rounded-lg border border-slate-700/50"> |
| <label class="block text-xs font-bold text-slate-400 mb-1">توکن Hugging Face (اختیاری):</label> |
| <input type="password" id="hfToken" class="w-full bg-slate-900 border border-slate-700 rounded-lg p-2 text-xs text-white focus:outline-none focus:ring-1 focus:ring-teal-500" placeholder="Bearer hf_..."> |
| <p class="text-[10px] text-slate-500 mt-1">در صورت شلوغ بودن صف عمومی یا مسدود شدن درخواستها، از توکن شخصی خود استفاده کنید.</p> |
| </div> |
|
|
| |
| <button id="btnGenerate" onclick="generateClone()" class="w-full bg-gradient-to-r from-teal-500 to-emerald-500 hover:from-teal-600 hover:to-emerald-600 text-white font-bold py-3.5 px-6 rounded-xl transition duration-300 shadow-lg flex items-center justify-center"> |
| <span id="btnText">تولید صدای شبیهسازیشده (Synthesize)</span> |
| </button> |
|
|
| |
| <div id="statusBox" class="hidden bg-slate-950/50 p-4 rounded-xl border border-slate-800 text-xs"> |
| <div class="flex items-center space-x-3 space-x-reverse"> |
| |
| <div class="w-4 h-4 border-2 border-teal-500 border-t-transparent rounded-full animate-spin"></div> |
| <span id="statusMessage" class="text-slate-300">در حال بررسی ارتباط...</span> |
| </div> |
| </div> |
|
|
| |
| <div id="resultContainer" class="hidden bg-slate-900/90 p-5 rounded-xl border border-teal-500/40 space-y-4"> |
| <h3 class="text-sm font-bold text-teal-400">فایل صوتی شبیهسازیشده با موفقیت آماده شد:</h3> |
| <audio id="audioPlayer" controls class="w-full"></audio> |
| <a id="btnDownload" download="cloned_speech.wav" class="block text-center bg-teal-600 hover:bg-teal-500 text-white text-xs font-bold py-2.5 px-4 rounded-lg transition shadow"> |
| دانلود فایل صوتی خروجی (WAV) |
| </a> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <script type="module"> |
| import { Client, handle_file } from "https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"; |
| |
| window.generateClone = async function() { |
| const btn = document.getElementById("btnGenerate"); |
| const btnText = document.getElementById("btnText"); |
| const statusBox = document.getElementById("statusBox"); |
| const statusMessage = document.getElementById("statusMessage"); |
| const resultContainer = document.getElementById("resultContainer"); |
| const audioPlayer = document.getElementById("audioPlayer"); |
| const btnDownload = document.getElementById("btnDownload"); |
| |
| const text = document.getElementById("inputText").value.trim(); |
| const refAudioFile = document.getElementById("refAudio").files[0]; |
| const refTextVal = document.getElementById("refText").value.trim() || null; |
| const language = document.getElementById("language").value; |
| const hfToken = document.getElementById("hfToken").value.trim(); |
| |
| |
| if (!text) { |
| alert("لطفاً ابتدا متنی را برای خوانده شدن وارد کنید."); |
| return; |
| } |
| if (!refAudioFile) { |
| alert("لطفاً فایل صوتی مرجع را آپلود کنید تا صدا شبیهسازی شود."); |
| return; |
| } |
| |
| |
| btn.disabled = true; |
| btnText.innerText = "منتظر بمانید..."; |
| statusBox.classList.remove("hidden"); |
| resultContainer.classList.add("hidden"); |
| statusMessage.innerText = "در حال ایجاد اتصال به موتور هوش مصنوعی OmniVoice..."; |
| |
| try { |
| |
| const app = await Client.connect("k2-fsa/OmniVoice", { |
| token: hfToken ? `Bearer ${hfToken}` : undefined |
| }); |
| |
| statusMessage.innerText = "اتصال برقرار شد. در حال آپلود فایل صوتی مرجع..."; |
| |
| |
| const cloneDep = app.config.dependencies.find(dep => dep.inputs && dep.inputs.length === 12); |
| if (!cloneDep) { |
| throw new Error("تنظیمات مربوط به مدل شبیهسازی روی سرور یافت نشد."); |
| } |
| const endpoint = cloneDep.api_name ? `/${cloneDep.api_name}` : cloneDep.id; |
| |
| |
| const refAudioPayload = handle_file(refAudioFile); |
| |
| statusMessage.innerText = "درخواست شما با موفقیت در صف سرور قرار گرفت. پردازش روی کارت گرافیک (ZeroGPU) در حال انجام است. لطفاً پنجره را نبندید..."; |
| |
| |
| const result = await app.predict(endpoint, [ |
| text, |
| language, |
| refAudioPayload, |
| refTextVal, |
| "", |
| 32, |
| 2.0, |
| true, |
| 1.0, |
| 0, |
| true, |
| true |
| ]); |
| |
| |
| if (result && result.data && result.data[0]) { |
| const audioUrl = result.data[0].url; |
| audioPlayer.src = audioUrl; |
| btnDownload.href = audioUrl; |
| |
| statusMessage.innerText = "پردازش نهایی شد و فایل صدا تولید گردید."; |
| resultContainer.classList.remove("hidden"); |
| } else { |
| throw new Error("خروجی معتبری از هوش مصنوعی دریافت نشد."); |
| } |
| |
| } catch (error) { |
| console.error(error); |
| statusMessage.innerText = `خطا در فرآیند: ${error.message || error}`; |
| } finally { |
| btn.disabled = false; |
| btnText.innerText = "تولید صدای شبیهسازیشده (Synthesize)"; |
| } |
| } |
| </script> |
| </body> |
| </html> |