| """ |
| Thorsten-Voice/Kokoro - German TTS inference example. |
| |
| Downloads model, config and voicepack from the Hugging Face Hub |
| (no local files required) and synthesizes German speech in Thorsten's voice. |
| |
| Usage: |
| python inference.py "Hallo, hier spricht Thorsten." output.wav |
| python inference.py "Hallo, hier spricht Thorsten." output.wav ep10 |
| python inference.py "Hallo, hier spricht Thorsten." output.wav ep3 |
| """ |
| import sys |
|
|
| import numpy as np |
| import soundfile as sf |
| import torch |
| from huggingface_hub import hf_hub_download |
|
|
| REPO_ID = "Thorsten-Voice/Kokoro" |
| BASE_REPO_ID = "hexgrad/Kokoro-82M" |
| SAMPLE_RATE = 24000 |
|
|
| |
| |
| |
| |
| |
| CHECKPOINTS = {f"ep{n}": {"model": f"model_ep{n}.pth", "voice": f"voices/thorsten_ep{n}.pt"} for n in range(1, 11)} |
| CHECKPOINTS["ep5"] = {"model": "model.pth", "voice": "voices/thorsten.pt"} |
| CHECKPOINTS["default"] = CHECKPOINTS["ep5"] |
|
|
|
|
| def load_pipeline(variant: str = "default", device: str | None = None): |
| from kokoro import KModel, KPipeline |
|
|
| device = device or ("cuda" if torch.cuda.is_available() else "cpu") |
| if variant not in CHECKPOINTS: |
| raise ValueError(f"Unknown checkpoint '{variant}'. Available: {sorted(CHECKPOINTS)}") |
| files = CHECKPOINTS[variant] |
|
|
| config_path = hf_hub_download(repo_id=REPO_ID, filename="config.json") |
| model_path = hf_hub_download(repo_id=REPO_ID, filename=files["model"]) |
| voice_path = hf_hub_download(repo_id=REPO_ID, filename=files["voice"]) |
|
|
| kmodel = KModel(repo_id=BASE_REPO_ID, config=config_path, model=model_path) |
| kmodel = kmodel.to(device).eval() |
|
|
| pipeline = KPipeline(lang_code="d", repo_id=BASE_REPO_ID, model=kmodel) |
|
|
| |
| |
| |
| |
| |
| _original_g2p = pipeline.g2p |
|
|
| def _patched_g2p(text): |
| phonemes, tokens = _original_g2p(text) |
| return phonemes.replace("ʏ", "y"), tokens |
|
|
| pipeline.g2p = _patched_g2p |
|
|
| voice = torch.load(voice_path, map_location="cpu", weights_only=True) |
| return pipeline, voice, device |
|
|
|
|
| def synthesize(text: str, output_path: str = "output.wav", speed: float = 1.0, variant: str = "default") -> None: |
| pipeline, voice, device = load_pipeline(variant) |
| print(f"Using device: {device} | checkpoint: {variant}") |
|
|
| audio_chunks = [] |
| for _, phonemes, audio in pipeline(text, voice=voice, speed=speed): |
| print(f"Phonemes: {phonemes}") |
| audio_chunks.append(audio) |
|
|
| if not audio_chunks: |
| print("WARNING: no audio was generated") |
| return |
|
|
| combined = np.concatenate(audio_chunks) |
| sf.write(output_path, combined, SAMPLE_RATE) |
| print(f"Saved: {output_path} ({len(combined) / SAMPLE_RATE:.1f}s)") |
|
|
|
|
| if __name__ == "__main__": |
| input_text = sys.argv[1] if len(sys.argv) > 1 else "Hallo, hier spricht Thorsten." |
| output_file = sys.argv[2] if len(sys.argv) > 2 else "output.wav" |
| checkpoint_variant = sys.argv[3] if len(sys.argv) > 3 else "default" |
| synthesize(input_text, output_file, variant=checkpoint_variant) |