Library / SDK
samuel-vitorino/sopro avatar
samuel-vitorino/sopro

Sopro: a 120M parameter text-to-speech model built for streaming and voice cloning

A lightweight text-to-speech model with zero-shot voice cloning

977 stars44 forksPythonApache-2.0

At a glance

What is it?
Sopro ships a 120M parameter open text-to-speech model with zero-shot voice cloning from 5 to 20 seconds of reference audio, a streaming path measured in hundreds of milliseconds to first audio, and an ONNX build that runs in the browser. The text frontend is minimal, mixed-language input is a known weak spot, and no training code is on the way.
Who is it for?
Adopt Sopro for on-device or in-browser text-to-speech, for dubbing and voice prototyping where a short reference clip is available, and for latency-sensitive streaming where the offline path is not an option. Skip it if you need precise pronunciation of arbitrary text, if your inputs mix languages inside sentences, or if you need to fine-tune the model yourself, because the training code is not planned for release.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 53 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A 120M parameter model sized for a laptop and a browser tab

Sopro is a lightweight voice-cloning text-to-speech model family, named after the Portuguese word for breath or blow. This repository ships sopro-v2-turbo, a 120M parameter open model.

The size is the point. A model at this scale streams, runs comfortably on a laptop CPU or in the browser, and the README states that it reaches state-of-the-art-level intelligibility against much larger systems. The claimed latency is roughly 300 ms time-to-first-audio on a laptop CPU, with real-time factor figures of 0.24 offline and 0.21 streaming on an M3 CPU, and 0.07 on an H100.

The audience is developers who need speech generation to run somewhere close to the user, on a device without a GPU, or entirely client-side in a web page. The alternatives that dominate the field are large enough that they need a server, and that constraint is what Sopro is designed around.

Language coverage is stated as English, European Portuguese, French and German. Voice cloning is zero-shot from 5 to 20 seconds of reference audio, which means no fine-tuning and no enrolment step. The full evaluations and audio samples live in a linked blog post rather than in the repository, so the README gives you the headline numbers without the measurement setup behind them.

Streaming and offline paths that are not bit-exact

Sopro exposes two synthesis paths, and the relationship between them is the most important technical detail in the documentation.

The streaming path uses chunked attention and a causal vocoder, and it is not bit-exact with the offline path. The project states plainly that the offline path should be preferred for best quality. Streaming buys time-to-first-audio, and it pays for that with a different output, which matters if you are comparing waveforms or running regression tests on generated audio.

The Python interface makes the streaming shape explicit. The non-streaming call returns a waveform in one piece:

python
from sopro import SoproTTS

tts = SoproTTS.from_pretrained("samuel-vitorino/sopro-v2-turbo", device="cpu")

wav = tts.synthesize(
    "Hello! This is a non-streaming Sopro TTS example.",
    ref_audio_path="ref.wav",
)

tts.save_wav("out.wav", wav)

The streaming call is an iterator over chunks, which you concatenate before saving:

python
import torch
from sopro import SoproTTS

tts = SoproTTS.from_pretrained("samuel-vitorino/sopro-v2-turbo", device="cpu")

chunks = []
for chunk in tts.stream(
    "Hello! This is a streaming Sopro TTS example.",
    ref_audio_path="ref.mp3",
):
    chunks.append(chunk.cpu())

wav = torch.cat(chunks, dim=-1)
tts.save_wav("out_stream.wav", wav)

The chunk_frames parameter defaults to 64 and must be at least 64. Larger values such as 128 or 256 are supported and reduce call frequency at the cost of later audio emission. That is a direct latency versus overhead dial: bigger chunks mean fewer crossings into the model, and the first audio arrives later.

Preparing the reference once to cut time to first audio

Because the reference audio has to be turned into a conditioning representation before synthesis can begin, that work can be done ahead of time and reused.

The streaming example with a prepared reference looks like this:

python
import torch
from sopro import SoproTTS

tts = SoproTTS.from_pretrained("samuel-vitorino/sopro-v2-turbo", device="cpu")

ref = tts.prepare_reference(ref_audio_path="ref.mp3", stream=True)

chunks = []
for chunk in tts.stream(
    "Hello! This is a streaming Sopro TTS example.",
    ref=ref,
):
    chunks.append(chunk.cpu())

wav = torch.cat(chunks, dim=-1)
tts.save_wav("out_stream.wav", wav)

The stream=True argument marks the reference as prepared for the streaming path, which matters because the streaming and offline paths do not produce identical output.

This is the pattern for a voice assistant or a live captioning service. A user speaks once, the reference is encoded, and every subsequent utterance reuses that encoding instead of paying for it again. The zero-shot framing means the encoding is derived from 5 to 20 seconds of audio rather than from a training session, so the setup cost per voice is one short clip.

Installing and serving the local demo

The quickest way to hear the model is a single command that runs the package without installing it into your environment:

bash
uvx --from sopro soprotts serve

If Sopro is already installed, the same thing is:

bash
soprotts serve

Then you open http://localhost:7860 in a browser. The model downloads on first use and stays in the Hugging Face cache, so the first run is the slow one and later runs start from cache.

For a normal project install, the package comes from PyPI:

bash
pip install -U sopro

Or from a source checkout:

bash
git clone https://github.com/samuel-vitorino/sopro
cd sopro
pip install -e .

Device handling is automatic, with one caveat that will matter on Apple hardware. Sopro selects CUDA or CPU automatically and defaults to CPU on macOS, so you have to pass --device mps explicitly to use MPS. The serve command also exposes model, device, port and CPU int8 options through soprotts serve --help.

The package itself declares the entry point soprotts = "sopro.cli:main" and requires Python 3.10 or newer, with torch, torchaudio, numpy, safetensors, sentencepiece, soundfile and huggingface_hub as dependencies.

CLI flags that trade speed against quality and pronunciation

The command line interface exposes the knobs that matter, and the documentation is specific about what each one does.

A basic synthesis call names the text, the reference clip and the output file:

bash
soprotts "Sopro is a lightweight 120 million parameter text-to-speech model that streams and runs on device." --ref ref.wav --out out.wav

Adding --stream switches to the streaming path. The expected sampling parameters --temperature, --top-p and --top-k are available, and four more flags control the specifics.

--lang takes en, pt, fr or de. It is optional, and the documentation says it helps pronunciation on ambiguous text, so it is a fallback rather than a requirement for every input.

--steps controls the acoustic solver steps and defaults to 2. If you want to trade speed for quality, 8, 16 or even 32 steps can give higher quality speech on more challenging references. That default is aggressive, and the quality ceiling on hard references is the flag to reach for.

--int8 applies int8 AR weights on CPU, which is the memory and speed lever for constrained machines. --max-seconds caps the length of each generated segment, and long text is split into segments, so the total output length is unbounded. Both together are how a caller keeps a single request bounded without truncating the text.

The browser build and the cost of quantization

There is a second demo that involves no server at all, hosted as a static page, and the project calls it fully in-browser. It uses an ONNX runtime, and the dependency-isolated ONNX runtime and exporter are documented separately in web/README.md.

The npm package is @soprotts/onnx-web, and the compatibility note in the README states that current model artifacts require sopro>=2.2.0 or @soprotts/onnx-web>=0.3.0, with the upgrade given as pip install -U sopro or npm install @soprotts/onnx-web@latest. That version coupling is worth noting, because artifacts and runtime are versioned separately and a mismatch produces confusing failures rather than an obvious error.

Both demos, the Python-backed one and the browser one, use the same frontend in demos/web. The packaging configuration force-includes that frontend into the wheel as sopro/demo, along with the assets directory, so the web demo ships inside the Python package.

The browser path has honest caveats. On mobile the model is quantized, so results can be slightly below the local demo, and devices with low memory may crash. The project frames this as a trade rather than a bug, but it means a mobile user and a desktop user of the same demo are not hearing the same model.

Disclaimers, training data and the licence

The disclaimers section is unusually direct, and each item constrains what you can expect from the model.

There is no watermarking. The reasoning given is that with an open-source inference pipeline it would be trivial to remove, so it would only provide a false sense of safety. The request that follows is to use the model for good and not to impersonate people. For an organisation deploying this, that means the responsibility for consent and disclosure of synthetic voice sits with you, since the model provides nothing to help with it.

The text frontend is deliberately minimal, so some abbreviations, numbers and symbols may not be pronounced correctly. The advice is to prefer words, so 1 + 2 should be written one plus two, while common abbreviations such as CPU or TTS are generally read fine, and you can put a language-specific normalizer in front of the model.

Mixed-language text is described as a weak spot, where an English product name inside a Portuguese sentence can be mispronounced.

On training, the project states that it is not planning to release the training code in the near future because of its complexity. The training data is credited as Emilia YODAS, LibriTTS-R and FalAR. The model is Apache-2.0, with the licence text in LICENSE.txt, and the package version in pyproject.toml is 2.2.0.

On release state, the repository has no GitHub releases, and the last push was on 2026-09-01. Versioning therefore comes from the package index and the compatibility note rather than from tagged artifacts, which is a small but real difference in how upgrades are pinned.

Editorial conclusion

Adopt Sopro for on-device or in-browser text-to-speech, for dubbing and voice prototyping where a short reference clip is available, and for latency-sensitive streaming where the offline path is not an option. Skip it if you need precise pronunciation of arbitrary text, if your inputs mix languages inside sentences, or if you need to fine-tune the model yourself, because the training code is not planned for release. Before adopting, confirm your Python version is 3.10 or newer, read the disclaimers about watermarking and impersonation, and decide deliberately between the streaming path, which is not bit-exact with the offline path, and the offline path, which the project recommends for best quality.

Frequently asked questions

How do I install and run Sopro for text-to-speech?

Install the package from PyPI with pip install -U sopro, or from a checkout with git clone followed by pip install -e .. The quickest way to try it is uvx --from sopro soprotts serve, then open http://localhost:7860. Python 3.10 or newer is required.

How much reference audio does Sopro need for zero-shot voice cloning?

Zero-shot cloning works from 5 to 20 seconds of reference audio, so no fine-tuning or enrolment step is involved. The reference can be prepared once with prepare_reference and reused to reduce time-to-first-audio.

Does Sopro support streaming synthesis and which device does it use?

Yes, streaming is available through the --stream flag on the command line and the stream method in Python, with roughly 300 ms time-to-first-audio on a laptop CPU. Device selection is automatic between CUDA and CPU, but macOS defaults to CPU unless you pass --device mps explicitly.

Which languages does Sopro support?

English, European Portuguese, French and German. The --lang flag accepts en, pt, fr or de and is optional, helping with pronunciation on ambiguous text. Mixed-language sentences are a known weak point of the model.

Can Sopro run in the browser without a server?

Yes, there is a fully in-browser demo built on an ONNX runtime, with the runtime and exporter documented in web/README.md and the npm package @soprotts/onnx-web. Current model artifacts require @soprotts/onnx-web>=0.3.0, and on mobile the model is quantized, which can lower quality and crash low-memory devices.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. Project website
  4. README
  5. samuel-vitorino/sopro on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/samuel-vitorino-sopro.svg)](https://hysenlabs.com/projects/samuel-vitorino-sopro)

Was this useful?