Revert VOX preset to the NetTTS approximation

The tuned values (pitch 0, rate 15, head size 34, lowering 50) matched
the reference metrics better but sound far off the game voice, so restore
the published NetTTS approximation: pitch 3, rate 17, prominences 22,
head size 52, lowering 40, volume 70. Also revert the test expectations,
the published default metadata, and the tuning notes in the docs; keep
tools/vox_compare.py for future experiments.
This commit is contained in:
Fisto
2026-09-22 19:56:22 +02:00
parent de4631fe54
commit c6b5fb71e8
5 changed files with 29 additions and 74 deletions
+3 -5
View File
@@ -93,11 +93,9 @@ A short low-level leading hum (80 ms, 75 Hz, -48 dBFS) primes Discord's audio pi
so the first word is not gated; tune with `VESKSLOP_TTS_HUM_MS` (0 = off), so the first word is not gated; tune with `VESKSLOP_TTS_HUM_MS` (0 = off),
`VESKSLOP_TTS_HUM_HZ`, and `VESKSLOP_TTS_HUM_DB`. `VESKSLOP_TTS_HUM_HZ`, and `VESKSLOP_TTS_HUM_DB`.
**Half-Life presets** (Keith Bell): `VESKSLOP_TTS_PRESET=vox` applies the Valve VOX **Half-Life presets** (Keith Bell): `VESKSLOP_TTS_PRESET=vox` applies the approximated
profile (pitch 0, rate 15, head size 34, lowering 50) and enables VOX prosody; Valve VOX profile and enables VOX prosody; `VESKSLOP_TTS_PRESET=hev` is a starting point
`VESKSLOP_TTS_PRESET=hev` is a starting point for the HEV suit voice. The `vox` and `hev` for the HEV suit voice. The `vox` and `hev` voice IDs select these directly. Values are in
voice IDs select these directly. The VOX values were
tuned against the game's word assets with `tools/vox_compare.py`; details in
`docs/TTS-DESIGN.md`. `docs/TTS-DESIGN.md`.
`VESKSLOP_TTS_CRUNCH` (`hl90`, `hl90hq`, `telephone`, `adpcm`, `crush`) adds 1990s lossy `VESKSLOP_TTS_CRUNCH` (`hl90`, `hl90hq`, `telephone`, `adpcm`, `crush`) adds 1990s lossy
+4 -8
View File
@@ -129,14 +129,10 @@ without them.
- VOX mode is Flextalk-only. The wrapper enables it with `VESKSLOP_TTS_VOX=1` - VOX mode is Flextalk-only. The wrapper enables it with `VESKSLOP_TTS_VOX=1`
(`VESKSLOP_TTS_VOX_CLEAN=1` drops the stutter wrapper); word mode (`VESKSLOP_TTS_VOX_CLEAN=1` drops the stutter wrapper); word mode
(`VESKSLOP_TTS_WORDS=1`) wins and skips VOX prosody. (`VESKSLOP_TTS_WORDS=1`) wins and skips VOX prosody.
- Half-Life presets (`VESKSLOP_TTS_PRESET=vox|hev`) target Keith Bell only. VOX starts - Half-Life presets (`VESKSLOP_TTS_PRESET=vox|hev`) target Keith Bell only. VOX values
from NetTTS `nettts_flextalk_vox_profile.reg` (pitch 3, rate 17, head 52, lowering 40) come from NetTTS `nettts_flextalk_vox_profile.reg`: pitch 3, rate 17, prominences 22,
and is tuned against the game's word assets from `suchipi/half-life-vox-console`: head size 52, volume 70 (factory: 27/33/17/46/100); `vox` also implies VOX prosody.
shipped values are pitch 0, rate 15, prominences 22, head size 34, lowering 50, `hev` (pitch 15, rate 24) is an unverified starting point - HL1 credits the suit
volume 70. Measured on 38 words vs the game assets: F0 82.3 vs 82.3 Hz, duration
22.99 vs 23.36 s, >2 kHz share 0.0172 vs 0.0186 (old approximation: 87.5 Hz /
22.28 s / 0.0137). `tools/vox_compare.py` reproduces the comparison. `hev`
(pitch 15, rate 24) remains an unverified starting point - HL1 credits the suit
sounds to Kelly Bailey with no published settings. sounds to Kelly Bailey with no published settings.
- Flextalk rate/pitch are applied as **vendor tags** (`\!R` = duration factor, - Flextalk rate/pitch are applied as **vendor tags** (`\!R` = duration factor,
`\!%` = pitch multiplier), never `-s`/`-p`, because ITTSAttributes override the `\!%` = pitch multiplier), never `-s`/`-p`, because ITTSAttributes override the
+11 -48
View File
@@ -402,20 +402,16 @@ 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=vox # implies VOX prosody unless VESKSLOP_TTS_VOX is set
VESKSLOP_TTS_PRESET=hev # HEV suit starting point (see note below) VESKSLOP_TTS_PRESET=hev # HEV suit starting point (see note below)
| Profile value | Factory (Keith) | VOX preset (tuned) | HEV preset (approx.) | | Profile value | Factory (Keith) | VOX preset | HEV preset (approx.) |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| Pitch | 27 | 0 | 15 | | Pitch | 27 | 3 | 15 |
| Speaking Rate | 33 | 15 | 24 | | Speaking Rate | 33 | 17 | 24 |
| Prominences | 17 | 22 | 22 | | Prominences | 17 | 22 | 22 |
| Head Size | 46 | 34 | 52 | | Head Size | 46 | 52 | 52 |
| Lowering | 40 | 50 | 40 | | Lowering | 40 | 40 | 40 |
| Volume | 100 | 70 | 70 | | Volume | 100 | 70 | 70 |
| Expression | 59 | 59 | 59 | | 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. - Presets apply only to `keith-bell`; every other voice keeps its defaults.
- Precedence: preset < `VESKSLOP_TTS_PROFILE` < `VESKSLOP_TTS_EXPRESSION`. - Precedence: preset < `VESKSLOP_TTS_PROFILE` < `VESKSLOP_TTS_EXPRESSION`.
- Equivalent explicit override: - Equivalent explicit override:
@@ -473,49 +469,16 @@ Verified matrix (same line, Keith Bell):
| Run | Duration | F0 median | | Run | Duration | F0 median |
| --- | --- | --- | | --- | --- | --- |
| factory, no adjustments | 3.35 s | 123 Hz | | factory, no adjustments | 3.35 s | 123 Hz |
| VOX preset, no adjustments | 6.06 s | 82 Hz | | VOX preset, no adjustments | 5.83 s | 87 Hz |
| VOX preset + `speed=1.0 pitch=0` | 6.06 s, byte-identical | 82 Hz | | VOX preset + `speed=1.0 pitch=0` | 5.83 s, byte-identical | 87 Hz |
| VOX preset + `speed=1.3` | 4.02 s | - | | VOX preset + `speed=1.3` | 4.02 s | 84 Hz |
| VOX preset + `pitch=+3` | 6.06 s | 96 Hz | | VOX preset + `pitch=+3` | 5.83 s | 88 Hz |
| HEV preset, no adjustments | 3.79 s | - |
Registry inspection after a preset run: 26 values in the Keith speaker key in both Registry inspection after a preset run: 26 values in the Keith speaker key in both
hives, `Pitch=0x0`, `Speaking Rate=0xf`, `Head Size=0x22`, `Lowering=0x32`, hives, `Pitch=0x3`, `Speaking Rate=0x11`, `Gender=0x2`, `Line Mode=0x1`,
`Gender=0x2`, `Line Mode=0x1`,
`Default Speaker={11260610-C84A-11CE-AEC4-0000C0E9CB87}`. `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) ### Inline rate/pitch controls (engine-side)
The wrapper consumes NetTTS-style, line-leading controls, so Veskslop and the WebUI do The wrapper consumes NetTTS-style, line-leading controls, so Veskslop and the WebUI do
+4 -4
View File
@@ -94,8 +94,8 @@ check "vox preset implies VOX parser" "1" "$(grep -c 'wH1' "$STUB_LOG")"
# The preset writes the full audible profile to both hives. # The preset writes the full audible profile to both hives.
VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_PRESET=vox run_wrapper VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_PRESET=vox run_wrapper
check "preset writes both hives" "2" "$(grep -c 'Speakers\\{11260610' "$STUB_REG")" check "preset writes both hives" "2" "$(grep -c 'Speakers\\{11260610' "$STUB_REG")"
check "preset pitch (both hives)" "2" "$(grep -c '"Pitch"=dword:00000000' "$STUB_REG")" check "preset pitch (both hives)" "2" "$(grep -c '"Pitch"=dword:00000003' "$STUB_REG")"
check "preset rate (both hives)" "2" "$(grep -c '"Speaking Rate"=dword:0000000f' "$STUB_REG")" check "preset rate (both hives)" "2" "$(grep -c '"Speaking Rate"=dword:00000011' "$STUB_REG")"
check "preset gender and line mode" "4" "$(grep -cE '"Gender"=dword:00000002|"Line Mode"=dword:00000001' "$STUB_REG")" check "preset gender and line mode" "4" "$(grep -cE '"Gender"=dword:00000002|"Line Mode"=dword:00000001' "$STUB_REG")"
check "preset default speaker" "2" "$(grep -c '"Default Speaker"' "$STUB_REG")" check "preset default speaker" "2" "$(grep -c '"Default Speaker"' "$STUB_REG")"
@@ -111,7 +111,7 @@ check "word mode with preset synthesizes every word" "3" "$(grep -c '^ARGV:' "$S
check "word mode skips the VOX parser" "0" "$(grep -c 'wH1' "$STUB_LOG")" check "word mode skips the VOX parser" "0" "$(grep -c 'wH1' "$STUB_LOG")"
check "word mode tags every word" "3" \ check "word mode tags every word" "3" \
"$(sed -n 's/.*\[-t\] \[\(.*\)\] \[-o\].*/\1/p' "$STUB_LOG" | grep -c '^\\!R0.80 ')" "$(sed -n 's/.*\[-t\] \[\(.*\)\] \[-o\].*/\1/p' "$STUB_LOG" | grep -c '^\\!R0.80 ')"
check "word mode keeps the preset profile" "2" "$(grep -c '"Pitch"=dword:00000000' "$STUB_REG")" check "word mode keeps the preset profile" "2" "$(grep -c '"Pitch"=dword:00000003' "$STUB_REG")"
check "word mode emits no attributes" "0" "$(attr_count)" check "word mode emits no attributes" "0" "$(attr_count)"
# The vox preset implies VOX when the variable is unset, but word mode still wins. # The vox preset implies VOX when the variable is unset, but word mode still wins.
@@ -139,7 +139,7 @@ check "stream has a gap per word break" "2" \
# The Half-Life voices carry their profile preset. # The Half-Life voices carry their profile preset.
VESKSLOP_TTS_VOICE=vox VESKSLOP_TTS_VOX=1 run_wrapper VESKSLOP_TTS_VOICE=vox VESKSLOP_TTS_VOX=1 run_wrapper
check "vox voice writes the VOX profile (both hives)" "2" "$(grep -c '"Pitch"=dword:00000000' "$STUB_REG")" check "vox voice writes the VOX profile (both hives)" "2" "$(grep -c '"Pitch"=dword:00000003' "$STUB_REG")"
check "vox voice runs VOX prosody" "1" "$(grep -c 'wH1' "$STUB_LOG")" check "vox voice runs VOX prosody" "1" "$(grep -c 'wH1' "$STUB_LOG")"
VESKSLOP_TTS_VOICE=hev run_wrapper VESKSLOP_TTS_VOICE=hev run_wrapper
check "hev voice writes the HEV profile (both hives)" "2" "$(grep -c '"Pitch"=dword:0000000f' "$STUB_REG")" check "hev voice writes the HEV profile (both hives)" "2" "$(grep -c '"Pitch"=dword:0000000f' "$STUB_REG")"
+7 -9
View File
@@ -46,7 +46,7 @@ ROOT=$MICROSLOP_ROOT
# The Keith Bell defaults follow the active preset so the UI's "default" snap points # The Keith Bell defaults follow the active preset so the UI's "default" snap points
# match the values the preset actually writes. # match the values the preset actually writes.
VOX_DEFAULTS="pitch:0:rate:15:prominences:22:head_size:34:lowering:50:expression:59" VOX_DEFAULTS="pitch:3:rate:17:prominences:22:head_size:52:lowering:40:expression:59"
HEV_DEFAULTS="pitch:15:rate:24:prominences:22:head_size:52:lowering:40:expression:59" HEV_DEFAULTS="pitch:15:rate:24:prominences:22:head_size:52:lowering:40:expression:59"
case "${VESKSLOP_TTS_PRESET:-}" in case "${VESKSLOP_TTS_PRESET:-}" in
vox) KB_DEFAULTS="$VOX_DEFAULTS" ;; vox) KB_DEFAULTS="$VOX_DEFAULTS" ;;
@@ -243,17 +243,15 @@ if [ -n "$SPEAKER_GUID" ]; then
P_LOWER=40 P_LOWER=40
P_EXPR="" P_EXPR=""
# Half-Life presets for Keith Bell (the game's announcement voice). "vox" # Half-Life presets for Keith Bell (the game's announcement voice).
# starts from the approximated Valve VOX profile published by NetTTS # "vox" is the approximated Valve VOX profile published by NetTTS
# (scripts/winetricks/nettts_flextalk_vox_profile.reg), then refines pitch, # (scripts/winetricks/nettts_flextalk_vox_profile.reg); "hev" is a starting
# rate, head size and lowering against the actual game word assets # point for the HEV suit voice, which has no published profile.
# (see docs/TTS-DESIGN.md). "hev" is a starting point for the HEV suit
# voice, which has no published profile.
case "$PRESET" in case "$PRESET" in
vox) vox)
if [ "$ENGVOICE" = "Keith Bell" ]; then if [ "$ENGVOICE" = "Keith Bell" ]; then
P_PITCH=0; P_RATE=15; P_VOLUME=70 P_PITCH=3; P_RATE=17; P_VOLUME=70
P_PROM=22; P_HEAD=34; P_LOWER=50 P_PROM=22; P_HEAD=52; P_LOWER=40
fi fi
;; ;;
hev) hev)