sush0401 commited on
Commit
857c30c
·
verified ·
1 Parent(s): 7848482

DreamVoice: ZeroGPU app

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