File size: 5,276 Bytes
1f8f96b
 
 
 
835224a
e4674db
 
 
835224a
 
0f0cdf5
835224a
 
 
 
 
 
 
 
 
 
 
 
97c326a
835224a
 
 
 
d520a27
 
835224a
244cd20
0f0cdf5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
---
license: openrail
base_model:
- Supertone/supertonic
library_name: transformers.js
language:
- en
pipeline_tag: text-to-speech
---

## Usage

### Transformers.js

If you haven't already, you can install the [Transformers.js](https://huggingface.co/docs/transformers.js) JavaScript library from [NPM](https://www.npmjs.com/package/@huggingface/transformers) using:
```bash
npm i @huggingface/transformers
```

You can then generate audio as follows:
```js
import { pipeline } from '@huggingface/transformers';

const tts = await pipeline('text-to-speech', 'onnx-community/Supertonic-TTS-ONNX');

const input_text = 'This is really cool!';
const audio = await tts(input_text, {
    speaker_embeddings: 'https://huggingface.co/onnx-community/Supertonic-TTS-ONNX/resolve/main/voices/F1.bin',
    num_inference_steps: 5, // Higher = better quality (typically 1-50)
    speed: 1.05, // Higher = faster speech (typically 0.8-1.2)
});
await audio.save('output.wav'); // or `audio.toBlob()`;
```

### ONNXRuntime

First, let's create a helper class, `SupertonicTTS`:

```py
import os
import numpy as np
import onnxruntime as ort
from transformers import AutoTokenizer

class SupertonicTTS:
    SAMPLE_RATE = 44100
    CHUNK_COMPRESS_FACTOR = 6
    BASE_CHUNK_SIZE = 512
    LATENT_DIM = 24
    STYLE_DIM = 128
    LATENT_SIZE = BASE_CHUNK_SIZE * CHUNK_COMPRESS_FACTOR

    def __init__(self, model_path):
        self.model_path = model_path
        self.tokenizer = AutoTokenizer.from_pretrained(self.model_path)

        # Load ONNX sessions
        self.text_encoder = ort.InferenceSession(os.path.join(self.model_path, "onnx", "text_encoder.onnx"))
        self.latent_denoiser = ort.InferenceSession(os.path.join(self.model_path, "onnx", "latent_denoiser.onnx"))
        self.voice_decoder = ort.InferenceSession(os.path.join(self.model_path, "onnx", "voice_decoder.onnx"))

    def _load_style(self, voice: str) -> np.ndarray:
        voice_path = os.path.join(self.model_path, "voices", f"{voice}.bin")
        if not os.path.exists(voice_path):
            raise ValueError(f"Voice '{voice}' not found.")

        style_vec = np.fromfile(voice_path, dtype=np.float32)
        return style_vec.reshape(1, -1, self.STYLE_DIM)

    def generate(self, text: list[str], *, voice: str = "M1", speed: float = 1.0, steps: int = 5) -> list[np.ndarray]:
        # 1. Prepare Text Inputs
        inputs = self.tokenizer(text, return_tensors="np", padding=True, truncation=True)
        input_ids = inputs["input_ids"]
        attn_mask = inputs["attention_mask"]
        batch_size = input_ids.shape[0]

        # 2. Prepare Style
        style = self._load_style(voice).repeat(batch_size, axis=0)

        # 3. Text Encoding
        last_hidden_state, raw_durations = self.text_encoder.run(
            None, 
            {"input_ids": input_ids, "attention_mask": attn_mask, "style": style}
        )
        durations = (raw_durations / speed * self.SAMPLE_RATE).astype(np.int64)

        # 4. Latent Preparation
        latent_lengths = (durations + self.LATENT_SIZE - 1) // self.LATENT_SIZE
        max_len = latent_lengths.max()
        latent_mask = (np.arange(max_len) < latent_lengths[:, None]).astype(np.int64)
        latents = np.random.randn(batch_size, self.LATENT_DIM * self.CHUNK_COMPRESS_FACTOR, max_len).astype(np.float32)
        latents *= latent_mask[:, None, :]

        # 5. Denoising Loop
        num_inference_steps = np.full(batch_size, steps, dtype=np.float32)
        for step in range(steps):
            timestep = np.full(batch_size, step, dtype=np.float32)
            latents = self.latent_denoiser.run(
                None,
                {
                    "noisy_latents": latents,
                    "latent_mask": latent_mask,
                    "style": style,
                    "encoder_outputs": last_hidden_state,
                    "attention_mask": attn_mask,
                    "timestep": timestep,
                    "num_inference_steps": num_inference_steps,
                },
            )[0]

        # 6. Decode Latents to Audio
        waveforms = self.voice_decoder.run(None, {"latents": latents})[0]

        # 7. Post-process: Trim padding and return list of arrays
        results = []
        for i, length in enumerate(latent_mask.sum(axis=1) * self.LATENT_SIZE):
            results.append(waveforms[i, :length])

        return results
```

Next, we clone this repository (using whichever way you want, `git clone`, `huggingface_hub`, etc.)
```py
# (Optional) Download model files (or use existing local directory)
from huggingface_hub import snapshot_download
model_id = "onnx-community/Supertonic-TTS-ONNX"
local_dir = "supertonic"
snapshot_download(model_id, local_dir=local_dir)
```

We can then use the model as follows:

```py
# Initialize TTS
tts = SupertonicTTS(local_dir)

# Generate audio
prompts = [
    "Once upon a time, there was a brave knight.",
    "Refactoring code makes it much easier to read!",
    "I love this!"
]
audio_data = tts.generate(prompts, voice="M1", speed=1.0, steps=10)

# (Optional) Save to files
import soundfile as sf
for i, audio in enumerate(audio_data):
    filename = f"output_{i}.wav"
    sf.write(filename, audio, tts.SAMPLE_RATE)
    print(f"Saved {filename}")
```