Merge the SAM VOX preset tuning and comparison tool
This commit is contained in:
@@ -254,6 +254,9 @@ record what the engine needs to interpret, add, or hide._
|
||||
- Assigned stable IDs: `sam`, `mary`, `mike`, `keith-bell`, `kerry-watson`; variants get
|
||||
`mike-telephone`, `mike-hall`, `mary-stadium`, `mike-space`, and so on. Unknown IDs fall
|
||||
back to `sam`.
|
||||
- `vox` (VOX (Half-Life)) and `hev` (HEV suit) are Keith Bell with a fixed preset profile;
|
||||
selecting one implies `VESKSLOP_TTS_PRESET` (`vox` also enables VOX prosody). Their
|
||||
`defaults=` metadata follows the active preset so the UI snap points match.
|
||||
- `/voices` (HTTP mode) reports `language: "en"`, gender, and the speed/pitch ranges below.
|
||||
|
||||
### Speed and pitch mapping (measured 2026-09-15)
|
||||
@@ -372,15 +375,16 @@ Requirements and scope:
|
||||
The host passes `TTSDATAFLAG_TAGGED`, so the engine honors them. Other voices ignore
|
||||
`VESKSLOP_TTS_VOX`.
|
||||
- Needs `python3` at runtime; the wrapper fails loudly when it is missing.
|
||||
- Mutually exclusive with `VESKSLOP_TTS_WORDS` (VOX wins).
|
||||
- Combines with `VESKSLOP_TTS_WORDS`: word mode wins and VOX prosody is skipped.
|
||||
- Measured on the dev host for "Now approaching Sector C test lab. Please stand clear
|
||||
of the test chamber.": plain 4.70 s with 1 pause, VOX 7.81 s with 8 pauses.
|
||||
|
||||
Other NetTTS features and their relevance:
|
||||
|
||||
- Inline `/rate N`, `/pitch N`, `[[pause N]]`: not ported. Veskslop has speed/pitch
|
||||
controls and the profile window; `[[pause N]]` could be added as a wrapper-level
|
||||
text splitter later (NetTTS supports it only outside VOX mode).
|
||||
- Inline `/rate N`, `/pitch N` are handled by the wrapper at the start of a line
|
||||
(percent, or a multiplier at or below 2.0); they override the env sliders for that
|
||||
line and are not spoken. `[[pause N]]` is not ported; it could be added as a
|
||||
wrapper-level text splitter later.
|
||||
- TCP server with `START`/`STOP` status sockets: not needed; Veskslop owns playback
|
||||
and knows when a line ends.
|
||||
- Winetricks prefix automation (`winxp`, `vcrun6`, `mfc42`, `riched20`): not needed;
|
||||
@@ -398,16 +402,20 @@ Kerry Watson at factory values. The wrapper implements it as a preset:
|
||||
VESKSLOP_TTS_PRESET=vox # implies VOX prosody unless VESKSLOP_TTS_VOX is set
|
||||
VESKSLOP_TTS_PRESET=hev # HEV suit starting point (see note below)
|
||||
|
||||
| Profile value | Factory (Keith) | VOX preset | HEV preset (approx.) |
|
||||
| Profile value | Factory (Keith) | VOX preset (tuned) | HEV preset (approx.) |
|
||||
| --- | --- | --- | --- |
|
||||
| Pitch | 27 | 3 | 15 |
|
||||
| Speaking Rate | 33 | 17 | 24 |
|
||||
| Pitch | 27 | 0 | 15 |
|
||||
| Speaking Rate | 33 | 15 | 24 |
|
||||
| Prominences | 17 | 22 | 22 |
|
||||
| Head Size | 46 | 52 | 52 |
|
||||
| Lowering | 40 | 40 | 40 |
|
||||
| Head Size | 46 | 34 | 52 |
|
||||
| Lowering | 40 | 50 | 40 |
|
||||
| Volume | 100 | 70 | 70 |
|
||||
| Expression | 59 | 59 | 59 |
|
||||
|
||||
The VOX column starts from NetTTS's approximation (pitch 3, rate 17, head 52, lowering
|
||||
40) and is refined against the actual game word assets; see
|
||||
"Tuning against the actual game assets" below.
|
||||
|
||||
- Presets apply only to `keith-bell`; every other voice keeps its defaults.
|
||||
- Precedence: preset < `VESKSLOP_TTS_PROFILE` < `VESKSLOP_TTS_EXPRESSION`.
|
||||
- Equivalent explicit override:
|
||||
@@ -465,16 +473,49 @@ Verified matrix (same line, Keith Bell):
|
||||
| Run | Duration | F0 median |
|
||||
| --- | --- | --- |
|
||||
| factory, no adjustments | 3.35 s | 123 Hz |
|
||||
| VOX preset, no adjustments | 5.83 s | 87 Hz |
|
||||
| VOX preset + `speed=1.0 pitch=0` | 5.83 s, byte-identical | 87 Hz |
|
||||
| VOX preset + `speed=1.3` | 4.02 s | 84 Hz |
|
||||
| VOX preset + `pitch=+3` | 5.83 s | 88 Hz |
|
||||
| HEV preset, no adjustments | 3.79 s | - |
|
||||
| VOX preset, no adjustments | 6.06 s | 82 Hz |
|
||||
| VOX preset + `speed=1.0 pitch=0` | 6.06 s, byte-identical | 82 Hz |
|
||||
| VOX preset + `speed=1.3` | 4.02 s | - |
|
||||
| VOX preset + `pitch=+3` | 6.06 s | 96 Hz |
|
||||
|
||||
Registry inspection after a preset run: 26 values in the Keith speaker key in both
|
||||
hives, `Pitch=0x3`, `Speaking Rate=0x11`, `Gender=0x2`, `Line Mode=0x1`,
|
||||
hives, `Pitch=0x0`, `Speaking Rate=0xf`, `Head Size=0x22`, `Lowering=0x32`,
|
||||
`Gender=0x2`, `Line Mode=0x1`,
|
||||
`Default Speaker={11260610-C84A-11CE-AEC4-0000C0E9CB87}`.
|
||||
|
||||
#### Tuning against the actual game assets (2026-09-22)
|
||||
|
||||
Reference: the word WAVs from `suchipi/half-life-vox-console`
|
||||
(`public/vox/*.wav`, Valve assets - used locally for analysis only, never committed).
|
||||
38 words at 11025 Hz 16-bit, the engine's native rate. `tools/vox_compare.py`
|
||||
reproduces the comparison; it reports the cepstrum F0 median, total audible duration,
|
||||
energy-weighted spectral centroid and the >2 kHz energy share per word set.
|
||||
|
||||
| Metric (38 words) | Game assets | NetTTS approximation | Shipped VOX preset |
|
||||
| --- | --- | --- | --- |
|
||||
| F0 median | 82.3 Hz | 87.5 Hz | 82.3 Hz |
|
||||
| Duration | 23.36 s | 22.28 s | 22.99 s |
|
||||
| Spectral centroid | 515 Hz | 479 Hz | 453 Hz |
|
||||
| >2 kHz energy share | 0.0186 | 0.0137 | 0.0172 |
|
||||
|
||||
Findings:
|
||||
|
||||
- Registry `Pitch 0` is the engine minimum and matches the game's F0; the NetTTS
|
||||
approximation (3) was about 5 Hz high.
|
||||
- `Speaking Rate 15` matches the word durations within about 1.6%; NetTTS's 17 was
|
||||
about 4.6% fast on this corpus.
|
||||
- The game voice is brighter than the approximation. Smaller `Head Size` raises
|
||||
brightness (34 is the engine's effective minimum here; 30 gives identical output) and
|
||||
`Lowering 50` adds a little more, together closing most of the >2 kHz gap
|
||||
(0.0137 -> 0.0172 against 0.0186).
|
||||
- The residual centroid difference is mostly the reference's mid/high-band noise floor
|
||||
(game assets), not voice timbre.
|
||||
- HEV stays a starting point: no published settings and no word-level reference exists.
|
||||
|
||||
Re-run recipe: write the profile to both hives, synthesize the reference word list with
|
||||
`build/sapi4host-winlib.exe -v "Keith Bell" -t <word> -o <dir>/<word>.wav`, then
|
||||
`tools/vox_compare.py <game-assets-dir> <synth-dir>`.
|
||||
|
||||
### Inline rate/pitch controls (engine-side)
|
||||
|
||||
The wrapper consumes NetTTS-style, line-leading controls, so Veskslop and the WebUI do
|
||||
@@ -666,9 +707,15 @@ Inputs: `{out}`, `{text_file}`, optional positional voice/speed/pitch/expression
|
||||
| `VESKSLOP_TTS_PROFILE` | `pitch:27:...`/`key=value,...` overrides; the wrapper writes the full profile (defaults + overrides) to the Wine registry with `wine reg import` before every synthesis. |
|
||||
| `VESKSLOP_TTS_HIGHPASS` | High-pass Hz, 40-400 (default 40). |
|
||||
| `VESKSLOP_TTS_ECHO` | Mono echo 0-100 (`aecho`), default 0. The echo is mixed under the dry voice, so the voice level does not change. |
|
||||
| `VESKSLOP_TTS_ECHO_DELAY_MS` / `VESKSLOP_TTS_ECHO_LEVEL` | Echo delay 10-400 ms (default 60) and repeat level 0-0.95 relative to the dry voice (default 0.85). |
|
||||
| `VESKSLOP_TTS_CRUNCH` | `off` (default) or `hl90`, `hl90hq`, `telephone`, `adpcm`, `crush`: 1990s lossy character, applied last. |
|
||||
| `VESKSLOP_TTS_CRUNCH_BITS` / `VESKSLOP_TTS_CRUNCH_RATE` | Bit depth 1-16 and sample rate 4000-48000 overrides for the crunch stage. |
|
||||
| `VESKSLOP_TTS_STREAM` / `VESKSLOP_TTS_STREAM_GAIN_DB` | `1` = word mode writes the WAV as it goes (no measurement pass); fixed gain in dB for that path. |
|
||||
| `VESKSLOP_TTS_WORDS` / `VESKSLOP_TTS_WORD_GAP_MS` | Word-by-word synthesis with silence gaps (Flextalk only). |
|
||||
|
||||
Placeholders `{profile}`, `{highpass}`, `{echo}`, `{words}`, `{word_gap_ms}` mirror the env vars.
|
||||
Placeholders `{profile}`, `{highpass}`, `{echo}`, `{echo_delay_ms}`, `{echo_level}`,
|
||||
`{words}`, `{word_gap_ms}`, `{crunch}`, `{crunch_bits}`, `{crunch_rate}`, `{stream}`,
|
||||
`{vox}`, `{vox_clean}`, `{preset}` mirror the env vars.
|
||||
`wine regedit` fails headlessly but returns 0: use `wine reg import`. A missing profile value
|
||||
must always write the engine default so no stale registry state survives.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user