Supertonic 2

Supertone's Supertonic 2 on-device TTS model, exported for loom.cpp. Encodes text itself -- no external phonemiser needed.

This is a loom.cpp export: a single self-describing GGUF that carries its own graph topologies, tokenizer (if any) and driver script, produced by loom-exporter.

Original model

Exported from Supertone/supertonic-2. Weights are unmodified; this repo packages the same parameters into loom.cpp's GGUF format.

License

OpenRAIL-M -- inherited from the base model above.

Language(s)

en, ko, es, pt, fr

Usage

Run it with loom-py -- loom-py-rt on PyPI:

pip install -U loom-py-rt[hub]
import loom

model = loom.Model.from_pretrained("loom-ai-org/supertonic-2-loom")

# This model encodes text itself -- no external phonemiser needed.
print(model.tokenizer)                       # kind, vocabulary size, default language
txt_ids = model.tokenize("hello world")      # model.tokenize(..., lang="ko") to pick a language

# Any length up to `model.hparam("txt_len")` -- the driver pads and masks the rest.
audio = model.infer(txt_ids=txt_ids, n_steps=4, seed=1234)

# That uses whatever voice the file itself defaults to. See below for choosing another.

Saving the audio

infer returns the waveform as a plain list of floats in [-1, 1] at 44.1 kHz. Writing it to a .wav is a scale and a write -- numpy and scipy are not loom dependencies, they are just the shortest way to say it:

import numpy as np
import scipy.io.wavfile as wavfile

sample_rate = 44100                                    # Supertonic 2's output rate

# 16-bit PCM is the usual container format, so scale the floats to its integer range.
audio_int16 = (np.asarray(audio, dtype=np.float32) * 32767).astype(np.int16)
wavfile.write("output.wav", sample_rate, audio_int16)

Choosing a voice

This file embeds one voice (F1) and uses it whenever no style is passed. Nine more ship in this repo under voice_styles/:

import json
from huggingface_hub import hf_hub_download

path = hf_hub_download("loom-ai-org/supertonic-2-loom", filename="voice_styles/M1.json")
style = json.load(open(path))

# Each file holds two embeddings, stored with a leading batch axis: style_ttl is (1, 50, 256) and
# style_dp is (1, 8, 16). `infer` takes them flat, so drop the batch axis and concatenate the rows.
flatten = lambda entry: [v for row in entry["data"][0] for v in row]
style_ttl = flatten(style["style_ttl"])   # 50 * 256 = 12800 floats
style_dp = flatten(style["style_dp"])     #  8 *  16 =   128 floats

audio = model.infer(txt_ids=txt_ids, style_ttl=style_ttl, style_dp=style_dp, n_steps=4, seed=1234)

The two arguments travel together: pass neither for the built-in voice, or both to select another. A different voice predicts a different duration, so the waveform generally changes length as well as timbre.

Plain lists are fine -- this package has no runtime dependencies and accepts any sequence of floats, so numpy.asarray(...).ravel() works equally well if numpy is already around.

model.driver_source prints the exact driver script this GGUF embeds, including a header comment documenting every argument model.infer()/model.generate() accepts for this model.

Known limitations

One synthesis call carries at most model.hparam("txt_len") ids -- 512 in this export, roughly 490 characters once the <lang>...</lang> wrap and the inserted final period are counted, so a short paragraph. Anything shorter is padded and masked by the driver, so any count up to the ceiling synthesizes correctly; anything longer has to be split by the caller, and this export deliberately does not do that for you (where a sentence may be broken is a text-domain decision, not a model contract). The text length is fixed rather than dynamic for two independent reasons -- a single dynamic-length symbol per graph, and a relative-position windowing step that cannot be traced dynamically -- so the graphs are traced at several widths and the driver runs the smallest that fits your text. Short text therefore does not pay for the ceiling.

One voice is built in. infer uses it when you pass no style, and takes any other voice as a style_ttl/style_dp pair. What this export does not carry is the two style encoders, so it cannot derive a style from your own audio -- cloning a new voice needs the upstream checkpoint. Selecting among existing voices does not.

Files

  • supertonic-2.gguf -- the model, exported with loom-exporter.
  • voice_styles/*.json -- ten precomputed voices (F1-F5, M1-M5), copied unmodified from the upstream checkpoint. F1 is also embedded in the GGUF as the default, so these are only needed to select a different voice. See the usage example above.
Downloads last month
68
GGUF
Model size
66.6M params
Architecture
loom-supertonic_mil
Hardware compatibility
Log In to add your hardware

We're not able to determine the quantization variants.

Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for loom-ai-org/supertonic-2-loom

Quantized
(3)
this model

Collection including loom-ai-org/supertonic-2-loom