diff --git a/gpt_sovits_api.py b/gpt_sovits_api.py new file mode 100644 index 0000000..9d4516f --- /dev/null +++ b/gpt_sovits_api.py @@ -0,0 +1,250 @@ +''' +GPT-SoVITS API - Direct call interface +''' +import os +import sys +import random +import torch +import numpy as np + +now_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.append(now_dir) +sys.path.append(os.path.dirname(now_dir)) + +from TTS_infer_pack.TTS import TTS, TTS_Config + +class GPTSoVITSAPI: + def __init__(self, version="v2ProPlus", device=None, is_half=None): + """ + Initialize GPT-SoVITS API (same as WebUI) + + Args: + version: Model version, options: v1, v2, v3, v4, v2Pro, v2ProPlus + device: Running device, e.g. "cuda" or "cpu" + is_half: Whether to use half precision + """ + self.version = version + + # Set device (same as WebUI) + if device is not None: + self.device = torch.device(device) + else: + self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") + + # Set half precision (same as WebUI) + if is_half is not None: + self.is_half = is_half + else: + self.is_half = torch.cuda.is_available() + + # Initialize TTS exactly like WebUI + self._init_tts() + + print(f"GPT-SoVITS API initialized") + # print(f" Version: {self.version}") + # print(f" Device: {self.device}") + # print(f" Half precision: {self.is_half}") + # print(f" GPT model: {self.tts.configs.t2s_weights_path}") + # print(f" SoVITS model: {self.tts.configs.vits_weights_path}") + + def _init_tts(self): + """Initialize TTS pipeline exactly like WebUI""" + # Use config file (same as WebUI) + self.tts_config = TTS_Config(os.path.join(now_dir, "configs/tts_infer.yaml")) + self.tts_config.device = self.device + self.tts_config.is_half = self.is_half + self.tts_config.update_version(self.version) + + # Create TTS instance + self.tts = TTS(self.tts_config) + + def generate( + self, + ref_wav_path, + prompt_text, + text, + prompt_language="all_zh", + text_language="all_zh", + top_k=15, + top_p=1, + temperature=1, + text_split_method="cut1", + batch_size=20, + speed_factor=1.0, + split_bucket=True, + seed=-1, + keep_random=True, + parallel_infer=True, + repetition_penalty=1.35, + sample_steps=32, + super_sampling=False, + output_path=None, + **kwargs + ): + """ + Generate speech from text (same parameters as WebUI) + + Args: + ref_wav_path: Path to reference audio file + prompt_text: Reference text (should match reference audio) + text: Target text to synthesize + prompt_language: Language of prompt text, options: all_zh, zh, ja, en + text_language: Language of target text, options: all_zh, zh, ja, en + top_k: Top-K sampling parameter + top_p: Top-P sampling parameter + temperature: Temperature for sampling + text_split_method: Text split method, options: cut1, cut2, cut3 + batch_size: Batch size for inference + speed_factor: Speed factor (1.0 = normal) + split_bucket: Whether to use bucket splitting + seed: Random seed (-1 = random, or specific seed value) + keep_random: Whether to keep random (True) or use fixed seed (False) + parallel_infer: Whether to use parallel inference + repetition_penalty: Repetition penalty + sample_steps: Number of sampling steps + super_sampling: Whether to use super sampling + output_path: Optional output file path to save audio (e.g., "output.wav") + + Returns: + (sample_rate, audio_array) tuple, or (None, None) on failure + """ + # Handle seed (same logic as WebUI) + seed = -1 if keep_random else seed + actual_seed = seed if seed not in [-1, "", None] else random.randint(0, 2**32 - 1) + + # Build inputs exactly like WebUI + inputs = { + "text": text, + "text_lang": text_language, + "ref_audio_path": ref_wav_path, + "aux_ref_audio_paths": [], + "prompt_text": prompt_text, + "prompt_lang": prompt_language, + "top_k": top_k, + "top_p": top_p, + "temperature": temperature, + "text_split_method": text_split_method, + "batch_size": batch_size, + "speed_factor": float(speed_factor), + "split_bucket": split_bucket, + "return_fragment": False, + "fragment_interval": 0.3, + "seed": actual_seed, + "parallel_infer": parallel_infer, + "repetition_penalty": repetition_penalty, + "sample_steps": sample_steps, + "super_sampling": super_sampling, + } + + # # Print parameters for debugging + # print("=" * 60) + # print("【API 推理参数】") + # print(f" version: {self.version}") + # print(f" text: {text[:50]}..." if len(text) > 50 else f" text: {text}") + # print(f" text_lang: {text_language}") + # print(f" ref_audio_path: {ref_wav_path}") + # print(f" prompt_text: {prompt_text[:50]}..." if len(prompt_text) > 50 else f" prompt_text: {prompt_text}") + # print(f" prompt_lang: {prompt_language}") + # print(f" top_k: {top_k}") + # print(f" top_p: {top_p}") + # print(f" temperature: {temperature}") + # print(f" text_split_method: {text_split_method}") + # print(f" batch_size: {batch_size}") + # print(f" speed_factor: {speed_factor}") + # print(f" split_bucket: {split_bucket}") + # print(f" seed: {actual_seed}") + # print(f" keep_random: {keep_random}") + # print(f" parallel_infer: {parallel_infer}") + # print(f" repetition_penalty: {repetition_penalty}") + # print(f" sample_steps: {sample_steps}") + # print(f" super_sampling: {super_sampling}") + # print("=" * 60) + + # Run inference (same as WebUI) + result = None + for item in self.tts.run(inputs): + result = item + + if result is not None: + # TTS.run() returns (sr, audio_array) tuple + if isinstance(result, tuple) and len(result) >= 2: + sr = result[0] + audio_data = result[1] + + if isinstance(audio_data, np.ndarray): + # Save audio if output_path is provided + if output_path is not None: + try: + import soundfile as sf + sf.write(output_path, audio_data, sr) + print(f"Audio saved to: {output_path}") + except Exception as e: + print(f"Error saving audio: {e}") + + return sr, audio_data + else: + print(f"Warning: audio_data is {type(audio_data)}, not numpy array") + return None, None + else: + print(f"Warning: result is {type(result)}, not a tuple") + return None, None + + return None, None + + def save_audio(self, audio_array, sample_rate, output_path): + """ + Save audio array to file + + Args: + audio_array: numpy array of audio data + sample_rate: Sample rate + output_path: Output file path + + Returns: + output_path if successful, None otherwise + """ + try: + import soundfile as sf + sf.write(output_path, audio_array, sample_rate) + print(f"Audio saved to: {output_path}") + return output_path + except Exception as e: + print(f"Error saving audio: {e}") + return None + + +# Command line interface +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="GPT-SoVITS API") + parser.add_argument("--ref_wav", required=True, help="Path to reference audio") + parser.add_argument("--prompt_text", required=True, help="Reference text") + parser.add_argument("--text", required=True, help="Target text to synthesize") + parser.add_argument("--prompt_lang", default="all_zh", help="Prompt language") + parser.add_argument("--text_lang", default="all_zh", help="Text language") + parser.add_argument("--output", default="output.wav", help="Output file path") + parser.add_argument("--version", default="v2ProPlus", help="Model version") + parser.add_argument("--seed", type=int, default=819407889, help="Random seed") + + args = parser.parse_args() + + # Initialize API + api = GPTSoVITSAPI(version=args.version) + + # Generate audio + sr, audio = api.generate( + ref_wav_path=args.ref_wav, + prompt_text=args.prompt_text, + text=args.text, + prompt_language=args.prompt_lang, + text_language=args.text_lang, + seed=args.seed + ) + + # Save output + if sr is not None and audio is not None: + api.save_audio(audio, sr, args.output) + print(f"\n✓ Successfully generated audio: {args.output}") + else: + print("\n✗ Failed to generate audio") \ No newline at end of file diff --git a/sovits.py b/sovits.py new file mode 100644 index 0000000..c4205ff --- /dev/null +++ b/sovits.py @@ -0,0 +1,19 @@ +import os +import sys +import random +# Change to project directory這個是針對C#的環境 +os.chdir(r"F:\GPT-SoVITS\GPT-SoVITS-v2pro-20250604") +sys.path.insert(0, os.getcwd()) + +now_dir = os.getcwd() +sys.path.append(now_dir) +sys.path.append("%s/GPT_SoVITS" % (now_dir)) + +print(f"Current directory: {now_dir}") +print(f"Python path: {sys.executable}") + +from gpt_sovits_api import GPTSoVITSAPI + +print("\nInitializing GPT-SoVITS API...") +api = GPTSoVITSAPI(version="v2ProPlus") +