TurkishCodeMan commited on
Commit
df972d3
·
verified ·
1 Parent(s): 989436d

Upload inference.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. inference.py +170 -0
inference.py ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import argparse
4
+ import torch
5
+ import soundfile as sf
6
+
7
+ CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
8
+ TTS_ROOT = os.path.abspath(os.path.join(CURRENT_DIR, ".."))
9
+ if TTS_ROOT not in sys.path:
10
+ sys.path.insert(0, TTS_ROOT)
11
+
12
+ # HuggingFace Cache ve Proxy ayarları (Ağ bloklanması / takılmasını önlemek için)
13
+ if "HF_HOME" not in os.environ:
14
+ scratch_hf = "/gpfs/scratch/ehpc540/users/huseyin/.cache/huggingface"
15
+ if os.path.exists(scratch_hf):
16
+ os.environ["HF_HOME"] = scratch_hf
17
+
18
+ if "HTTP_PROXY" not in os.environ and "http_proxy" not in os.environ:
19
+ os.environ["HTTP_PROXY"] = "http://127.0.0.1:9595"
20
+ os.environ["HTTPS_PROXY"] = "http://127.0.0.1:9595"
21
+ os.environ["http_proxy"] = "http://127.0.0.1:9595"
22
+ os.environ["https_proxy"] = "http://127.0.0.1:9595"
23
+
24
+ try:
25
+ from model.model import TTSModel, TTSConfig
26
+ from dataset.tokenizer import TextTokenizer, AudioCodecTokenizer, TTSProcessor
27
+ except (ImportError, ModuleNotFoundError):
28
+ from model import TTSModel, TTSConfig
29
+ from tokenizer import TextTokenizer, AudioCodecTokenizer, TTSProcessor
30
+
31
+
32
+ import numpy as np
33
+
34
+ def trim_silence_and_noise(wav: np.ndarray, sr: int = 24000, frame_ms: int = 50, pad_ms: int = 250) -> np.ndarray:
35
+ """
36
+ Konuşma bittikten sonra autoregressive modelin ürettiği boşluk veya cızırtıyı (statik gürültü)
37
+ kısa süreli RMS enerji analiziyle tespit edip yumuşak bir fade-out ile temizler.
38
+ """
39
+ frame_len = int(sr * (frame_ms / 1000.0))
40
+ num_frames = len(wav) // frame_len
41
+ if num_frames == 0:
42
+ return wav
43
+
44
+ frames = [wav[i * frame_len : (i + 1) * frame_len] for i in range(num_frames)]
45
+ energies = [np.sqrt(np.mean(f**2)) for f in frames]
46
+ peak_energy = max(energies) if len(energies) > 0 else 0.0
47
+
48
+ if peak_energy < 0.01:
49
+ return wav
50
+
51
+ # Aktif konuşma eşiği: tepe enerjinin %20'si (en az 0.032)
52
+ thresh = max(0.032, peak_energy * 0.20)
53
+
54
+ last_active_frame = 0
55
+ for i in range(num_frames - 1, -1, -1):
56
+ if energies[i] > thresh:
57
+ last_active_frame = i
58
+ break
59
+
60
+ # Konuşmanın doğal sönümlenmesi için 250ms tampon (pad) bırak
61
+ pad_frames = int(pad_ms / frame_ms)
62
+ cut_frame = min(num_frames, last_active_frame + 1 + pad_frames)
63
+ cut_sample = min(len(wav), cut_frame * frame_len)
64
+
65
+ trimmed = wav[:cut_sample].copy()
66
+ # Tıklama/pop sesini önlemek için son 50ms'ye yumuşak fade-out
67
+ fade_len = min(int(sr * 0.05), len(trimmed))
68
+ if fade_len > 0:
69
+ fade_curve = np.linspace(1.0, 0.0, fade_len)
70
+ trimmed[-fade_len:] *= fade_curve
71
+
72
+ cut_sec = (len(wav) - len(trimmed)) / sr
73
+ if cut_sec > 0.3:
74
+ print(f"[Inference] ✂️ Akıllı Kırpıcı: Orijinal {len(wav)/sr:.2f}s -> Temizlenmiş {len(trimmed)/sr:.2f}s ({cut_sec:.2f} saniye cızırtı/statik kesildi)", flush=True)
75
+
76
+ return trimmed
77
+
78
+
79
+ def main():
80
+ parser = argparse.ArgumentParser(description="TTS Inference and Audio Synthesis Pipeline")
81
+ parser.add_argument("--text", type=str, required=True, help="Seslendirilecek metin")
82
+ parser.add_argument("--instruction", type=str, default=None, help="Ses tasarımı talimatı (Voice Design - Örn: 'Kalın ve tok bir ses')")
83
+ parser.add_argument("--ref_audio", type=str, default=None, help="Klonlanacak referans ses dosyası (Voice Clone - .wav)")
84
+ parser.add_argument("--ref_max_sec", type=float, default=None, help="Referans sesin maksimum süresi (Varsayılan: None - sesin tamamı kullanılır)")
85
+ parser.add_argument("--checkpoint", type=str, default=None, help="Model ağırlık dosyası (.pt)")
86
+ parser.add_argument("--use_qwen_backbone", action="store_true", help="Qwen2.5-0.5B pretrained ağırlıklarını omurgaya yükler")
87
+ parser.add_argument("--output", type=str, default="output.wav", help="Kaydedilecek ses dosyası yolu")
88
+ parser.add_argument("--max_tokens", type=int, default=120, help="Maksimum üretilecek ses karesi (12.5 Hz'de 120 kare ~ 9.6 saniye)")
89
+ parser.add_argument("--temperature", type=float, default=0.6, help="Sampling sıcaklığı (0.6 doğal tonlama ve melodi sağlar)")
90
+ parser.add_argument("--depth_temperature", type=float, default=0.6, help="Akustik katman sıcaklığı (Yumuşak sampling ile robotikliği kırar)")
91
+ parser.add_argument("--top_k", type=int, default=30, help="Top-k sampling parametresi")
92
+ parser.add_argument("--no_trim", action="store_true", help="Cümle sonundaki sessizlik/cızırtı kırpmasını devre dışı bırakır")
93
+ parser.add_argument("--device", type=str, default="cuda" if torch.cuda.is_available() else "cpu")
94
+ args = parser.parse_args()
95
+
96
+ device = torch.device(args.device)
97
+ print(f"[Inference] Cihaz: {device}")
98
+ print(f"[Inference] Metin: '{args.text}'")
99
+ if args.instruction:
100
+ print(f"[Inference] Voice Design Talimatı: '{args.instruction}'")
101
+ if args.ref_audio:
102
+ print(f"[Inference] Voice Clone Referansı: '{args.ref_audio}'")
103
+
104
+ # 1. Model ve Tokenizer Yükleme
105
+ if args.use_qwen_backbone:
106
+ print("[Inference] Qwen2.5-0.5B omurgası ile model konfigüre ediliyor...")
107
+ config = TTSConfig.from_qwen("Qwen/Qwen2.5-0.5B")
108
+ model = TTSModel(config).to(device)
109
+ if not args.checkpoint:
110
+ model.load_qwen_backbone("Qwen/Qwen2.5-0.5B")
111
+ else:
112
+ config = TTSConfig()
113
+ model = TTSModel(config).to(device)
114
+
115
+ if args.checkpoint and os.path.exists(args.checkpoint):
116
+ print(f"[Inference] Checkpoint yükleniyor: {args.checkpoint}", flush=True)
117
+ ckpt = torch.load(args.checkpoint, map_location=device)
118
+ state_dict = ckpt.get("model", ckpt) if isinstance(ckpt, dict) else ckpt
119
+ if any(k.startswith("module.") for k in state_dict.keys()):
120
+ state_dict = {k.replace("module.", ""): v for k, v in state_dict.items()}
121
+ model.load_state_dict(state_dict)
122
+ print("[Inference] Model ağırlıkları başarıyla yüklendi!", flush=True)
123
+ elif not args.use_qwen_backbone:
124
+ print("[Inference] Bilgi: Checkpoint verilmedi, model başlangıç ağırlıklarıyla çalışacak.", flush=True)
125
+
126
+ print("[Inference] 1/4 Metin tokenizer hazırlanıyor...", flush=True)
127
+ text_tokenizer = TextTokenizer()
128
+ print("[Inference] 2/4 Kyutai Mimi ses çözücü (Audio Codec) yükleniyor...", flush=True)
129
+ audio_tokenizer = AudioCodecTokenizer(device=args.device)
130
+ processor = TTSProcessor(text_tokenizer, audio_tokenizer)
131
+
132
+ # 2. Girdileri Hazırla
133
+ print("[Inference] 3/4 Girdiler hazırlanıyor...", flush=True)
134
+ text_ids, ref_codes = processor.prepare_inference_inputs(
135
+ text=args.text,
136
+ instruction=args.instruction,
137
+ ref_audio_path=args.ref_audio,
138
+ max_ref_sec=args.ref_max_sec,
139
+ device=args.device,
140
+ )
141
+
142
+ # 3. Autoregressive Üretim (CB0 + CB1..7)
143
+ print(f"[Inference] 4/4 Ses tokenları üretiliyor (Maksimum {args.max_tokens} kare)...", flush=True)
144
+ generated_codes = model.generate(
145
+ text_ids=text_ids,
146
+ ref_audio_codes=ref_codes,
147
+ max_new_tokens=args.max_tokens,
148
+ temperature=args.temperature,
149
+ depth_temperature=args.depth_temperature,
150
+ top_k=args.top_k,
151
+ )
152
+ print(f"[Inference] Üretilen ses token matrisi boyutu: {generated_codes.shape}", flush=True)
153
+
154
+ # 4. Codec ile Sese Dönüştürme
155
+ print("[Inference] Ses dalgasına (waveform / 24 kHz) dönüştürülüyor...", flush=True)
156
+ wav = audio_tokenizer.decode(generated_codes)
157
+ if isinstance(wav, torch.Tensor):
158
+ wav = wav.squeeze().cpu().numpy()
159
+
160
+ # 5. Otomatik Cızırtı ve Sessizlik Kırpma (VAD / Energy Trimmer)
161
+ if not args.no_trim:
162
+ wav = trim_silence_and_noise(wav, sr=audio_tokenizer.sample_rate)
163
+
164
+ # 6. Dosyaya Yazma
165
+ sf.write(args.output, wav, samplerate=audio_tokenizer.sample_rate)
166
+ print(f"[Inference] 🎉 Başarılı! Temiz ve optimize edilmiş ses kaydedildi -> {args.output}", flush=True)
167
+
168
+
169
+ if __name__ == "__main__":
170
+ main()