Skip to main content

Convert Markdown and plain text files to speech audio using Kokoro-82M (free, local TTS)

Project description

md2speech

Convert Markdown and plain-text files to speech audio using Kokoro-82M — a free, local, offline text-to-speech engine. No API keys, no cloud services.

Features

  • Free and offline — Kokoro-82M runs locally via PyTorch; model weights download once from Hugging Face
  • Markdown and plain text — reads .md, .markdown, and .txt files
  • MP3 by default — also supports WAV, OGG, and FLAC
  • Smart text normalization — expands abbreviations, numbers, currency, percentages, and times for natural speech
  • Long-document support — synthesizes paragraph-by-paragraph with configurable pauses
  • CLI and Python API — use from the terminal or import as a library
  • Audio post-processing — peak normalization, silence padding, and resampling to 44.1 kHz

Requirements

Requirement Notes
Python 3.11 or 3.12 Required for Kokoro/torch compatibility; 3.13+ unsupported
ffmpeg Required for MP3, OGG, and FLAC export (not needed for WAV)
espeak-ng Phonemization via Kokoro/misaki; required for non-English, recommended for English
~500 MB disk Kokoro-82M model weights (downloaded on first synthesis)
Network (first run) Model downloads from Hugging Face automatically

Before you install

md2speech pulls in roughly 45 Python packages and needs ~2–4 GB disk (PyTorch + Kokoro stack). A first install typically takes 5–15 minutes depending on your network and whether pre-built wheels are available for your platform.

  • Use Python 3.11 or 3.12 only — Python 3.13+ is not supported
  • Do not install into conda base — NumPy 1.x in base conflicts with md2speech's NumPy 2.x requirement
  • After install, run md2speech --doctor to verify your environment

Installation

Ranked install paths

Path Audience Steps
pipx CLI users (recommended) pipx install md2speech
venv + pip Library users python3.12 -m venv .venv && source .venv/bin/activate && pip install md2speech
conda Data-science users conda env create -f environment.yml && conda activate md2speech

With pipx (recommended)

Install the CLI globally and keep it isolated from other Python projects:

brew install pipx ffmpeg   # macOS; ffmpeg required for MP3
pipx ensurepath            # add ~/.local/bin to PATH (restart terminal once)

pipx install md2speech
md2speech --doctor
md2speech --help

Upgrade later:

pipx upgrade md2speech

From PyPI

pip install md2speech
md2speech --doctor

With conda

Install system libraries via conda-forge; install md2speech and its Python stack via pip inside the env:

conda env create -f environment.yml
conda activate md2speech
md2speech --doctor

Important:

  • Do not install into conda base
  • Do not conda install pytorch in the same env — use pip-installed torch to avoid pip/conda splits
  • Do not conda install md2speech — use pip inside a dedicated env

On Linux, if espeak-ng fails inside conda, see the Kokoro README for optional libstdcxx troubleshooting.

Or use the helper script:

./scripts/install.sh --conda

From source

git clone https://github.com/evelasko/md2speech
cd md2speech
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -e ".[dev]"

With uv

cd md2speech
uv sync
uv pip install -e ".[dev]"

Install helper script

./scripts/install.sh              # pipx (default)
./scripts/install.sh --venv       # virtualenv in .venv
./scripts/install.sh --conda      # conda env from environment.yml

System dependencies

ffmpeg

ffmpeg must be on your PATH for compressed audio formats.

Platform Command
macOS brew install ffmpeg
Ubuntu/Debian sudo apt install ffmpeg
Windows winget install ffmpeg or choco install ffmpeg

espeak-ng

espeak-ng is used for phonemization (via Kokoro/misaki). It is required for non-English languages and recommended for English.

Platform Command
macOS brew install espeak-ng
Ubuntu/Debian sudo apt install espeak-ng
Windows Install the espeak-ng MSI, then set PHONEMIZER_ESPEAK_LIBRARY to the espeak-ng.dll path (see kokoro#101)

Windows build tools

If spaCy/blis wheels are missing for your Python version, install Microsoft C++ Build Tools before pip install md2speech.

First run: The Kokoro-82M model (~hundreds of MB) downloads automatically from Hugging Face on the first synthesis. Subsequent runs use the cached weights.

Quick Start

# Convert article.md → article.mp3 in the same folder
md2speech article.md

# Explicit output path and WAV format (no ffmpeg needed)
md2speech chapter.txt -o ~/Audio/chapter.wav --format wav

# Long document with a specific voice and speed
md2speech book.md -o book.mp3 --voice am_adam --speed 0.95

# British English
md2speech article.md --lang b --voice bf_emma

# Brazilian Portuguese (language inferred from voice if omitted)
md2speech texto.md --voice pm_alex -o texto.mp3

# List supported languages and voices
md2speech --list-languages
md2speech --list-voices
md2speech --list-voices en-gb

# Verbose mode with synthesis progress
md2speech notes.md -V

CLI Reference

Argument Flag Default Description
input (required) Path to .md, .markdown, or .txt file
--output -o Same dir/name as input Output audio file path
--format -f mp3 Output format: mp3, wav, ogg, flac
--voice af_heart Kokoro voice ID (see --list-voices)
--speed 1.0 Speech speed multiplier
--lang / --language inferred from voice Language code or alias (a, en-gb, pt-br, …)
--list-languages Print supported languages and exit
--list-voices [LANG] Print available voices, optionally filtered by language
--doctor Check environment dependencies and exit
--no-normalize off Skip text normalization
--paragraph-pause 0.4 Seconds of silence between paragraphs
--verbose -V off Enable debug logging and progress bar

Default output path: When --output is omitted, the output file is written next to the input with the same basename and the chosen format extension (e.g. article.mdarticle.mp3).

Python API

High-level

from md2speech import synthesize_file

result = synthesize_file(
    "article.md",
    output_path="article.mp3",
    output_format="mp3",
    voice="bf_emma",
    lang="b",                    # or "en-gb"
    speed=1.0,
)
print(result.path)              # Path to written audio
print(result.duration_seconds)  # float

Advanced building blocks

from md2speech import (
    read_document,
    extract_plain_text,
    TTSEngine,
    AudioWriter,
    list_languages,
    list_voices,
    resolve_voice_and_lang,
)
from md2speech.synthesize import synthesize_text, synthesize_long_text
from md2speech.normalize import normalize_text

# Read and extract speakable text
text = read_document("notes.md")
plain = extract_plain_text("# Hello\n\n**World**")

# Normalize for TTS
spoken = normalize_text("Dr. Smith paid $12.50 at 3:45 PM")

# Low-level synthesis
engine = TTSEngine(lang_code="a")
audio = synthesize_long_text(text, voice="af_heart", engine=engine)

# Post-process and write
writer = AudioWriter()
processed = writer.process(audio, orig_sr=24000)
writer.write_audio(processed, "output.mp3", format_name="mp3")

Run as a module:

python -m md2speech article.md -o article.mp3

Languages & Voices

Kokoro supports 9 languages. Use --lang / --language to set the language and --voice to pick a speaker. If --lang is omitted, the language is inferred from the voice prefix (e.g. bf_emma → British English).

Code Language Default voice Voices
a American English af_heart 20 (af_*, am_*)
b British English bf_emma 8 (bf_*, bm_*)
e Spanish em_alex 3
f French ff_siwis 1
h Hindi hf_alpha 4
i Italian if_sara 2
p Brazilian Portuguese pm_alex 3
j Japanese jf_alpha 5
z Mandarin Chinese zf_xiaoxiao 8

Aliases: en-us, en-gb, spanish, french, hindi, italian, pt-br, japanese, chinese, and more.

md2speech --list-languages
md2speech --list-voices b

If only --lang is set (no --voice), the default voice for that language is used. Voice and language must match — md2speech doc.md --lang b --voice af_heart will error with a list of valid British voices.

Note: Japanese (j) and Mandarin Chinese (z) require extra packages: pip install "md2speech[ja]" or pip install "md2speech[zh]".

Recommended voices

Voice ID Gender Language Character
af_heart Female American English Warm, clear — default
am_adam Male American English Deep, steady
af_bella Female American English Soft
am_michael Male American English Conversational
bf_emma Female British English Clear British accent
bm_george Male British English British male
em_alex Male Spanish Spanish male
ff_siwis Female French French female
pm_alex Male Brazilian Portuguese Portuguese male
jf_alpha Female Japanese Japanese female
zf_xiaoxiao Female Mandarin Chinese Mandarin female

See the Kokoro documentation for the full voice list.

Supported Formats

Format ffmpeg required Notes
mp3 Yes Default — best for sharing and playback
wav No Uncompressed PCM via soundfile
ogg Yes Vorbis encoding via pydub
flac Yes Lossless compression via pydub

How It Works

Input file (.md / .txt)
        │
        ▼
  Read & extract plain text
  (strip markup, front matter)
        │
        ▼
  Normalize text
  (numbers, abbreviations, currency)
        │
        ▼
  Split into paragraphs & lines
        │
        ▼
  Synthesize each line (Kokoro-82M)
  + silence between paragraphs
        │
        ▼
  Post-process audio
  (peak normalize → resample 44.1 kHz → pad)
        │
        ▼
  Export (MP3 / WAV / OGG / FLAC)

Markdown Handling

Element Behavior
YAML front matter Stripped (--- delimited block at top)
Headings (#) Converted to plain text (no "hash hash")
Bold / italic Markup removed, text preserved
Links Link text spoken; URL omitted
Images Alt text spoken if present
Fenced code blocks Skipped entirely (not spoken)
Inline code Spoken literally
HTML comments Removed
Lists Marker removed; item text preserved
Paragraph breaks Preserved for natural pacing

Troubleshooting

Run md2speech --doctor after any fix to confirm your environment is healthy.

See also docs/UPSTREAM_DEPS.md for dependency ownership and tracked upstream issues.

ffmpeg not found

MP3, OGG, and FLAC export require ffmpeg on your PATH. Install it for your platform (see System dependencies) or use --format wav which does not need ffmpeg. Run md2speech --doctor to confirm the fix.

espeak not installed on your system

Kokoro/misaki needs espeak-ng for phonemization. Install for your platform (see espeak-ng). On Windows, also set PHONEMIZER_ESPEAK_LIBRARY to the DLL path. Run md2speech --doctor to confirm the fix.

spaCy / blis / thinc build failure

Common on Python 3.12 when pre-built wheels are unavailable:

pip install --upgrade pip
pip install md2speech

On Linux, if compilation fails:

BLIS_ARCH=generic pip install md2speech

On Windows, install Microsoft C++ Build Tools if wheels are missing. Run md2speech --doctor to confirm the fix.

NumPy 1.x vs 2.x in conda base

Installing into conda base often leaves NumPy 1.x, which conflicts with md2speech's NumPy 2.x requirement. Create a fresh env instead:

python3.12 -m venv .venv && source .venv/bin/activate && pip install md2speech
# or: conda env create -f environment.yml

Run md2speech --doctor to confirm the fix.

conda solver conflicts

Do not conda install md2speech. Use a dedicated env with system libs from conda-forge and md2speech from pip:

conda env create -f environment.yml
conda activate md2speech

Run md2speech --doctor to confirm the fix.

Model download fails

The first synthesis downloads Kokoro-82M from Hugging Face. Ensure you have internet access and sufficient disk space. If behind a proxy, configure Hugging Face Hub environment variables (HF_ENDPOINT, etc.).

Slow synthesis / memory

Kokoro runs on CPU by default. If you have a CUDA or Apple Silicon GPU, the engine auto-detects and uses cuda or mps when available. Very long documents synthesize line-by-line with a progress bar in verbose mode (-V).

Torch / MPS / CUDA issues

If GPU inference fails, force CPU by setting device=None when creating a TTSEngine instance in the Python API. The CLI uses automatic device detection.

NumPy / PyTorch version mismatch

md2speech requires PyTorch >= 2.4.1. Older PyTorch builds are incompatible with current transformers releases and with NumPy 2.x, which can surface as:

  • A module that was compiled using NumPy 1.x cannot be run in NumPy 2.x
  • Disabling PyTorch because PyTorch >= 2.4 is required but found ...
  • Failed to load Kokoro model even when Hugging Face is reachable

Reinstall to pull compatible versions:

pipx reinstall md2speech
# or: pip install --upgrade --force-reinstall md2speech

Run md2speech --doctor to confirm the fix.

Empty document error

If the input file is empty or contains only markup/code with no speakable text, md2speech exits with an error.

Development

pip install -e ".[dev]"
pytest                  # unit tests (mocked TTS)
pytest -m integration   # optional real synthesis test

Publishing (maintainers)

Releases are automated when a version tag is pushed to main.

  1. Bump version in pyproject.toml and src/md2speech/__init__.py
  2. Commit, push to main
  3. Create and push a tag:
git tag v0.1.0
git push origin v0.1.0

The Release workflow will:

  1. Run unit tests on Python 3.12
  2. Build the wheel (.whl) and source distribution (.tar.gz)
  3. Publish both to PyPI
  4. Create a GitHub Release with the build artifacts attached

One-time setup (GitHub pypi environment):

  1. Create a PyPI API token at pypi.org/manage/account/token scoped to the md2speech project.
  2. In GitHub: Settings → Environments → pypi → Environment secrets → add PYPI_API_TOKEN with that token.
  3. Ensure the release workflow is on main and references the pypi environment (see release.yml).

Alternative (no token): configure PyPI trusted publishing for owner evelasko, repo md2speech, workflow release.yml, environment pypi. OIDC is already enabled in the workflow (id-token: write).

First release:

# bump version in pyproject.toml and src/md2speech/__init__.py, commit, push
git tag v0.1.0
git push origin main
git push origin v0.1.0

Watch the Actions tab — the Release workflow publishes to PyPI and creates a GitHub Release.

Users install published versions with:

pipx install md2speech
pipx install md2speech==0.1.0

License

MIT — see LICENSE.

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

md2speech-0.1.3.tar.gz (324.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

md2speech-0.1.3-py3-none-any.whl (24.7 kB view details)

Uploaded Python 3

File details

Details for the file md2speech-0.1.3.tar.gz.

File metadata

  • Download URL: md2speech-0.1.3.tar.gz
  • Upload date:
  • Size: 324.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for md2speech-0.1.3.tar.gz
Algorithm Hash digest
SHA256 0885a2fd4611c7dc3cd2fea4ea8748bf316ce04fbcc139be065f10e9e4304471
MD5 630d0b73b4ae2129de7dbdad97a7f335
BLAKE2b-256 c8a551e7dba530d30ee23c809b1dab78e140510a17fec19114ca2e4983e115a6

See more details on using hashes here.

Provenance

The following attestation bundles were made for md2speech-0.1.3.tar.gz:

Publisher: release.yml on evelasko/md2speech

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file md2speech-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: md2speech-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 24.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for md2speech-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7958fc55f2aafe39792ae41e2ce68494bcc5a3d18115ce1dce5fd9c2f9a8ebce
MD5 06f52686520a301cc704474358edb33c
BLAKE2b-256 caa3d9ddeab4a457c8b69c99fa7d4838bf051292edac964545012c5a1fbba8ee

See more details on using hashes here.

Provenance

The following attestation bundles were made for md2speech-0.1.3-py3-none-any.whl:

Publisher: release.yml on evelasko/md2speech

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page