Veskslop command wrapper, optional pitch/speed, measured calibration
This commit is contained in:
+75
-30
@@ -51,10 +51,12 @@ these placeholders, splits the string on whitespace, and runs the result directl
|
||||
| `{out}` | Output file path, extension `.wav`. Required. |
|
||||
| `{text_file}` | A UTF-8 text file holding the line. |
|
||||
| `{voice}` | The selected voice ID, or an empty string. |
|
||||
| `{setting}` | Reserved for speed/pitch once Veskslop grows settings support. |
|
||||
| `{speed}` | The rate multiplier, or an empty string when unset. |
|
||||
| `{pitch}` | The pitch offset in semitones, or an empty string when unset. |
|
||||
|
||||
The process also receives `VESKSLOP_TTS_VOICE` in the environment. Write the audio to `{out}` and
|
||||
exit 0. Non-zero exits are errors; stderr is captured into the log message, so keep it short.
|
||||
The process also receives `VESKSLOP_TTS_VOICE`, and `VESKSLOP_TTS_SPEED` / `VESKSLOP_TTS_PITCH`
|
||||
when those are set. Write the audio to `{out}` and exit 0. Non-zero exits are errors; stderr is
|
||||
captured into the log message, so keep it short.
|
||||
|
||||
Command mode is best for fast, stateless engines. It has no shell quoting: paths with spaces and
|
||||
shell tricks cannot work. Use a small wrapper script if you need options.
|
||||
@@ -120,15 +122,25 @@ Recommended HTTP shape (extras are optional; an engine may ignore them):
|
||||
{ "text": "...", "voice": "anna", "speed": 1.15, "pitch": -2 }
|
||||
```
|
||||
|
||||
Recommended command shape: `{setting}` is reserved. Until Veskslop passes it, read defaults from
|
||||
the engine's own config file or environment.
|
||||
Recommended command shape: `{speed}` and `{pitch}` are substituted when set, and the same values
|
||||
are exported as `VESKSLOP_TTS_SPEED` and `VESKSLOP_TTS_PITCH`. Read either; the environment form
|
||||
survives placeholder-free commands.
|
||||
|
||||
### How Veskslop should add first-class settings
|
||||
|
||||
A future change would add `speed` and `pitch` to `tts` preferences, pass them to the provider
|
||||
alongside `voice`, and add two controls to the TTS panel. For the command provider, `{speed}` and
|
||||
`{pitch}` placeholders (or one `{settings_file}` JSON file) would be substituted the same way as
|
||||
`{voice}`. Design the engine API so it works with defaults when these fields are absent.
|
||||
Implemented on the Veskslop side (2026-09-15):
|
||||
|
||||
- `tts` preferences carry `speed` (0.5–2.0) and `pitch` (-12–+12, semitones), both nullable.
|
||||
- `/api/audio/speak` and the MCP `speak` tool accept optional `speed` and `pitch` that override
|
||||
the preferences for that line.
|
||||
- The command provider substitutes `{speed}`/`{pitch}` and exports `VESKSLOP_TTS_SPEED` /
|
||||
`VESKSLOP_TTS_PITCH` only when the value is set.
|
||||
- The HTTP provider adds `speed` and `pitch` to the request body only when set.
|
||||
- The TTS panel has speed and pitch sliders; changes are saved to the daemon preferences.
|
||||
- Command mode advertises voices through `VESKSLOP_TTS_VOICES`
|
||||
(`id`, `id|Name`, `id|Name|gender`, or `id|Name|gender|language`, comma separated). While a
|
||||
custom command is set, the engine's declared voices replace the espeak list, so no misleading
|
||||
espeak entries appear. Reading `/voices` for the HTTP provider is still future work.
|
||||
|
||||
Do not encode settings into the voice string (for example `anna+speed1.2`). It breaks voice lists
|
||||
and is hard to validate.
|
||||
@@ -160,13 +172,17 @@ return the cached file or bytes. This is useful for repeated lines and for expen
|
||||
|
||||
## Veskslop changes needed
|
||||
|
||||
If the engine wants first-class voice lists, speed, or pitch in the Veskslop UI, Veskslop must:
|
||||
Status as of 2026-09-15:
|
||||
|
||||
1. Read the engine's `/voices` endpoint and merge it into `/api/audio/tts/voices`.
|
||||
2. Add `speed` and `pitch` to the `tts` preferences and to the `speak` request.
|
||||
3. Add controls to the TTS panel and pass the values through to the provider.
|
||||
1. `speed` and `pitch` are first-class: preferences, per-request overrides on `/api/audio/speak`
|
||||
and MCP `speak`, env exports and placeholders for the command provider, request fields for the
|
||||
HTTP provider, and two sliders in the TTS panel.
|
||||
2. Command-mode voices come from `VESKSLOP_TTS_VOICES`. Reading the engine's `GET /voices` for the
|
||||
HTTP provider is not implemented yet. Until then, declare voices with the env var or configure
|
||||
the voice inside the wrapper.
|
||||
3. No changes are needed for the audio format; the engine post-processes to the required output.
|
||||
|
||||
Until those exist, use defaults configured in the engine.
|
||||
Until `/voices` is wired, use defaults configured in the engine.
|
||||
|
||||
## Acceptance checklist
|
||||
|
||||
@@ -209,17 +225,19 @@ record what the engine needs to interpret, add, or hide._
|
||||
back to `sam`.
|
||||
- `/voices` (HTTP mode) reports `language: "en"`, gender, and the speed/pitch ranges below.
|
||||
|
||||
### Speed and pitch mapping
|
||||
### Speed and pitch mapping (measured 2026-09-15)
|
||||
|
||||
- SAPI4 uses engine units, not multipliers or semitones. Observed values:
|
||||
- MSTTS Sam: speed 30-450 (default 150), pitch 50-200 (default 100).
|
||||
- Flextalk: speed 50-390, pitch 50-250; the engine reports the **defaults as the
|
||||
maximum**, so the wrapper uses the voice profile from the registry as the natural
|
||||
values (Keith Bell: pitch 27, rate 33; Kerry Watson: pitch 65, rate 32).
|
||||
- Mapping (approximate, stated in `/voices`): `speed 1.0` = the voice's natural rate;
|
||||
0.5-2.0 scales that rate and clamps to the engine range. `pitch` ±12 semitones scales the
|
||||
engine pitch range by about ±25% around the natural value.
|
||||
- Command mode: `{setting}` is unused for now. The wrapper reads `VESKSLOP_TTS_SPEED` and
|
||||
- SAPI4 uses engine units, not multipliers or semitones. Measured on the dev host:
|
||||
- MSTTS Sam: speed 30-450, natural about 150; pitch 50-200 with a real audible effect
|
||||
(F0 about 134 Hz at pitch 50, about 199 Hz at pitch 200 on the test phrase).
|
||||
- Flextalk: speed 50-390, natural about 300 (output with no setting is byte-identical to
|
||||
`-s 300`); **pitch has no effect** (byte-identical output at pitch 100/150/200), so the
|
||||
wrapper ignores `pitch` for Keith Bell and Kerry Watson.
|
||||
- Mapping implemented in `setup/vekslop-tts.sh`: `speed 1.0` = the voice's natural rate
|
||||
(150 MSTTS, 300 Flextalk), scaled by the multiplier and clamped to the engine range.
|
||||
`pitch` for MSTTS maps ±12 semitones to engine units `100 ± 6.25·st`, clamped to 50-200.
|
||||
Pitch is a no-op for Flextalk voices.
|
||||
- `{setting}` is unused; the wrapper reads `VESKSLOP_TTS_SPEED` and
|
||||
`VESKSLOP_TTS_PITCH` from the environment when Veskslop later exports them.
|
||||
|
||||
### Text handling
|
||||
@@ -231,8 +249,12 @@ record what the engine needs to interpret, add, or hide._
|
||||
|
||||
### Transport and latency
|
||||
|
||||
- Command provider (first target): a wrapper script reads `{text_file}`, calls the engine,
|
||||
post-processes, writes `{out}`, and exits non-zero with one short stderr line on failure.
|
||||
- Command provider, implemented as `setup/vekslop-tts.sh`:
|
||||
`VESKSLOP_TTS_CMD="<repo>/setup/vekslop-tts.sh {out} {text_file}"` and
|
||||
`VESKSLOP_TTS_VOICES="$(<repo>/setup/vekslop-tts.sh --voices)"`. The wrapper reads the
|
||||
text file, transliterates UTF-8 to CP1252, starts or reuses an Xvfb when `DISPLAY` is
|
||||
unset, calls the engine, then post-processes (trim, fades, two-pass `loudnorm`, 48 kHz
|
||||
mono s16le). Verified: -17.7 LUFS integrated, -3.0 dBFS true peak.
|
||||
- Measured on the dev host (warm Wine): Sam line about 288 ms wall, Flextalk line about
|
||||
41 ms wall, including process start, synthesis, and file write. Both are inside the
|
||||
500 ms budget.
|
||||
@@ -253,7 +275,30 @@ record what the engine needs to interpret, add, or hide._
|
||||
|
||||
### Extra acceptance items for the engine
|
||||
|
||||
- [ ] Post-processed WAV is 48 kHz mono s16le, peak about -3 dBFS, no clipping, fades present.
|
||||
- [ ] Leading silence under 100 ms.
|
||||
- [ ] Voice ID fallback works; TruVoice entries filtered.
|
||||
- [ ] Latency recorded (Sam about 288 ms, Flextalk about 41 ms on the dev host).
|
||||
- [x] Post-processed WAV is 48 kHz mono s16le, peak about -3 dBFS, no clipping, fades present.
|
||||
- [x] Leading silence under 100 ms.
|
||||
- [x] Voice ID fallback works; TruVoice entries filtered.
|
||||
- [x] Latency recorded (Sam about 288 ms, Flextalk about 41 ms on the dev host).
|
||||
|
||||
## Responses from the Veskslop side
|
||||
|
||||
_Added 2026-09-15._
|
||||
|
||||
- **Env vars and placeholders**: `VESKSLOP_TTS_SPEED` and `VESKSLOP_TTS_PITCH` are exported only
|
||||
when a value is set; `{speed}` and `{pitch}` substitute the same values. Reading the environment
|
||||
is the safer option because it works even when the command has no placeholders.
|
||||
- **Defaults**: a missing value means "engine default". Leave the sliders at 1.00x and 0 st in
|
||||
Veskslop to keep the fields out of the request entirely.
|
||||
- **`loudnorm` caution**: single-pass `loudnorm` can pump or over-amplify noise on short lines.
|
||||
Set the target explicitly (`loudnorm=I=-16:TP=-1.5:LRA=11`) and verify a few lines with
|
||||
`ebur128`. A fixed gain that hits about -3 dBFS peak with a consistent RMS is often steadier for
|
||||
one-line utterances. Ducking lowers the sink to 30%, so keep peaks conservative and loudness
|
||||
even between lines.
|
||||
- **Display**: the daemon does not export `DISPLAY` to TTS children. The wrapper must start or
|
||||
find its own Xvfb and set `DISPLAY` itself, as the project helper already does. Do not rely on
|
||||
the client's display.
|
||||
- **Voice declarations**: list only usable IDs in `VESKSLOP_TTS_VOICES`. Veskslop hides the
|
||||
espeak list while a custom command is set, so a missing declaration means an empty voice list,
|
||||
not a fallback.
|
||||
- **Licensing**: keep the engine DLLs and voice data local and out of any repository; Veskslop
|
||||
never bundles or redistributes them.
|
||||
|
||||
Reference in New Issue
Block a user