Danish multi-task text-to-speech. A 335M
LlamaForCausalLM that autoregressively
predicts
Kanade 25 Hz
audio tokens from Danish BPE text and decodes them to 24 kHz speech. A 128-d
speaker embedding — extracted from any reference clip with the Kanade encoder —
is projected and prepended to the sequence, so every generation is voiced.
Voice-reference is the only control that swaps the speaker embedding (to the
reference clip's); the others keep the target voice. Edit composes with
pronunciation only. All combinations are trained, not emergent.
The easiest path is the
plapre library,
which wraps every task and combination:
1from plapre import Plapre
2
3tts = Plapre("syvai/plapre-nano-v2")
4
5# plain TTS
6tts.speak("Hej, hvordan har du det?", output="out.wav", split_sentences=True)
7
8# clone a voice from any clip
9tts.clone("Denne sætning har stemmen aldrig sagt.", reference_wav="voice.wav")
10
11# cloned voice + set pace + pinned pronunciation, in one call
12tts.clone(
13 "Mette Frederiksen mødte Volodymyr Zelenskyj i København.",
14 reference_wav="voice.wav",
15 durations=[14, 22, 12, 24, 4, 18], # one frame count per word
16 pronunciations=[("Zelenskyj", "zelenskyj_ref.wav")],
17)
18
19# continue a conversation with matching prosody
20tts.continue_context("Og det er derfor, vi handler nu.",
21 prev_text="Situationen har ændret sig markant.",
22 prev_wav="previous_line.wav", speaker_wav="voice.wav")
23
24# edit a recording: replace words in place
25tts.edit("… ny formulering her …", mask_start=40, mask_end=55,
26 original_wav="clip.wav")
1import numpy as np, torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3from huggingface_hub import hf_hub_download
4
5CKPT = "syvai/plapre-nano-v2"
6tok = AutoTokenizer.from_pretrained(CKPT)
7m = AutoModelForCausalLM.from_pretrained(CKPT, torch_dtype=torch.float32).eval()
8spj = torch.nn.Linear(128, m.config.hidden_size)
9spj.load_state_dict(torch.load(hf_hub_download(CKPT, "speaker_proj.pt"), map_location="cpu"))
10spj.eval()
11
12g = tok.convert_tokens_to_ids
13STOPS = [g("</audio>"), tok.eos_token_id] # stop on BOTH terminators
14pre = [g("<text>")] + tok.encode(text, add_special_tokens=False) + [g("<audio>")]
15
16pe = m.get_input_embeddings()(torch.tensor(pre))
17spk = spj(torch.tensor(np.asarray(speaker_embedding), dtype=torch.float32)).unsqueeze(0)
18inp = torch.cat([spk, pe], 0).unsqueeze(0)
19out = m.generate(inputs_embeds=inp,
20 attention_mask=torch.ones(inp.shape[:2], dtype=torch.long),
21 max_new_tokens=500, do_sample=True, temperature=0.7,
22 top_p=0.95, top_k=50, eos_token_id=STOPS,
23 pad_token_id=tok.eos_token_id)[0].tolist()
24audio_base = g("<audio_0>")
25content = []
26for t in out:
27 if audio_base <= t < audio_base + 12800:
28 content.append(t - audio_base)
29 elif content:
30 break
31# decode `content` with kanade_tokenizer (frothywater/kanade-25hz-clean) -> 24 kHz wav
Control-block prompt formats (reference audio caps,
<dur_j> ids, edit
masking/splicing) are implemented in
plapre/tasks.py
— pure token-layout builders you can read or reuse directly.