Update app.py
Browse files
app.py
CHANGED
|
@@ -1,35 +1,47 @@
|
|
| 1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
import gradio as gr
|
| 4 |
-
import spaces
|
| 5 |
import torch
|
| 6 |
from transformers import AutoModel
|
| 7 |
|
| 8 |
REPO = "Tharshan/indicf5_hindi-english_code_switch"
|
|
|
|
| 9 |
|
| 10 |
print("loading model ...")
|
| 11 |
model = AutoModel.from_pretrained(REPO, trust_remote_code=True)
|
| 12 |
model.eval()
|
|
|
|
| 13 |
|
| 14 |
VOICES = model.voices()
|
| 15 |
CHOICES = [(v.get("name", k), k) for k, v in VOICES.items()]
|
| 16 |
DEFAULT = list(VOICES)[0]
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
| 30 |
]
|
| 31 |
-
|
| 32 |
-
|
| 33 |
|
| 34 |
|
| 35 |
def synth(text, voice_key, nfe, speed):
|
|
@@ -37,69 +49,69 @@ def synth(text, voice_key, nfe, speed):
|
|
| 37 |
if not text:
|
| 38 |
raise gr.Error("Enter some text first.")
|
| 39 |
if len(text) > MAX_CHARS:
|
| 40 |
-
raise gr.Error(f"Keep it under {MAX_CHARS} characters
|
| 41 |
-
"
|
| 42 |
-
|
|
|
|
| 43 |
|
| 44 |
ref_audio, ref_text = model.voice(voice_key)
|
| 45 |
-
model.to("cuda" if torch.cuda.is_available() else "cpu")
|
| 46 |
-
|
| 47 |
audio, sr = model.generate(text, ref_audio=ref_audio, ref_text=ref_text,
|
| 48 |
nfe_step=int(nfe), speed=float(speed))
|
| 49 |
return sr, audio
|
| 50 |
|
| 51 |
|
| 52 |
with gr.Blocks(title="Indic code-switched TTS") as demo:
|
| 53 |
-
gr.Markdown("""
|
| 54 |
# Indic code-switched TTS
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
| 58 |
[ai4bharat/IndicF5](https://huggingface.co/ai4bharat/IndicF5) on Hindi-English
|
| 59 |
-
code-switched speech.
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
| 63 |
""")
|
| 64 |
|
| 65 |
with gr.Row():
|
| 66 |
with gr.Column(scale=3):
|
| 67 |
text = gr.Textbox(
|
| 68 |
-
label="Text",
|
| 69 |
-
|
| 70 |
-
placeholder="Write an Indic sentence with English words mixed in…",
|
| 71 |
-
)
|
| 72 |
voice = gr.Dropdown(choices=CHOICES, value=DEFAULT,
|
| 73 |
label="Language / voice")
|
| 74 |
with gr.Accordion("Advanced", open=False):
|
| 75 |
nfe = gr.Slider(8, 32, value=16, step=4,
|
| 76 |
-
label="Denoising steps
|
|
|
|
| 77 |
speed = gr.Slider(0.7, 1.3, value=1.0, step=0.05, label="Speed")
|
| 78 |
btn = gr.Button("Generate", variant="primary")
|
| 79 |
|
| 80 |
with gr.Column(scale=2):
|
| 81 |
out = gr.Audio(label="Output", show_download_button=True)
|
| 82 |
-
gr.Markdown(
|
| 83 |
-
**Tip:** match the voice to your text's language. The reference
|
| 84 |
-
|
| 85 |
-
than any other setting here.
|
| 86 |
-
""")
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
|
|
|
| 90 |
|
| 91 |
gr.Markdown(f"""
|
| 92 |
---
|
| 93 |
-
**Model:** [{REPO}](https://huggingface.co/{REPO}) ·
|
| 94 |
-
|
| 95 |
-
|
| 96 |
|
| 97 |
-
**Limitations.** Single-pass generation
|
| 98 |
-
|
| 99 |
-
upsampled to 24 kHz, so timbre is duller than the base model.
|
| 100 |
""")
|
| 101 |
|
| 102 |
btn.click(synth, inputs=[text, voice, nfe, speed], outputs=out)
|
| 103 |
text.submit(synth, inputs=[text, voice, nfe, speed], outputs=out)
|
| 104 |
|
| 105 |
-
demo.queue(max_size=
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Gradio demo for Tharshan/indicf5_hindi-english_code_switch.
|
| 3 |
+
|
| 4 |
+
Tuned for HF Spaces CPU basic (2 vCPU / 16 GB):
|
| 5 |
+
- no `spaces` / ZeroGPU decorator (nothing to allocate on CPU)
|
| 6 |
+
- torch pinned to 2 threads (container over-reports core count)
|
| 7 |
+
- default 16 denoising steps instead of 32 (~halves inference time)
|
| 8 |
+
- 150-char cap, validated before any model work
|
| 9 |
+
- requests queued so each gets the full CPU instead of contending
|
| 10 |
+
"""
|
| 11 |
|
| 12 |
import gradio as gr
|
|
|
|
| 13 |
import torch
|
| 14 |
from transformers import AutoModel
|
| 15 |
|
| 16 |
REPO = "Tharshan/indicf5_hindi-english_code_switch"
|
| 17 |
+
MAX_CHARS = 150
|
| 18 |
|
| 19 |
print("loading model ...")
|
| 20 |
model = AutoModel.from_pretrained(REPO, trust_remote_code=True)
|
| 21 |
model.eval()
|
| 22 |
+
torch.set_num_threads(2)
|
| 23 |
|
| 24 |
VOICES = model.voices()
|
| 25 |
CHOICES = [(v.get("name", k), k) for k, v in VOICES.items()]
|
| 26 |
DEFAULT = list(VOICES)[0]
|
| 27 |
+
print(f"ready — voices: {list(VOICES)}")
|
| 28 |
+
|
| 29 |
+
# Same sentence across languages, so switching the dropdown is directly
|
| 30 |
+
# comparable. Keys must exist in voices.json or the buttons raise KeyError.
|
| 31 |
+
_CANDIDATES = [
|
| 32 |
+
("ritu_hinglish", "मैं आज office जा रहा हूँ, morning में meeting है।"),
|
| 33 |
+
("ta_hinglish", "நான் இன்னைக்கு office போறேன், morning ல meeting இருக்கு."),
|
| 34 |
+
("bn", "আমি আজ office যাচ্ছি, morning এ একটা meeting আছে।"),
|
| 35 |
+
("te", "నేను ఈరోజు office కి వెళ్తున్నాను, morning లో meeting ఉంది."),
|
| 36 |
+
("kn", "ನಾನು ಇವತ್ತು office ಗೆ ಹೋಗುತ್ತಿದ್ದೇನೆ, morning ನಲ್ಲಿ meeting ಇದೆ."),
|
| 37 |
+
("ml", "ഞാൻ ഇന്ന് office ലേക്ക് പോകുന്നു, morning ൽ ഒരു meeting ഉണ്ട്."),
|
| 38 |
+
("mr", "मी आज office ला जातोय, morning मध्ये एक meeting आहे."),
|
| 39 |
+
("gu", "હું આજે office જઈ રહ્યો છું, morning માં એક meeting છે."),
|
| 40 |
+
("pa", "ਮੈਂ ਅੱਜ office ਜਾ ਰਿਹਾ ਹਾਂ, morning ਵਿੱਚ ਇੱਕ meeting ਹੈ।"),
|
| 41 |
+
("or", "ମୁଁ ଆଜି office ଯାଉଛି, morning ରେ ଗୋଟିଏ meeting ଅଛି।"),
|
| 42 |
]
|
| 43 |
+
# drop any example whose voice isn't actually bundled
|
| 44 |
+
EXAMPLES = [[text, key] for key, text in _CANDIDATES if key in VOICES]
|
| 45 |
|
| 46 |
|
| 47 |
def synth(text, voice_key, nfe, speed):
|
|
|
|
| 49 |
if not text:
|
| 50 |
raise gr.Error("Enter some text first.")
|
| 51 |
if len(text) > MAX_CHARS:
|
| 52 |
+
raise gr.Error(f"Keep it under {MAX_CHARS} characters — this runs on "
|
| 53 |
+
f"free CPU hardware. You entered {len(text)}.")
|
| 54 |
+
if voice_key not in VOICES:
|
| 55 |
+
raise gr.Error("Pick a voice from the dropdown.")
|
| 56 |
|
| 57 |
ref_audio, ref_text = model.voice(voice_key)
|
|
|
|
|
|
|
| 58 |
audio, sr = model.generate(text, ref_audio=ref_audio, ref_text=ref_text,
|
| 59 |
nfe_step=int(nfe), speed=float(speed))
|
| 60 |
return sr, audio
|
| 61 |
|
| 62 |
|
| 63 |
with gr.Blocks(title="Indic code-switched TTS") as demo:
|
| 64 |
+
gr.Markdown(f"""
|
| 65 |
# Indic code-switched TTS
|
| 66 |
|
| 67 |
+
Because nobody says "कार्यालय" when they mean office.
|
| 68 |
+
|
| 69 |
+
Most Indic text-to-speech mangles English words embedded in an Indic sentence.
|
| 70 |
+
This one doesn't. Fine-tuned from
|
| 71 |
[ai4bharat/IndicF5](https://huggingface.co/ai4bharat/IndicF5) on Hindi-English
|
| 72 |
+
code-switched speech — the English gain carries across to the other scripts too.
|
| 73 |
|
| 74 |
+
> ⏳ Running on free CPU hardware: **30–60 seconds per sentence**, and the first
|
| 75 |
+
> request after idle is slower while the model wakes up. Keep it short.
|
| 76 |
""")
|
| 77 |
|
| 78 |
with gr.Row():
|
| 79 |
with gr.Column(scale=3):
|
| 80 |
text = gr.Textbox(
|
| 81 |
+
label="Text", lines=2,
|
| 82 |
+
placeholder="An Indic sentence with English words mixed in…")
|
|
|
|
|
|
|
| 83 |
voice = gr.Dropdown(choices=CHOICES, value=DEFAULT,
|
| 84 |
label="Language / voice")
|
| 85 |
with gr.Accordion("Advanced", open=False):
|
| 86 |
nfe = gr.Slider(8, 32, value=16, step=4,
|
| 87 |
+
label="Denoising steps — lower is faster, "
|
| 88 |
+
"quality difference is small")
|
| 89 |
speed = gr.Slider(0.7, 1.3, value=1.0, step=0.05, label="Speed")
|
| 90 |
btn = gr.Button("Generate", variant="primary")
|
| 91 |
|
| 92 |
with gr.Column(scale=2):
|
| 93 |
out = gr.Audio(label="Output", show_download_button=True)
|
| 94 |
+
gr.Markdown(
|
| 95 |
+
"**Tip:** match the voice to your text's language. The reference "
|
| 96 |
+
"voice sets accent and prosody, and a mismatched one degrades "
|
| 97 |
+
"output more than any other setting here.")
|
|
|
|
| 98 |
|
| 99 |
+
if EXAMPLES:
|
| 100 |
+
gr.Examples(examples=EXAMPLES, inputs=[text, voice],
|
| 101 |
+
label="The same sentence across languages")
|
| 102 |
|
| 103 |
gr.Markdown(f"""
|
| 104 |
---
|
| 105 |
+
**Model:** [{REPO}](https://huggingface.co/{REPO}) · fine-tuned from
|
| 106 |
+
[ai4bharat/IndicF5](https://huggingface.co/ai4bharat/IndicF5) (MIT) on
|
| 107 |
+
OpenSLR-104 Hindi-English.
|
| 108 |
|
| 109 |
+
**Limitations.** Single-pass generation with no text chunking — one or two
|
| 110 |
+
sentences at a time. Heavy code-switching can drop words. Training audio came
|
| 111 |
+
from an ASR corpus upsampled to 24 kHz, so timbre is duller than the base model.
|
| 112 |
""")
|
| 113 |
|
| 114 |
btn.click(synth, inputs=[text, voice, nfe, speed], outputs=out)
|
| 115 |
text.submit(synth, inputs=[text, voice, nfe, speed], outputs=out)
|
| 116 |
|
| 117 |
+
demo.queue(max_size=10).launch()
|