The Problem
Deploying high‑quality text‑to‑speech on constrained hardware usually requires a large cloud API or a custom‑built inference stack. Teams that need on‑device voice synthesis must stitch together model loading, phonemisation, and streaming logic themselves, which is error‑prone and hard to benchmark.
What This Does
The repository supplies a ready‑to‑run PyTorch implementation of NeuTTS, a family of compact LLM‑backed TTS models. Core code lives in neutts/neutts.py (model class, loading helpers) and neutts/phonemizers.py (text‑to‑phoneme utilities). Example scripts in examples/ demonstrate three usage patterns:
basic_example.py– single‑utterance inference and audio write‑out.basic_streaming_example.py– chunked streaming inference with a background audio player.onnx_example.py– inference via an exported ONNX model.
All examples import the NeuTTS class from neutts/neutts.py, which internally calls _load_phonemizer, _load_backbone, and _load_codec before exposing infer and infer_stream.
How It Is Wired
Execution starts at the main function in examples/basic_example.py (line 7). The flow is:
main→NeuTTSconstructor (neutts/neutts.py).- Constructor loads three resources via:
_load_phonemizer(callsphonemizers.__init__→phonemize)_load_backbone(loads the LLM checkpoint) *_load_codec(initialises the NeuCodec). maincallsinferon the instance, which routes to_apply_chat_template→_to_phones→_decode._decodeuses_linear_overlap_addto stitch audio frames and returns a NumPy array thatmainwrites tooutput.wav.
The streaming path (examples/basic_streaming_example.py) follows the same start‑up sequence, then calls infer_stream. This invokes _infer_stream_ggml, which again reaches _to_phones and _decode, but processes audio in overlapping chunks and passes raw PCM to audio_player_thread. The only external effect is a file open (open(...)) in basic_streaming_example.py that is not wrapped in a context manager.
The internal call graph contains 37 intra‑repo edges. The hub functions with the widest blast radius are NeuTTS (instantiated in four places) and infer (called from three places). No circular imports are present, simplifying refactoring.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/neutts
cd neutts
# Install dependencies (pyproject.toml declares them)
pip install . # editable install works as well: pip install -e .
# Run the single‑utterance demo
python examples/basic_example.py
# Output audio: output.wav
For streaming:
python examples/basic_streaming_example.py path/to/audio.wav
If you prefer ONNX inference, first export the model (not provided in the repo) and then run:
python examples/onnx_example.py
No environment variables or additional configuration files are required beyond the model checkpoints placed in samples/.
Real‑World Use
A robotics platform can embed NeuTTS by importing neutts.neutts.NeuTTS, loading a quantised checkpoint from samples/, and calling infer_stream on live command text. The streaming example shows how to pipe PCM directly to an audio output thread, enabling low‑latency voice feedback without network dependency.
Code Health & Issues
- High – lockfile missing –
pyproject.tomldeclares dependencies but no lockfile. Reproducible builds require committing a lockfile (e.g.,uv lockorpip freeze > requirements.txt). - Medium – overly permissive GITHUB_TOKEN –
.github/workflows/publish.yamlomits explicitpermissions. Addpermissions: { contents: read }at the workflow top. - Medium – no automated dependency updates – No Dependabot or Renovate config. Add
.github/dependabot.ymlto keep libraries patched. - Medium – no CI dependency‑vulnerability scan – Workflows lack a step such as
dependency-review-action. Insert it onpull_requestevents. - Low – missing job timeout –
publish.yamldoes not settimeout-minutes. Define a reasonable bound (e.g.,timeout-minutes: 30) for each job.
Additional observations: the test suite (tests/test_neutts.py) exercises model loading and inference but only covers two scenarios; expanding coverage would improve confidence. Documentation is limited to a README and a TRAINING.md; a developer guide for extending the phonemizer or codec would aid contributors.
The Bottom Line
NeuTTS provides a functional, on‑device TTS stack with clear entry points and modest code size, making it suitable for embedded applications that can afford a PyTorch runtime. However, the repository lacks reproducible dependency locking and several CI hardening measures, which must be addressed before adopting it in production environments. Engineers comfortable with PyTorch and willing to add the missing safety scaffolding will find this a solid foundation for on‑device voice AI.