mitvho09 commited on
Commit
f4e2ae5
·
verified ·
1 Parent(s): 22e8aeb

Upload indic_tts.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. indic_tts.py +120 -0
indic_tts.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Kannada narration via AI4Bharat IndicF5 — local GPU for HF Spaces.
2
+
3
+ Uses the fine-tuned checkpoint from mitvho09/IndicF5-Kannada-Bedtime-v2
4
+ (best Kannada quality: MOS 4.2, speaking rate 3.0 syll/s).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import re
11
+ import tempfile
12
+
13
+ import numpy as np
14
+ import torch
15
+
16
+ from config import KANNADA_TTS_MODEL, KANNADA_FINETUNE
17
+ _BASE_HUB_ID = KANNADA_TTS_MODEL.hub_id
18
+ _CHECKPOINT = KANNADA_FINETUNE
19
+ INDICF5_SR = 24_000
20
+
21
+ _model = None
22
+
23
+
24
+ def _get_model():
25
+ global _model
26
+ if _model is None:
27
+ from transformers import AutoModel
28
+ token = os.environ.get("HF_TOKEN") or None
29
+
30
+ _model = AutoModel.from_pretrained(
31
+ _BASE_HUB_ID, trust_remote_code=True, token=token)
32
+
33
+ # Load fine-tuned Kannada checkpoint from HuggingFace Hub
34
+ try:
35
+ from huggingface_hub import hf_hub_download
36
+ cfm_path = hf_hub_download(
37
+ repo_id=_CHECKPOINT,
38
+ filename="cfm.pt",
39
+ token=token,
40
+ )
41
+ cfm_state = torch.load(cfm_path, map_location="cpu", weights_only=True)
42
+ _model.ema_model.load_state_dict(cfm_state)
43
+ print(f"✓ Loaded fine-tuned CFM from {_CHECKPOINT}")
44
+ except Exception as e:
45
+ print(f"⚠ Could not load fine-tuned checkpoint: {e}")
46
+
47
+ _model = _model.to("cuda")
48
+ _model.eval()
49
+ return _model
50
+
51
+
52
+ # Load at module level for ZeroGPU (CUDA emulation outside @spaces.GPU)
53
+ try:
54
+ _get_model()
55
+ except Exception:
56
+ pass
57
+
58
+
59
+ def _split_sentences(text: str, max_chars: int = 200):
60
+ parts = re.split(r"(?<=[.!?।])\s+|\n+", text.strip())
61
+ out = []
62
+ for p in parts:
63
+ p = p.strip()
64
+ if not p:
65
+ continue
66
+ while len(p) > max_chars:
67
+ cut = p.rfind(" ", 0, max_chars)
68
+ cut = cut if cut > 0 else max_chars
69
+ out.append(p[:cut].strip())
70
+ p = p[cut:].strip()
71
+ out.append(p)
72
+ return out or [text.strip()]
73
+
74
+
75
+ def _pause_for(mood: str, energy: float = 0.45) -> float:
76
+ energy = max(0.0, min(1.0, float(energy)))
77
+ base = 0.45 if mood in ("funny", "magical") else 0.65
78
+ return round(base + (0.85 - base) * (1.0 - energy), 3)
79
+
80
+
81
+ def _postprocess_np(audio, sr):
82
+ from audio_postprocess import postprocess
83
+ return postprocess(audio, sr)
84
+
85
+
86
+ def narrate_kannada(ref_wav: str, ref_text: str, kannada_text: str, mood: str = "", energy: float = 0.45) -> str:
87
+ """Clone the parent's voice and narrate Kannada text. Returns a temp WAV path."""
88
+ if not ref_wav or not os.path.exists(ref_wav):
89
+ raise ValueError("Please provide a prepared voice reference WAV.")
90
+ if not (ref_text or "").strip():
91
+ raise ValueError("Reference transcript (ref_text) is required for Kannada cloning.")
92
+ if not (kannada_text or "").strip():
93
+ raise ValueError("Please provide Kannada text to narrate.")
94
+
95
+ model = _get_model()
96
+
97
+ pause = _pause_for(mood, energy) * 1.3
98
+ silence = np.zeros(int(pause * INDICF5_SR), dtype=np.float32)
99
+
100
+ chunks = []
101
+ for sentence in _split_sentences(kannada_text, max_chars=200):
102
+ audio = model(sentence, ref_audio_path=ref_wav, ref_text=ref_text.strip())
103
+ audio = np.asarray(audio, dtype=np.float32)
104
+ if audio.size and float(np.max(np.abs(audio))) > 1.0:
105
+ audio = audio / 32768.0
106
+ if audio.size:
107
+ chunks.append(audio)
108
+ chunks.append(silence)
109
+
110
+ if not chunks:
111
+ raise RuntimeError("IndicF5 produced no audio.")
112
+
113
+ full = np.concatenate(chunks)
114
+ full = _postprocess_np(full, INDICF5_SR)
115
+
116
+ import soundfile as sf
117
+ fd, out_path = tempfile.mkstemp(prefix="dreamvoice_kn_", suffix=".wav")
118
+ os.close(fd)
119
+ sf.write(out_path, full, INDICF5_SR)
120
+ return out_path