File size: 6,221 Bytes
3a61198
 
 
 
a0a4c44
3a61198
 
 
 
 
 
 
 
 
 
 
 
a0a4c44
 
 
 
 
 
 
 
3a61198
 
 
 
 
a0a4c44
 
3a61198
 
 
a0a4c44
3a61198
a0a4c44
 
 
 
 
 
 
 
 
 
ec8c262
 
 
a0a4c44
 
 
 
 
ec8c262
 
 
 
 
 
 
 
 
 
 
 
 
a0a4c44
 
3a61198
a0a4c44
 
3a61198
 
 
a0a4c44
 
3a61198
 
 
df4b178
a0a4c44
df4b178
a0a4c44
 
 
 
 
3a61198
a0a4c44
 
 
 
df4b178
 
 
 
 
 
a0a4c44
 
3a61198
a0a4c44
 
 
 
 
 
 
 
 
 
 
 
 
3a61198
 
 
 
 
 
 
 
 
 
 
a0a4c44
 
 
 
3a61198
 
a0a4c44
 
 
3a61198
 
 
a0a4c44
3a61198
 
 
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
---
license: mit
language:
  - en
  - es
pipeline_tag: text-to-speech
tags:
  - piper
  - vits
  - text-to-speech
  - synaptics
  - torq
  - npu
---

# Piper TTS for Synaptics Torq (SL2619)

Piper (VITS) text-to-speech **split across CPU and NPU** for the Synaptics SL2619
board, in three voices:

| Voice | Language | Sample rate | Speakers | Assets |
|---|---|---|---|---|
| `en_US-libritts_r-medium` (default) | English (US) | 22.05 kHz | 904 | top level |
| `en_US-lessac-low` | English (US) | 16 kHz | 1 | `en_US-lessac-low/` |
| `es_MX-ald-medium` | Spanish (Mexico) | 22.05 kHz | 1 | `es_MX-ald-medium/` |

The VITS graph is cut where the per-phoneme durations are ceiled and summed —
the point at which the output length becomes exact:

```
text --[espeak]--> phoneme ids --> [partA]  (CPU, onnxruntime)  --> z [1,192,F] (+ g [1,512,1])
                                           z (,g) --> [partB]   (NPU, bf16 vmfb) --> audio [F*256]
```

partA holds 85% of the *nodes* (small shape/attention ops) but partB — the
HiFi-GAN vocoder — holds most of the *time* and is pure convolution, which is
what the NPU accelerates. Because partA yields the exact frame count `F`, the
right static vocoder window is known before the vocoder runs. `g` is the speaker
embedding: the single-speaker voices have none, so their vocoder takes `z` alone.

## Performance (SL2619 board)

A 3-sentence sample, text in to all audio synthesized (phonemization included,
model load excluded), median of 5 runs:

| Voice | Audio | CPU only | CPU + NPU | Speedup | First audio (CPU / CPU + NPU) |
|---|---|---|---|---|---|
| `en_US-libritts_r-medium` | 7.70 s | 5.55 s (1.39× RT) | **2.83 s (2.72× RT)** | **1.96×** | 0.95 s / **0.77 s** |
| `en_US-lessac-low` | 9.82 s | 5.25 s (1.87× RT) | **2.51 s (3.92× RT)** | **2.09×** | 0.89 s / **0.68 s** |
| `es_MX-ald-medium` | 12.63 s | 8.66 s (1.46× RT) | **4.07 s (3.10× RT)** | **2.13×** | 1.81 s / **1.38 s** |

"CPU only" is the full Piper voice in onnxruntime (2 threads), one sentence at a
time; "CPU + NPU" is partA on the CPU pipelined with the vocoder on the NPU. The
English and Spanish samples are the same three sentences, translated.

The vocoder alone on the NPU, per window:

| window | libritts / Spanish (22.05 kHz) | lessac-low (16 kHz) |
|---|---|---|
| 1 s | 170 ms (5.9× RT) | 126 ms (7.8× RT) |
| 1.5 s | — | 186 ms (8.1× RT) |
| 2 s | 335 ms (6.0× RT) | 249 ms (8.0× RT) |
| 3 s | — | 365 ms (8.2× RT) |
| 4 s | 688 ms (5.8× RT) | 497 ms (8.0× RT) |
| 5 s | — | 628 ms (8.0× RT) |
| 6 s | 1032 ms (5.8× RT) | 749 ms (8.0× RT) |
| 8 s | 1356 ms (5.9× RT) | 1027 ms (7.8× RT) |

The CPU+NPU pipeline overlaps the halves: the CPU encodes sentence *n+1* while the
NPU vocodes sentence *n* and the speaker plays sentence *n-1*.

Accuracy: each bf16 NPU vocoder window matches the fp32 onnxruntime vocoder on the
same latent at 37.3–40.7 dB SNR (correlation ≥ 0.9999).

## Contents

Per voice (English at the top level, the others under their voice-named folder):

| path | what it is |
|---|---|
| `onnx/partA.onnx` | Text encoder + duration predictor. Runs on the CPU under onnxruntime. |
| `vmfb/partB_static_<N>s.vmfb` | The vocoder compiled for the Torq NPU (NSS-only, bf16), one per window: 1, 2, 4, 6, 8 s (lessac-low adds 1.5, 3, 5 s). |
| `voice/<voice>.onnx.json` | Voice config: phoneme→id map, sample rate, espeak language. |
| `onnx/partB_static_<N>s{.bf16io,}.onnx` | The statically shaped bf16-I/O vocoder each VMFB was compiled from. |

Shared, and English-only extras:

| path | what it is |
|---|---|
| `espeak/phonemizerd` | Small resident espeak-ng phonemizer daemon (aarch64) mirroring libpiper's phonemization. |
| `espeak/espeak-ng-data.tar.gz` | espeak-ng dictionaries; one copy covers every language. |
| `onnx/en_US-libritts_r-medium.onnx` | The original monolithic Piper voice, for reference. |
| `tflite/partB_static_4s.{int8,int16x8}.tflite` | Quantized vocoder conversions (int8 was slower than bf16 on this target; kept for reference). |

The vocoder ships as one VMFB per window because the NPU model is statically
shaped: 1, 2, 4, 6 and 8 seconds of audio, plus 1.5, 3 and 5 s for `en_US-lessac-low`.
Per sentence, pick the smallest window that fits, edge-pad the latent up to it, and
trim the output back to `F × 256` samples. lessac-low's finer windows cut padding
from 26% to 16% of vocoder frames (NPU vocoder time −12.5% on a 34-sentence mix;
1–3% end to end, since that voice is CPU-bound once the vocoder is on the NPU).

## How these were built

Every voice is produced by one command in
[torq-tools](https://github.com/synaptics-torq/torq-tools):

```sh
torq-export-model piper -v es_MX-ald-medium     # any rhasspy/piper-voices key
```

It downloads the voice from `rhasspy/piper-voices` (pinned to `v1.0.0`), splits
it, pins the five vocoder windows (sized from the voice's own sample rate),
converts them to bf16 with bf16 I/O, and compiles each NSS-only
(`--torq-disable-css --torq-disable-host`). The VMFBs here were compiled with the
Torq compiler including the HiFi-GAN vocoder fixes (not yet on main; these builds are about 30% faster on the NPU than the first
release of this repo, with bit-identical output).

## Usage

These files are consumed by the `piper_tts` demo in
[synaptics-torq/torq-examples](https://github.com/synaptics-torq/torq-examples),
which writes a `.wav` and plays it on the board's speaker:

```sh
cd piper_tts
pip install -r requirements.txt
cd .. && python setup_demos.py piper_tts
cd piper_tts
python src/infer.py --text "Hello from the Synaptics board."
python src/infer.py --voice en_US-lessac-low --interactive
python src/infer.py --voice es_MX-ald-medium --text "Buenos días."
```

Only the requested voice is downloaded. The demo reads each window's frame width
from the VMFB signature and each voice's sample rate from its config, so
recompiling with a different set of windows needs no code change.

## Licensing

The Piper voices and models are MIT. `espeak/phonemizerd` links espeak-ng
(**GPLv3**) statically; its source ships with the demo at
`piper_tts/piper_core/phonemizerd.c` in torq-examples, with the build command in
its header comment.