Skip to main content

PyPI - Version PyPI - Python Version PyPI - Downloads codecov

PyKokoro

A Python library for Kokoro TTS (Text-to-Speech) using ONNX runtime.

Features

  • ONNX-based TTS: Fast, efficient text-to-speech using the Kokoro-82M model
  • Multiple Languages: Support for English, Spanish, French, German, Italian, Portuguese, and more
  • Multiple Voices: 54+ built-in voices (or 103 voices with v1.1-zh model)
  • Voice Blending: Create custom voices by blending multiple voices
  • Multiple Model Sources: Download models from HuggingFace or GitHub (v1.0/v1.1-zh)
  • Model Quality Options: Choose from fp32, fp16, q8, q4, and uint8 quantization levels
  • ONNX Execution Providers: Capability-driven CUDA, NNAPI, XNNPACK, CoreML, DirectML, and other runtime-reported providers
  • Phoneme Support: Advanced phoneme-based generation with kokorog2p
  • Language-Aware spaCy Models: Shared pipeline-owned Pass-A and Pass-B resources with disabled, local fallback, and strict policies
  • Hugging Face Integration: Automatic model downloading from Hugging Face Hub
  • Explicit Language Planning: The document language is required before parsing; SSMD lang spans provide explicit mixed-language runs
  • Text Normalization: Spokenform owns generic written-to-spoken preparation, including semantic say-as behavior when supported by the SSMD/Spokenform contract
  • Maintainer Benchmarking: PolyNorm-based phoneme regression tooling for the PyKokoro frontend path

v0.9 orchestration contract

PyKokoro v0.9 requires an explicit document language. Set GenerationConfig(lang="en-us") or pass lang="en-us" to run; the per-call value takes precedence. A voice or model profile never selects the language. Mixed-language documents must use explicit SSMD language spans, for example:

config = PipelineConfig(generation=GenerationConfig(lang="en-us"))
result = KokoroPipeline(config).run(
    'Hello [Welt]{lang="de"}.',
    lang="en-us",
 )

Integrated requests perform source analysis before Spokenform and fresh prepared-text analysis afterward. The pipeline reuses loaded local spaCy pipelines, but releases request documents before returning results. Use tokenizer_config.use_spacy=False to disable NLP, leave it unset for local-only fallback, or set it to True for strict model availability.

Runtime model support

Runtime model selection uses the canonical catalog/models.json registry. Model metadata, voices, frontend IDs, runtime layouts, artifact hashes, provider, and redistribution policy are not inferred from GitHub release names.

Status Meaning Examples
ready Registry distribution and PyKokoro frontend/layout are usable v1.0, v1.1-zh, v1.2-de-martin, de-crane, de-thorsten, th-wayu, sv-joakim, kk-anuarsv, vi-anphunl, Zaakirio Russian
experimental Usable only when explicitly enabled for an experimental frontend Profiles marked experimental by the local compatibility policy
restricted Runtime is visible but redistribution policy is not ordinary he-hebrew-nc
unsupported-layout/frontend Registry metadata is valid but the local implementation cannot serve it A future registry layout or frontend

Thai Wayu uses the registry's split-onnx-v1 layout and selects its prosody, curves, and decoder components as one distribution. Russian Zaakirio uses pinned upstream-only distributions and validates raw float32 voice artifacts locally; those bytes are not mirrored to GitHub.

Use python examples/models_and_languages.py to inspect every registry model, language, provider, voice, quality, frontend, layout, and support status without downloading model weights. Pass --model MODEL_ID to synthesize only one selected model; experimental frontends additionally require --include-experimental.

Public model capability discovery

Downstream applications can inspect the canonical runtime inventory without importing the ONNX backend or downloading model assets:

from pykokoro import discover_models

inventory = discover_models()
for model in inventory.models:
    print(model.model_id, model.languages, model.voices, model.status)

discover_models() reports runtime capabilities, selected distribution provenance, frontend and G2P metadata, qualities, and verified named lexicons. offline=True uses only the cached registry and forbids network access. refresh=True refreshes registry metadata only, never model or voice assets; the two options cannot be combined. registry_source and cache_fallback describe where the inventory came from.

This API is distinct from available_model_releases(): discovery describes capabilities the installed PyKokoro runtime can use, while the release catalog describes published model artifacts.

Public pipeline configuration resolution

For preflight, orchestration, dry-run planning, and configuration inspection, resolve the automatic model choices without constructing the synthesis runtime or loading model and voice assets:

from pykokoro import GenerationConfig, PipelineConfig, resolve_pipeline_config

requested = PipelineConfig(generation=GenerationConfig(lang="de"))
resolved = resolve_pipeline_config(requested)

print(resolved.model_variant)
print(resolved.model_source)
print(resolved.model_quality)
print(resolved.voice)

resolve_pipeline_config() is a metadata-only operation. It applies PyKokoro's built-in model, source, quality, and voice policy without constructing KokoroPipeline, importing ONNX Runtime, creating an ONNX session, loading assets, or synthesizing audio. It does not replace discover_models(), which provides runtime capability and model inventory metadata.

Logging and runtime diagnostics

PyKokoro uses standard Python logging for streaming operational visibility. It does not configure global logging, install handlers or formatters, set logger levels, or add timestamps. The embedding application owns logging presentation and decides whether lifecycle records are shown.

For local diagnostics, configure logging before constructing a pipeline:

import logging

logging.basicConfig(level=logging.DEBUG)

from pykokoro import KokoroPipeline

Production applications should configure the pykokoro logger through their existing logging setup instead of relying on basicConfig:

import logging

logging.getLogger("pykokoro").setLevel(logging.INFO)

INFO records cover major milestones such as model and distribution selection, artifact downloads, ONNX session creation, backend readiness, and voice loading. DEBUG records add stage timings, cache decisions, inference counts and runtimes, and audio-unit completion. Records contain no application-provided timestamps, so the host formatter can apply its own timestamp format.

Streaming logs and PipelineConfig(return_trace=True) serve different purposes. Logging reports lifecycle events while synthesis is running. return_trace=True attaches structured diagnostics to returned audio results for programmatic inspection. Enabling one does not enable or print the other. Routine lifecycle logs avoid complete user documents, phoneme streams, audio arrays, credentials, and model contents.

Installation

Basic Installation (CPU only)

pip install "pykokoro[cpu]"

The ONNX Runtime distributions are alternatives. Install exactly one provider extra for inference: cpu, gpu, openvino, or directml. The coreml extra uses the macOS CPU distribution plus CoreML tooling. The all extra adds optional playback support (sounddevice) and never installs multiple ONNX Runtime wheels.

GPU and Accelerator Support

PyKokoro supports multiple hardware accelerators for faster inference:

NVIDIA CUDA GPU

pip install pykokoro[gpu]

Intel OpenVINO

Note: OpenVINO is currently incompatible with Kokoro models due to dynamic rank tensor requirements. The provider will automatically fall back to CPU if OpenVINO fails.

pip install pykokoro[openvino]

DirectML (Windows - AMD/Intel/NVIDIA GPUs)

pip install pykokoro[directml]

Apple CoreML (macOS)

pip install pykokoro[coreml]

Optional Features

pip install pykokoro[all]

Direct Playback

For direct playback from memory, install the optional feature extra:

pip install "pykokoro[cpu,playback]"

AudioResult.play() sends an already-generated NumPy waveform directly to the system audio output. Playback is blocking and does not create a WAV file. For long text with low startup latency, use pipeline.play_streaming(text, unit="sentence"); it prepares the document globally, then generates sentence audio while one persistent bounded stream plays earlier sentences. Linux-like systems may also need a PortAudio system package. The older pykokoro[sounddevice] extra remains valid.

Performance Comparison

To find the best provider for your system, run the benchmark:

python examples/gpu_benchmark.py

Quick Start

The pipeline API is the only supported interface.

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"))
res = pipe.run("Hello")
audio = res.audio

First-run model downloads

Managed runtime assets are provisioned lazily when synthesis first needs them. On a cold cache, user-facing examples can show the asset name, expected size, byte progress, and checksum verification with the built-in reporter:

from pykokoro import ConsoleAssetProgress, GenerationConfig, KokoroPipeline, PipelineConfig

config = PipelineConfig(
    voice="af_sarah",
    generation=GenerationConfig(lang="en-us"),
    asset_progress=ConsoleAssetProgress(),
)
pipe = KokoroPipeline(config)
result = pipe.run("Hello")

Applications can receive structured AssetProgressEvent values instead by passing a callable as asset_progress. Valid managed assets are reused silently from the local cache, and offline mode never starts a download.

Managing Result Memory

By default, AudioResult retains the raw and processed waveform for each phoneme segment for diagnostics and callers that inspect segment audio. For long documents, enable compact result retention when only the final waveform and metadata are needed:

from pykokoro import GenerationConfig, PipelineConfig, build_pipeline

pipeline = build_pipeline(
    config=PipelineConfig(
        generation=GenerationConfig(lang="en-us"),
        voice="af_heart",
        retain_segment_audio=False,
    )
)

result = pipeline.run("Long text")
result.save_wav("chapter.wav")
result.release_audio()
pipeline.close()

Compact mode reduces memory retained by the completed result after generation; it does not make peak memory independent of input duration because the pipeline still builds the whole-result concatenated waveform. Use result.release_segment_audio() to retain the final waveform while dropping per-segment arrays, or result.release_audio() to drop both. These methods only release references owned by the result, so arrays held separately by callers remain valid. Callers that need raw or processed segment waveforms should keep retain_segment_audio=True. Use the paragraph streaming API below for bounded unit rendering.

Paragraph-Unit Streaming

Use prepare_units() when a document should be prepared once but rendered and stored one paragraph at a time. Preparation resolves SSMD directives, pauses, markers, voices, and preprocessing globally; skip_indices can omit units already completed by a caller.

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipeline = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"))
with pipeline.prepare_units("First paragraph.\n\nSecond paragraph.") as prepared:
    for unit in prepared.render(skip_indices={0}):
        try:
            save_waveform(unit.audio, unit.sample_rate)
        finally:
            unit.release_audio()

AudioUnitResult.release_audio() is destructive and idempotent. The iterator also releases the previous unit before yielding the next one, so consumers should copy or persist audio inside the loop. Closing the prepared object releases prepared segment audio but does not close the reusable pipeline backend.

Each descriptor has a zero-based source-order index and a text_hash using the pykokoro-audio-unit-v1 schema. Store both values in resume manifests; schema changes must use a new prefix. Hashes include audio-semantic settings such as voice, pauses, language, prosody, model variant, and explicit model_identity, but ignore tracing, retention, cache-directory, and machine-local runtime toggles. Persist or copy a unit's waveform before advancing the iterator because advancing releases the previous result.

Pipeline Stages

The pipeline is built from composable stages so you can swap behavior without rewriting the whole flow:

doc_parser (SSMD structure) -> text_preparer (Spokenform) -> sentence_segmenter (Phrasplit) -> g2p (prepared mode) -> phoneme_processing -> audio_generation -> audio_postprocessing

Stages can be replaced with no-op adapters when you want to disable behavior. See examples/pipeline_stage_showcase.py for a full wiring example.

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.stages.doc_parsers.plain import PlainTextDocumentParser

pipe = KokoroPipeline(
    PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"),
    doc_parser=PlainTextDocumentParser(),
)
res = pipe.run("First paragraph.\n\nSecond paragraph.")

Migration

Old (removed):

# Legacy Kokoro-based API has been removed in favor of the pipeline.

New:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"))
res = pipe.run("Hello")
audio = res.audio

Helper Snippet

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

generation = GenerationConfig(lang="en-us", speed=1.0)
config = PipelineConfig(voice="af_sarah", generation=generation)
pipe = KokoroPipeline(config)
res = pipe.run("Hello")

Hardware Acceleration

# Auto-select by runtime capability (CUDA > NNAPI > OpenVINO > CoreML > DirectML > XNNPACK > CPU)
# The selected accelerator is paired with CPU fallback when the session supports it.
from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), provider="auto", voice="af_sarah"))
res = pipe.run("Hello")

Explicit Provider Selection

# Force specific provider
from pykokoro import KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(provider="cuda", voice="af_sarah"))  # NVIDIA CUDA
pipe = KokoroPipeline(PipelineConfig(provider="nnapi", voice="af_sarah"))  # Android NNAPI
pipe = KokoroPipeline(PipelineConfig(provider="xnnpack", voice="af_sarah"))  # XNNPACK
pipe = KokoroPipeline(PipelineConfig(provider="openvino", voice="af_sarah"))  # Intel OpenVINO
pipe = KokoroPipeline(PipelineConfig(provider="directml", voice="af_sarah"))  # Windows DirectML
pipe = KokoroPipeline(PipelineConfig(provider="coreml", voice="af_sarah"))  # Apple CoreML
pipe = KokoroPipeline(PipelineConfig(provider="cpu", voice="af_sarah"))  # CPU only

Check Available Providers

# See all available providers on your system
python examples/provider_info.py

# Benchmark all providers
python examples/gpu_benchmark.py

Environment Variable Override

# Force a specific provider via environment variable
export ONNX_PROVIDER="OpenVINOExecutionProvider"
python your_script.py

Provider aliases and full names returned by ONNX Runtime are accepted. Inspect and resolve providers without duplicating platform-specific mappings:

from pykokoro.onnx_session import (
    get_available_execution_providers,
    resolve_execution_provider,
)

print(get_available_execution_providers())
print(resolve_execution_provider("auto"))

Usage Examples

Maintainer benchmark documentation for the PolyNorm phoneme gate lives in docs/polynorm_benchmark.md.

Short-sentence benchmark tools

PyKokoro has separate tools for structural diagnosis and listening-based parameter comparison:

  • benchmarks/short_sentence_latency.py runs the warm policy/scenario benchmark and reports final cases, actual cut strategies, failures, retries, and inference costs. It defaults to a human-readable table, supports --output-format table|jsonl|both, JSONL and summary JSON export, and --dry-run. It does not write WAV artifacts.
  • benchmarks/short_sentence_parameter_sweep.py renders one fixed short segment for an ordered parameter sweep. It writes one combined labeled WAV and a JSON manifest. Labels use short-sentence handling disabled, phrase retries default to zero, and the manifest records reproducibility metadata and audio offsets.

Use the parameter sweep for human listening, not automatic optimization. See docs/short_sentence_quality.md for CLI examples, supported parameters, starting ranges, and artifact details.

Basic Text-to-Speech

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

# Create pipeline with GPU acceleration and fp16 model
config = PipelineConfig(
    voice="af_nicole",
    provider="cuda",
    model_quality="fp16",
    generation=GenerationConfig(lang="en-us"),
)
pipe = KokoroPipeline(config)

# Generate audio
res = pipe.run("Hello world")
audio = res.audio

Voice Blending

# Blend two voices (50% each)
from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.voice_manager import VoiceBlend

blend = VoiceBlend.parse("af_nicole:50,am_michael:50")
pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice=blend))
res = pipe.run("Mixed voice")
audio = res.audio

Direct Playback of Generated Chunks

For independent chunks, AudioResult.play() plays each generated waveform directly:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"))
chunks = ["Long text", "here..."]
for text_chunk in chunks:
    result = pipe.run(text_chunk)
    result.play()

For long text with low startup latency, prefer sentence streaming through one persistent bounded output stream:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

text = "First sentence. Second sentence. Third sentence."
with KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah")) as pipe:
    pipe.play_streaming(text, unit="sentence", queue_size=2)

play_streaming() performs global document preparation first, then generates and queues each selected unit as playback consumes the previous one. It creates no temporary WAV and retains no complete generated waveform. queue_size is bounded pending-waveform capacity, not an exact startup prebuffer count.

For custom consumers or paragraph-sized chunks, use the prepared-unit API directly:

with pipe.prepare_units(text, unit="paragraph") as prepared:
    for result in prepared.render():
        try:
            consume(result.audio, result.sample_rate)
        finally:
            result.release_audio()

Use result.play() for a short utterance that has already been generated completely.

Phoneme-Based Generation

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig
from pykokoro.tokenizer import Tokenizer

# Create tokenizer
tokenizer = Tokenizer()

# Convert text to phonemes
phonemes = tokenizer.phonemize("Hello world", lang="en-us")
print(phonemes)  # hə'loʊ wɜːld

# Generate from phonemes
config = PipelineConfig(
    voice="af_sarah",
    generation=GenerationConfig(lang="en-us", is_phonemes=True),
)
pipe = KokoroPipeline(config)
res = pipe.run(phonemes)
audio = res.audio

Pause Control

PyKokoro uses SSMD (Speech Synthesis Markdown) syntax for controlling pauses in generated speech:

1. SSMD Break Markers

Add explicit pauses using SSMD break syntax in your text:

# Use SSMD break markers in your text
text = "Chapter 5 ...p I'm Klaus. ...c Welcome to the show!"

# Breaks are processed automatically
from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="am_michael"))
res = pipe.run(text)
audio = res.audio

SSMD Break Markers:

  • ...n - No pause (0ms)
  • ...w - Weak pause (150ms by default)
  • ...c - Clause/comma pause (300ms by default)
  • ...s - Sentence pause (600ms by default)
  • ...p - Paragraph pause (1000ms by default)
  • ...500ms - Custom pause (500 milliseconds)
  • ...2s - Custom pause (2 seconds)

Note: Bare ... (ellipsis) is NOT treated as a pause and will be phonemized normally.

Custom Pause Durations:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

config = PipelineConfig(
    voice="am_michael",
    generation=GenerationConfig(
        lang="en-us",
        pause_mode="manual",
        pause_clause=0.2,  # ...c = 200ms
        pause_sentence=0.5,  # ...s = 500ms
        pause_paragraph=1.5,  # ...p = 1500ms
    ),
)
pipe = KokoroPipeline(config)
res = pipe.run(text)
audio = res.audio

2. Automatic Natural Pauses

For more natural speech, enable automatic pause insertion at linguistic boundaries with pause_mode="auto":

text = """
Artificial intelligence is transforming our world. Machine learning models
are becoming more sophisticated, efficient, and accessible.

Deep learning, a subset of AI, uses neural networks with many layers. These
networks can learn complex patterns from data, enabling breakthroughs in
computer vision, natural language processing, and speech recognition.
"""

# Automatic pauses at sentence/paragraph boundaries, parenthetical asides, and high-confidence clausal commas
from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

config = PipelineConfig(
    voice="af_sarah",
    generation=GenerationConfig(
        lang="en-us",
        pause_mode="auto",
        pause_clause=0.25,  # Pause after high-confidence clausal commas
        pause_parenthetical=0.15,  # Short pause around parenthetical asides
        pause_sentence=0.5,  # Pause after sentences
        pause_paragraph=1.0,  # Pause after paragraphs
        pause_variance=0.05,  # Add natural variance (default)
        random_seed=42,  # For reproducible results (optional)
    ),
)
pipe = KokoroPipeline(config)
res = pipe.run(text)
audio = res.audio

Key Features:

  • Natural boundaries: Automatically pauses at sentences, paragraphs, high-confidence clausal commas, and parenthetical asides
  • Variance: Gaussian variance prevents robotic timing (±100ms by default)
  • Reproducible: Use random_seed for consistent output
  • Composable: Works with SSMD break markers

Splitting Behavior:

  • SsmdDocumentParser handles paragraph/sentence segmentation using SSMD.

  • PlainTextDocumentParser uses optional phrasplit sentence splitting.

  • In pause_mode="auto", dependency-aware Phrasplit analysis detects high-confidence clausal commas and parenthetical asides. List commas and shared-subject continuations remain untouched.

For example, It had picked up the sound of a explosion, direction suggested it was behind. is refined at the detected comma so the preceding segment receives one deterministic pause_clause. For example, They changed out their clothes (stained with blood). receives a short pause_parenthetical before the aside in auto mode. Set pause_parenthetical=0.0 to disable only these inferred parenthetical pauses. The setting is independent from pause_clause, pause_sentence, and pause_paragraph.

Pause Variance Options:

  • pause_variance=0.0 - No variance (exact pauses)
  • pause_variance=0.05 - Default (±100ms at 95% confidence)
  • pause_variance=0.1 - More variation (±200ms at 95% confidence)

Note: For sentence splitting with PlainTextDocumentParser and spaCy-based G2P tokenization, install spaCy and at least one language model:

pip install spacy
python -m spacy download en_core_web_sm
python -m spacy download en_core_web_md

If both TokenizerConfig.spacy_model and spacy_model_size are unset (the default), PyKokoro asks each spaCy-using backend to select the highest installed compatible model for the effective language (trf > lg > md > sm). No spaCy model is downloaded automatically. "auto" remains accepted as a compatibility alias for unset.

Use with_spacy_model(size="lg") or an explicit package when a strict choice is needed. lg and trf can improve linguistic quality but require substantially more memory and startup time than sm/md. The selected concrete sentence and G2P packages are available in AudioResult.document_metadata["spacy_models"].

For TTSForge integrations, use the PyKokoro version that provides these TokenizerConfig fields, read concrete sentence and G2P selections from that metadata path, and rely on the guarantee that plain and SSMD parsing share the same request while G2P delegates resolution to kokorog2p.

Combining Both Approaches:

Use SSMD markers for emphasis metadata and automatic pauses for natural rhythm:

text = "Welcome! ...p Let's discuss AI, machine learning, and deep learning."

config = PipelineConfig(
    voice="af_sarah",
    generation=GenerationConfig(lang="en-us", pause_mode="auto", pause_variance=0.05),
)
pipe = KokoroPipeline(config)
res = pipe.run(text)
audio = res.audio

See examples/pauses_demo.py, examples/pauses_with_splitting.py, and examples/automatic_pauses_demo.py for complete examples.

SSMD emphasis policy

SSMD emphasis is preserved in segment metadata, but PyKokoro defaults to SSMDRenderConfig(emphasis_mode="plain") so ordinary synthesis is not changed surprisingly. The policy modes are:

  • plain: preserve emphasis metadata and synthesize unmodified speech silently
  • approximate: apply deterministic gain-only changes: strong +6dB, moderate +3dB, and reduced -3dB by default
  • warn: synthesize unmodified speech and emit one ssmd.emphasis_unsupported trace warning per logical source segment
  • error: reject effectful emphasis before model inference

emphasis="none" means ordinary speech and is accepted silently in every mode. Set emphasis_gain_scale on SSMDRenderConfig to scale only the automatic gain while preserving the semantic level. 1.0 is the current/default strength, 0.5 halves the automatic gain, and 1.5 makes it 50% stronger; the supported range is 0.0..2.0. Explicit SSMD volume metadata takes precedence over automatic emphasis gain. This setting does not add automatic pitch or rate changes, and PyKokoro does not provide TTSForge's user-facing integer emphasis presets.

config = PipelineConfig(
    ssmd=SSMDRenderConfig(
        emphasis_mode="approximate",
        emphasis_gain_scale=1.5,
    )
)

Prosody backend selection

Combined SSMD pitch, rate, and volume effects use one AudioSig speech-effects compositor pass. PyKokoro defaults to the speech-oriented WSOLA backend:

from pykokoro import PipelineConfig, ProsodyConfig

config = PipelineConfig(
    prosody=ProsodyConfig(method="wsola"),
)

ESOLA and TD-PSOLA are experimental alternatives, while phase_vocoder remains available as a compatibility and diagnostic reference. The psola spelling is accepted as an alias for td_psola:

config = PipelineConfig(
    prosody=ProsodyConfig(method="td_psola"),
)

For an apples-to-apples comparison, disable fallback so unsupported methods cannot be silently relabeled:

config = PipelineConfig(
    prosody=ProsodyConfig(
        method="esola",
        fallback_methods=(),
        strict=True,
    ),
)

WSOLA is the production default. ESOLA validates its computed backend rate in 0.5..2.0, and current TD-PSOLA limits are rate 0.75..1.5 and pitch -6..+6 st; non-strict mode can fall back to configured backends after a failure. No backend guarantees formant preservation, and quality depends on the voice and utterance. Run examples/compare_prosody_algorithms.py before changing a default. Prosody is applied to isolated rendered segments, so it cannot restore sentence-level coarticulation, intonation, or spectral continuity lost during separate synthesis.

To request audible approximation explicitly:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig, SSMDRenderConfig

config = PipelineConfig(generation=GenerationConfig(lang="en-us"), ssmd=SSMDRenderConfig(emphasis_mode="approximate"))
result = KokoroPipeline(config).run("This is *moderate emphasis*.")

Voice Switching (SSMD)

You can switch voices per segment using SSMD directives. Block directives use <div voice="..."> while inline annotations use [text]{voice="..."}.

text = (
    '<div voice="af_sarah">\n'
    "Hello there.\n"
    "</div>\n\n"
    '<div voice="am_michael">\n'
    "General Kenobi.\n"
    "</div>"
)

pipe = KokoroPipeline(
    PipelineConfig(voice="af_sarah", generation=GenerationConfig(lang="en-us"))
)
res = pipe.run(text)

Explicit mixed-language text

Automatic routing is optional and remains separate from the explicit document language. Set the document language explicitly and mark semantic or pronunciation-only language spans with SSMD:

from pykokoro import (
    GenerationConfig,
    LanguageDetectionConfig,
    KokoroPipeline,
    PipelineConfig,
)

config = PipelineConfig(
    generation=GenerationConfig(lang="de"),
    language_detection=LanguageDetectionConfig(mode="auto", languages=("de", "en")),
)
pipe = KokoroPipeline(config)
text = 'Die [File]{lang="en" scope="pronunciation"} wird gecancelt.'
result = pipe.run(text)

scope="pronunciation" changes only G2P. Automatic KokoroG2P routing also changes only pronunciation fragments. The selected PyKokoro acoustic model, voice, ONNX provider, and document language remain German.

Automatic Spoken-Form Normalization

Spokenform owns ordinary written-to-spoken preparation in the integrated path. Common abbreviations and structured forms such as dates, times, numbers, currency, measurements, ordinals, and other supported expressions can be spoken naturally from raw text:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

text = (
    "Dr. Smith will see you at 10:30 on 05/20/2023. "
    "The box weighs 5 kg and costs $10.99."
)
pipeline = KokoroPipeline(
    PipelineConfig(generation=GenerationConfig(lang="en-us"))
)
result = pipeline.run(text)

The default pipeline owns this order explicitly: SSMD structure is parsed first, Spokenform prepares the written text, Phrasplit detects sentences and high-confidence clausal commas in the prepared text, structural refinement applies the resulting boundaries, and kokorog2p receives that prepared text through phonemize_prepared(). Segment offsets therefore refer to the prepared spoken clean_text; structural annotations, events, and preparation provenance remain available in the document metadata used by downstream stages. Use examples/german3.py for a German regression containing dates, quantities, abbreviations, ordinals, and currency.

Explicit SSMD Say-As Overrides

Use SSMD (Speech Synthesis Markdown) say-as annotations when the author needs explicit interpretation or an override. They are not required for common automatic forms:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"))

# Cardinal numbers
text = 'I have [123]{as="cardinal"} apples'
res = pipe.run(text)
# TTS says: "I have one hundred twenty-three apples"

# Ordinal numbers
text = 'I came in [3]{as="ordinal"} place'
res = pipe.run(text)
# TTS says: "I came in third place"

# Digits (spell out)
text = 'My PIN is [1234]{as="digits"}'
res = pipe.run(text)
# TTS says: "My PIN is one two three four"

# Telephone numbers
text = 'Call [+1-555-0123]{as="telephone"}'
res = pipe.run(text)
# TTS says: "Call plus one five five five oh one two three"

# Dates with custom formatting
text = 'Today is [12/31/2024]{as="date" format="mdy"}'
res = pipe.run(text)
# TTS says: "Today is December thirty-first, two thousand twenty-four"

# Time (12-hour or 24-hour)
text = 'The time is [14:30]{as="time"}'
res = pipe.run(text)
# TTS says: "The time is two thirty PM"

# Characters (spell out)
text = 'The code is [ABC]{as="characters"}'
res = pipe.run(text)
# TTS says: "The code is A B C"

# Fractions
text = 'Add [1/2]{as="fraction"} cup of sugar'
res = pipe.run(text)
# TTS says: "Add one half cup of sugar"

# Units
text = 'The package weighs [5kg]{as="unit"}'
res = pipe.run(text)
# TTS says: "The package weighs five kilograms"

Supported Say-As Types:

  • cardinal - Numbers as cardinals: "123" → "one hundred twenty-three"
  • ordinal - Numbers as ordinals: "3" → "third"
  • digits - Spell out digits: "123" → "one two three"
  • number - Alias for cardinal
  • fraction - Fractions: "1/2" → "one half"
  • characters - Spell out text: "ABC" → "A B C"
  • telephone - Phone numbers: "+1-555-0123" → "plus one five five five oh one two three"
  • date - Dates with format support (mdy, dmy, ymd, ym, my, md, dm, d, m, y)
  • time - Time in 12h or 24h format
  • unit - Units: "5kg" → "five kilograms"
  • expletive - Censors to "beep"

Multi-language Support:

Say-as works with multiple languages (English, French, German, Spanish, and more):

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

# French cardinal
text = '[123]{as="cardinal"}'
pipe = KokoroPipeline(PipelineConfig(voice="ff_siwis", generation=GenerationConfig(lang="fr-fr")))
res = pipe.run(text)
# TTS says: "cent vingt-trois"

# German ordinal
text = '[3]{as="ordinal"}'
pipe = KokoroPipeline(PipelineConfig(voice="gf_maria", generation=GenerationConfig(lang="de-de")))
res = pipe.run(text)
# TTS says: "dritte"

Combining with Other Features:

Say-as works seamlessly with all SSMD features:

# With prosody
text = '[100]{as="cardinal" volume="loud"} dollars!'

# With pauses
text = '[First]{as="ordinal"} ...c [second]{as="ordinal"} ...c [third]{as="ordinal"}!'

# With emphasis metadata (audible approximation is opt-in)
text = 'The winner is [1]{as="ordinal" emphasis="moderate"}!'

See examples/say_as_demo.py for comprehensive examples.

4. Automatic Short Sentence Handling

When processing text, very short sentences (like "Why?" or "Go!") can produce poor audio quality when processed individually (only 3-8 phonemes each). Pykokoro can add phoneme context around those short segments before synthesis.

How It Works:

  1. Short segments are detected based on phoneme token length.
  2. Depending on the chosen resolve mode, the segment is wrapped with more context. (default resolve mode: randomized-phrase when the loaded model exposes duration timestamps)
  3. TTS generates audio from the wrapped phoneme sequence.
  4. Cut away the extra context and put audio together.

This happens automatically during pipe.run() - no configuration needed! Phrase-based modes require a model duration/timestamp output. When no explicit short-sentence configuration is supplied, PyKokoro automatically uses wrap for models without that output. If a phrase mode is explicitly requested for such a model, PyKokoro logs a warning and falls back to wrap. Phrase-based short-sentence handling uses language-localized carrier catalogs for English, German, Spanish, French, Italian, Portuguese, European Portuguese, Korean, Japanese, Chinese/Mandarin, Arabic, Hebrew, Kazakh, Swedish, Thai, Vietnamese, Russian, Hindi, Polish, and Turkish. Unknown languages never fall back to English carrier phrases. They use wrap unless a user-provided ShortSentencePhraseSet is supplied.

NOTE: Carrier quality remains voice and model dependent. Benchmark localized phrases before relying on them in production.

Customizing the Behavior:

You can customize the behavior using ShortSentenceConfig:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.short_sentence_handler import ShortSentenceConfig

# Less aggressive short sentence handling (also less acurate)
short_sentence_config = ShortSentenceConfig(
    resolve_mode="wrap",
    min_phoneme_length=10,  # Treat segments <10 phoneme tokens as short
    phoneme_pretext="—",  # Add this before and after short phonemes
)

# More advanced short sentence handling (useful for some voices)
short_sentence_config = ShortSentenceConfig(
    resolve_mode="randomized-phrase",
    min_phoneme_length=40,  # Treat segments <40 phoneme tokens as short
)

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah", short_sentence_config=short_sentence_config))
res = pipe.run("Why?")

Default Configuration:

  • enabled=True: Short-sentence handling is enabled by default
  • min_phoneme_length=30: Segments below this token count engage short-sentence handling
  • resolve_mode="randomized-phrase" Chooses between randomized-phrase (default), phrase, or wrap (fallback). Phrase-based defaults require model timestamps; no-timestamp models automatically use wrap.
  • phrase_selection="auto" Chooses which phrase templates to use. auto uses "end" if the phrase ends with '.', otherwise uses "neutral"
  • phrase_fallback_tries=5: Phrase modes try up to X alternate phrase templates before falling back to wrap mode when a cut lacks confident boundaries.
  • phoneme_pretext="—": Phoneme context added in wrap mode before and after short segments
from pykokoro.short_sentence_handler import (
    PhraseResolveMode,
    ShortSentenceConfig,
)

short_sentence_config = ShortSentenceConfig(
    resolve_modes={
        "phrase": PhraseResolveMode(
            phrase_selection="end",  # "auto", "neutral", or "end"
        ),
        "randomized-phrase": RandomizedPhraseResolveMode(
            phrase_selection="neutral",  # "auto", "neutral", or "end"
        ),
        "wrap": WrapResolveMode(phoneme_pretext="…"),
    },
    resolve_mode="phrase",
    phrase_fallback_tries=10,
)

Voice Recommendation:

For phrase-based short-sentence handling, prefer these voices in order: am_santa, af_nicole, bm_lewis, bm_george, af_bella, am_echo, af_sky, af_sarah, bm_fable, af_heart, am_michael, af_alloy, af_nova, bf_isabella, and am_adam. If you prefer one of the less accurate voices, try blending it with one on this list. E.g. --voice-blend "bf_lily:60,bf_isabella:40"

Disabling Short Sentence Handling:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.short_sentence_handler import ShortSentenceConfig

short_sentence_config = ShortSentenceConfig(enabled=False)
pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah", short_sentence_config=short_sentence_config))
res = pipe.run("Why?")

See examples/optimal_phoneme_length_demo.py for a demonstration.

Advanced customization of short-sentence handling

You can add custom template phrases used to add context in phrase mode, but THIS IS NOT RECOMMENDED for most users! You can supply a ShortSentencePhraseSet when you need custom carrier wording or language coverage beyond the built-in catalogs.

WARNING: The quality of the phrase makes a huge difference. If possible, test the phrases first, e.g. by using the various short-sentence py scripts in metrics/. All default phrases have been verified with the metrics\rank_short_sentence_phrases_across_voice_list.py script to work reliably with most voices.

from pykokoro.short_sentence_handler import (
    PhraseResolveMode,
    ShortSentenceConfig,
)

short_sentence_config = ShortSentenceConfig(
    resolve_modes={
        "phrase": PhraseResolveMode(
            phrase_selection="end",  # "auto", "neutral", or "end"
            neutral_phrase="The word, {segment}, appears here.",  # Changing this to anything not in the default neutral_phrases list is not recommended
            end_phrase="The word is hello. The word is '{segment}'",  # Changing this to anything not in the default end_phrases list is not recommended
        ),
        "randomized-phrase": RandomizedPhraseResolveMode(
            phrase_selection="neutral",  # "auto", "neutral", or "end"
            neutral_phrases=[  # Adding new untested phrases is not recommended without rigurous testing
                "First {segment} is the word.",
                "Second {segment} is the word.",
                "Third {segment} is the word.",
                "Fourth {segment} is the word.",
            ],
            end_phrases=[  # Adding new untested phrases is not recommended without rigurous testing
                "First {segment}."
            ],
        ),
        "wrap": WrapResolveMode(phoneme_pretext="…"),
    },
    resolve_mode="phrase",
)

Available Voices

The library includes voices across different languages and accents. The number of available voices depends on the model source:

HuggingFace & GitHub v1.0 (54 voices)

  • American English: af_alloy, af_bella, af_sarah, am_adam, am_michael, etc.
  • British English: bf_alice, bf_emma, bm_george, bm_lewis
  • Spanish: ef_dora, em_alex
  • French: ff_siwis
  • Japanese: jf_alpha, jm_kumo
  • Chinese: zf_xiaobei, zm_yunxi
  • And many more...

GitHub v1.1-zh (103 voices)

Includes all voices from v1.0 plus additional Chinese voices:

  • English voices: af_maple, af_sol, bf_vale (confirmed working)
  • Chinese voices: zf_001 through zf_099, zm_009 through zm_100

Example - Using v1.1-zh with English:

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

config = PipelineConfig(
    voice="af_maple",
    model_source="github",
    model_variant="v1.1-zh",
    generation=GenerationConfig(lang="en-us"),
)
pipe = KokoroPipeline(config)
res = pipe.run("Hello world!")
audio = res.audio

List all available voices:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah"))
pipe.run("Hello")
# Voices are loaded lazily by the backend after the first run.
voices = pipe.synth._kokoro.get_voices()
print(voices)

Model Sources

PyKokoro supports downloading models from multiple sources:

HuggingFace (Default)

HuggingFace is the default source with 54 multi-language voices. It downloads the model, voice archive, and the vocabulary config required by the HuggingFace profile:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(
    PipelineConfig(
        generation=GenerationConfig(lang="en-us"),
        voice="af_sarah",
        model_source="huggingface",
        model_quality="fp32",  # fp32, fp16, q8, q8f16, q4, q4f16, uint8, uint8f16
    )
)
res = pipe.run("Hello world")

GitHub v1.0

54 voices with additional fp16-gpu optimized quality:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(
    PipelineConfig(
        generation=GenerationConfig(lang="en-us"),
        voice="af_sarah",
        model_source="github",
        model_variant="v1.0",
        model_quality="fp16-gpu",  # fp32, fp16, fp16-gpu, q8
    )
)
res = pipe.run("Hello world")

Termux/Android: GitHub v1.0

When HuggingFace downloads are unavailable, select the GitHub v1.0 source explicitly. GitHub v1.0 downloads only its ONNX model and voice archive and uses the embedded standard v1.0 vocabulary, so it does not require a HuggingFace config.json:

from pykokoro import KokoroPipeline, PipelineConfig

pipe = KokoroPipeline(
    PipelineConfig(
        voice="af_heart",
        model_source="github",
        model_variant="v1.0",
        model_quality="fp32",
    )
)

PyKokoro never silently switches between model sources. Explicit model_path and voices_path files are validated in place and are never replaced with managed cache files. A Termux/Android ONNX Runtime warning is a separate runtime-provider issue; it does not change model-download or source-selection behavior.

GitHub v1.1-zh (English + Chinese)

103 voices including English and Chinese speakers:

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

pipe = KokoroPipeline(
    PipelineConfig(
        voice="af_maple",
        model_source="github",
        model_variant="v1.1-zh",
        model_quality="fp32",  # Only fp32 available
        generation=GenerationConfig(lang="en-us"),
    )
)
res = pipe.run("Hello world")
audio = res.audio

Note: Chinese text generation requires proper phonemization support (currently in development).

German Martin v1.2

When GenerationConfig(lang="de") (or de-de, de-at, or de-ch) is used without explicit model settings, PyKokoro selects the GitHub v1.2-de-martin profile, its fp32-only kokoro-german-martin-v1.2.onnx model, and the single martin voice before backend and G2P caches are constructed. The first run downloads roughly 311 MB of model and voice assets into the normal ~/.cache/pykokoro cache. GitHub downloads are verified with the published SHA-256 digests and structural checks; invalid managed cached files are removed and re-downloaded. Explicit model_path and voices_path files are validated in place and are never silently replaced.

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

config = PipelineConfig(generation=GenerationConfig(lang="de", speed=1.125))
with KokoroPipeline(config) as pipe:
    result = pipe.run("Das ist ein deutscher Testsatz.")

For reproducible configuration, select the profile explicitly:

config = PipelineConfig(
    voice="martin",
    model_source="github",
    model_variant="v1.2-de-martin",
    model_quality="fp32",
    generation=GenerationConfig(lang="de", speed=1.125),
)

Martin uses the built-in Kokoro v1.0 vocabulary and does not download a Tundragoon config. martin alone also infers German; custom voice archives may expose additional voice names when selected explicitly. The profile's suggested speed of 1.125 is advisory, so applications must set it explicitly when they want it. German Language-specific automatic spoken-form normalization belongs to the compatible kokorog2p release. PyKokoro keeps source offsets and segments tied to the original input text, consumes kokorog2p's prepared G2P result, and owns synthesis.

Model Quality Options

Available quality options vary by source:

HuggingFace Models:

  • fp32: Full precision (highest quality, largest size)
  • fp16: Half precision (good quality, smaller size)
  • q8: 8-bit quantized (fast, small)
  • q8f16: 8-bit with fp16 (balanced)
  • q4: 4-bit quantized (fastest, smallest)
  • q4f16: 4-bit with fp16 (compact)
  • uint8: Unsigned 8-bit (compatible)
  • uint8f16: Unsigned 8-bit with fp16

GitHub v1.0 Models:

  • fp32: Full precision
  • fp16: Half precision
  • fp16-gpu: GPU-optimized fp16
  • q8: 8-bit quantized

GitHub v1.1-zh Models:

  • fp32: Full precision only

GitHub v1.2-de-martin:

  • fp32: Full precision only; no fp16 or quantized Martin artifacts are published
from pykokoro import KokoroPipeline, PipelineConfig

# HuggingFace with q8
pipe = KokoroPipeline(
    PipelineConfig(voice="af_sarah", model_source="huggingface", model_quality="q8")
)

# GitHub v1.0 with GPU-optimized fp16
pipe = KokoroPipeline(
    PipelineConfig(
        voice="af_sarah",
        model_source="github",
        model_variant="v1.0",
        model_quality="fp16-gpu",
    )
)

Source-Aware Asset Inspection

Model, config, and voice status checks use the exact (source, variant, quality) configuration. Voice archives use source- and variant-specific names, including voices-v1.0.bin, voices-v1.1-zh.bin, and voices-german-v1.1.bin for GitHub.

from pykokoro.model_assets import are_models_downloaded, get_model_asset_paths

assets = get_model_asset_paths(
    source="github",
    variant="v1.0",
    quality="fp32",
)
print(assets.model, assets.voices, assets.missing)
print(are_models_downloaded(source="github", variant="v1.0", quality="fp32"))

Inspection does not download assets or consult another source, variant, or quality. Runtime startup performs checksum and structural validation for managed assets.

Registry and model-cache updates

Managed registry metadata is cached at ~/.cache/pykokoro/registry/models.json; runtime artifacts are stored in model and distribution-specific subdirectories below the same registry cache. Every cached and downloaded artifact is checked against its recorded size and SHA-256 digest.

When an online load must use the last valid local registry because the remote catalog is temporarily unavailable, PyKokoro records that fallback and logs a warning. If a newly downloaded artifact proves that the selected catalog metadata is stale, PyKokoro bypasses the catalog cache, refreshes the registry once, and retries resolution using the fresh distribution metadata. Only artifacts that fail validation are replaced. Integrity verification is never disabled.

Offline mode reads and validates the cached registry and artifacts without network access. Missing or invalid offline assets fail clearly. Users do not need to delete models.json or an entire model directory after a catalog or model update.

Configuration

Configuration is stored in a platform-specific directory:

  • Linux: ~/.config/pykokoro/config.json
  • macOS: ~/Library/Application Support/pykokoro/config.json
  • Windows: %APPDATA%\pykokoro\config.json
from pykokoro.utils import load_config, save_config

# Load config
config = load_config()

# Modify config
config["model_quality"] = "fp16"
config["use_gpu"] = True

# Save config
save_config(config)

Advanced Features

Custom Phoneme Dictionary

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.tokenizer import TokenizerConfig

# Create config with custom phoneme dictionary
tokenizer_config = TokenizerConfig(phoneme_dictionary_path="my_pronunciations.json")

pipe = KokoroPipeline(
    PipelineConfig(
        voice="af_sarah",
        generation=GenerationConfig(lang="en-us"),
        tokenizer_config=tokenizer_config,
    )
)
res = pipe.run("Hello")

Explicit Mixed Language Support

Automatic language detection is intentionally not configured in the tokenizer. Use the document language and explicit SSMD lang spans instead:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig

config = PipelineConfig(generation=GenerationConfig(lang="en-us"))
pipe = KokoroPipeline(config)
res = pipe.run("[Ich gehe]{lang=\"de\"} zum Meeting", lang="en-us")

Language-Aware spaCy Model Selection

Use the helper to request highest-available selection, an exact tier, or an exact package. The transform applies the same request to sentence segmentation and G2P:

from pykokoro import (
    GenerationConfig,
    KokoroPipeline,
    PipelineConfig,
    with_spacy_model,
)

base = PipelineConfig(
    voice="af_sarah",
    generation=GenerationConfig(lang="de"),
)
config = with_spacy_model(size="lg")(base)

# For lang="de", this asks both lower libraries for de_core_news_lg
pipe = KokoroPipeline(config)
res = pipe.run("Guten Tag")

# Or select one exact package:
config = with_spacy_model("de_core_news_sm")(base)

You can still force an explicit model package name:

from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.tokenizer import TokenizerConfig

tokenizer_config = TokenizerConfig(
    spacy_model="fr_core_news_sm",  # explicit package
)
pipe = KokoroPipeline(PipelineConfig(voice="af_sarah", tokenizer_config=tokenizer_config))

Backend Configuration

Control which phonemization backend and dictionaries to use:

from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig
from pykokoro.tokenizer import TokenizerConfig

# Native KokoroG2P with explicit Gold lexicon and eSpeak provider fallback.
tokenizer_config = TokenizerConfig(
    backend="kokorog2p",
    lexicons=("gold",),
    fallback="espeak",
)

# Native KokoroG2P with no static Lexphon layer and no provider fallback.
tokenizer_config = TokenizerConfig(
    backend="kokorog2p",
    lexicons=(),
    fallback="none",
)

# Primary eSpeak backend. This is not lexicon-first fallback behavior.
tokenizer_config = TokenizerConfig(backend="espeak")

# Primary Goruut backend (requires the Goruut extra/runtime).
tokenizer_config = TokenizerConfig(backend="goruut")
pipe = KokoroPipeline(PipelineConfig(generation=GenerationConfig(lang="en-us"), voice="af_sarah", tokenizer_config=tokenizer_config))
res = pipe.run("Hello")

backend="kokorog2p" selects the native lexicon-first stack. Its fallback chooses the optional Lexphon provider after selected lexicons miss: none, espeak, or goruut. backend="espeak" and backend="goruut" select those engines as the primary backend.

Note: use_dictionary, load_gold, and load_silver are legacy compatibility inputs. New code should use TokenizerConfig.lexicons. Explicit named selections take precedence; the only legacy combination without a faithful current mapping is Gold disabled with Silver enabled, which raises an actionable error.

Named KokoroG2P Lexicons

The native KokoroG2P backend also supports explicit named lexicon selection through TokenizerConfig.lexicons:

from pykokoro.tokenizer import TokenizerConfig

# Compatibility behavior. PyKokoro selects de-de:espeak implicitly for German; other languages use their KokoroG2P defaults.
default_config = TokenizerConfig()
# German Gold lexicon only.
gold_config = TokenizerConfig(lexicons="gold")

# German Crane lexicon only.
crane_config = TokenizerConfig(lexicons="crane")

lexicons=None uses PyKokoro's language defaults. For German, the implicit selection is the static de-de:espeak lexicon. An explicit selection takes precedence over legacy dictionary flags. lexicons=("gold", "crane") are supported for layered lookup, where the first matching layer wins. That layered lookup is not a Gold-versus-Crane A/B comparison. For an A/B comparison, render separately with ("gold",) and ("crane",) and combine the results yourself.

Provider-only operation is explicit with lexicons=(); it selects no static Lexphon layers and can use fallback="espeak" or fallback="goruut". A static lexicon named espeak is still a lexical resource and does not mean the dynamic eSpeak provider. The named lexicons are KokoroG2P/G2Lex resources consumed by PyKokoro; they are not PyKokoro-owned datasets.

Automatic Lexphon data provisioning

Before native KokoroG2P construction, PyKokoro resolves the effective named lexicons for the routed language and checks the local Lexphon store. In auto mode only missing Lexphon-backed assets are installed. Warm runs require no catalog access or network access. Provisioning applies only to the native backend="kokorog2p" path; primary eSpeak and Goruut backends do not download static lexicons.

Use "installed-only" for offline or pre-provisioned deployments. In that mode PyKokoro never installs or consults the catalog. A missing asset raises Lexphon's original installation error. Catalog, download, integrity, alphabet, and other G2P errors are propagated unchanged.

from pykokoro.tokenizer import TokenizerConfig

automatic = TokenizerConfig(lexicons=("gold",))
offline = TokenizerConfig(
    lexicons=("gold",),
    lexicon_data_policy="installed-only",
)

For explicit provisioning, install the selected assets before running PyKokoro:

lexphon data available de-DE
lexphon data install de-de:gold
lexphon data verify de-de:gold

Set LEXPHON_DATA_HOME to select the persistent data store. Set LEXPHON_CATALOG_URL to use a pinned local or remote catalog during provisioning. A pre-populated data store can be copied into an offline runtime; no catalog is needed on warm paths.

External G2P Libraries: You can also use external phonemization libraries like Misaki:

from misaki import en, espeak
from pykokoro import KokoroPipeline, PipelineConfig
from pykokoro.generation_config import GenerationConfig

# Misaki G2P with espeak-ng fallback
fallback = espeak.EspeakFallback(british=False)
g2p = en.G2P(trf=False, british=False, fallback=fallback)
phonemes, _ = g2p("Hello, world!")

# Generate audio from phonemes
pipe = KokoroPipeline(
    PipelineConfig(
        voice="af_bella",
        generation=GenerationConfig(is_phonemes=True, lang="en-us"),
    )
)
res = pipe.run(phonemes)
samples = res.audio

SSMD 0.8 portable documents

PyKokoro consumes SSMD 0.8 portable front matter by default. Header metadata is never spoken: title is returned in AudioResult.document_metadata, voice_bindings.kokoro maps portable role names to concrete Kokoro voices, and pause_defaults controls implicit sentence, paragraph, and concrete voice-change boundaries. Explicit SSMD breaks always win over document defaults, and simultaneous defaults use the longest duration.

from dataclasses import replace
from pykokoro import GenerationConfig, KokoroPipeline, PipelineConfig, SSMDRenderConfig

script = """---
title: Portable review
voice_bindings:
  kokoro:
    host: af_sarah
    guest: af_bella
pause_defaults:
  enabled: true
  sentence: 250ms
  paragraph: 700ms
  voice_change: 350ms
---
<div voice="host">Welcome to the review.</div>

<div voice="guest">The roles remain portable across renderers.</div>
"""
cfg = PipelineConfig(generation=GenerationConfig(lang="en-us"), ssmd=SSMDRenderConfig())
result = KokoroPipeline(cfg).run(
    script,
    ssmd=replace(cfg.ssmd, voice_bindings={"kokoro": {"guest": "bf_emma"}}),
)
assert result.document_metadata["title"] == "Portable review"

Use SSMDRenderConfig(parse_header=False) only when a literal leading --- block must remain text. PyKokoro does not read SSMD's user configuration files implicitly. Voice language, gender, and variant hints are preserved as metadata but do not select voices; audio annotations require an application-supplied resolver, and unsupported extensions are rejected for the Kokoro profile.

Word timings

Timestamp-capable Kokoro ONNX models expose model-derived word timings from named duration outputs (pred_dur, pred_duration, or durations). AudioUnitResult.word_timings is relative to that unit's waveform, while AudioResult.word_timings is relative to the complete waveform. Each WordTiming uses integer sample offsets into the exact final waveform and clean-text character offsets; derive seconds with start_seconds(sample_rate) and end_seconds(sample_rate). Missing or incomplete duration output, waveform-only models, and externally replaced audio produce no fabricated timings. The G2P cache rebuilds schema-incompatible entries after upgrade, and release_audio() preserves timing metadata.

For sentence streaming, see examples/stream_with_word_timings.py. Applications can copy each unit's audio, keep its timing metadata, and highlight document.clean_text[word.char_start:word.char_end] whenever the playback sample cursor is within [word.start_sample, word.end_sample).

License

This library is licensed under the Apache License 2.0.

Credits

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Release files for pykokoro 0.9.9

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pykokoro 0.9.9
File Size Uploaded
pykokoro-0.9.9.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for pykokoro 0.9.9
File Interpreter ABI Platform
pykokoro-0.9.9-py3-none-any.whl Python 3 none any Details

Total release size: 1.4 MB

Release files / pykokoro-0.9.9.tar.gz

Download URL pykokoro-0.9.9.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
e89fc86608817d886c4896931eededa4104250810ccc7e4746288df6501e186e
BLAKE2b-256 checksum
How to use checksums
0e2878753a617658b15f6309302feac1520d405c55e9c908af1488fe1fe8d1e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.14

Release files / pykokoro-0.9.9-py3-none-any.whl

Download URL pykokoro-0.9.9-py3-none-any.whl
Size 246.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8ae416e395cb10db1d0d20f47f73f5cc9dd47dbbf1e3ba2fd4b9c203445a1911
BLAKE2b-256 checksum
How to use checksums
1908d27834d2f544048844f51194fdb8d85e1e9fa5d6d6de4073222681450d4a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.10.0

2 release files

0.9.10

2 release files

This release

0.9.9 This release

2 release files

0.9.8

2 release files

0.9.7

2 release files

0.9.6

2 release files

0.9.5

2 release files

0.9.4

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.8

2 release files

0.8.7

2 release files

0.8.6

2 release files

0.8.5

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.5

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page