Views
No views yet
Supertone/supertonic-2. Weights are unmodified; this repo packages the same parameters into
loom.cpp's GGUF format.en, ko, es, pt, frloom-py-rt on PyPI:pip install -U "loom-py-rt[hub]"1import loom
2
3model = loom.Model.from_pretrained("loom-ai-org/supertonic-2-loom")
4
5# This model encodes text itself -- no phonemiser needed at all.
6print(model.tokenizer) # kind, vocabulary size, default language
7
8# sample_rate=44100: a rate is not something a checkpoint necessarily carries, so it is a value
9# you have to know from the model's documentation and pass. It is used only if the GGUF declares none;
10# a wrong rate does not fail, it plays the voice at the wrong speed.
11audio = model.text2speech.infer("hello world", sample_rate=44100)
12audio.save("out.wav")
13
14# That uses whatever voice the file itself defaults to. See below for choosing another.F1) and uses it whenever no style is passed. Nine more ship in this repo
under voice_styles/:1import json
2from huggingface_hub import hf_hub_download
3
4path = hf_hub_download("loom-ai-org/supertonic-2-loom", filename="voice_styles/M1.json")
5style = json.load(open(path))
6
7# Each file holds two embeddings, stored with a leading batch axis: style_ttl is (1, 50, 256) and
8# style_dp is (1, 8, 16). `infer` takes them flat, so drop the batch axis and concatenate the rows.
9flatten = lambda entry: [v for row in entry["data"][0] for v in row]
10style_ttl = flatten(style["style_ttl"]) # 50 * 256 = 12800 floats
11style_dp = flatten(style["style_dp"]) # 8 * 16 = 128 floats
12
13# A specific voice is a knob the high-level door does not name, so this goes through `infer`.
14txt_ids = model.tokenize("hello world")
15audio = model.infer(txt_ids=txt_ids, style_ttl=style_ttl, style_dp=style_dp, n_steps=4, seed=1234)numpy.asarray(...).ravel() works equally well if numpy is already around.model.infer(...)
passes your arguments straight to the driver this GGUF embeds -- which is where you go for a knob the
door does not name.model.driver_source prints that driver, including a header comment documenting every argument it
accepts for this model, and is the authority on it. See loom-py for the API and
loom.cpp for what the engine does between the two.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.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.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.