From c6b5fb71e8f05c706054198470aca81026383c8d Mon Sep 17 00:00:00 2001 From: Fisto Date: Tue, 22 Sep 2026 19:56:22 +0200 Subject: [PATCH] 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. --- README.md | 8 +++--- docs/NOTES.md | 12 +++------ docs/TTS-DESIGN.md | 59 ++++++++----------------------------------- setup/test-wrapper.sh | 8 +++--- setup/veskslop-tts.sh | 16 +++++------- 5 files changed, 29 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index 3389bc1..4affa83 100644 --- a/README.md +++ b/README.md @@ -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), `VESKSLOP_TTS_HUM_HZ`, and `VESKSLOP_TTS_HUM_DB`. -**Half-Life presets** (Keith Bell): `VESKSLOP_TTS_PRESET=vox` applies the Valve VOX -profile (pitch 0, rate 15, head size 34, lowering 50) and enables VOX prosody; -`VESKSLOP_TTS_PRESET=hev` is a starting point for the HEV suit voice. The `vox` and `hev` -voice IDs select these directly. The VOX values were -tuned against the game's word assets with `tools/vox_compare.py`; details in +**Half-Life presets** (Keith Bell): `VESKSLOP_TTS_PRESET=vox` applies the approximated +Valve VOX profile and enables VOX prosody; `VESKSLOP_TTS_PRESET=hev` is a starting point +for the HEV suit voice. The `vox` and `hev` voice IDs select these directly. Values are in `docs/TTS-DESIGN.md`. `VESKSLOP_TTS_CRUNCH` (`hl90`, `hl90hq`, `telephone`, `adpcm`, `crush`) adds 1990s lossy diff --git a/docs/NOTES.md b/docs/NOTES.md index a7be815..5946ca1 100644 --- a/docs/NOTES.md +++ b/docs/NOTES.md @@ -129,14 +129,10 @@ without them. - 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_WORDS=1`) wins and skips VOX prosody. -- Half-Life presets (`VESKSLOP_TTS_PRESET=vox|hev`) target Keith Bell only. VOX starts - from NetTTS `nettts_flextalk_vox_profile.reg` (pitch 3, rate 17, head 52, lowering 40) - and is tuned against the game's word assets from `suchipi/half-life-vox-console`: - shipped values are pitch 0, rate 15, prominences 22, head size 34, lowering 50, - 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 +- Half-Life presets (`VESKSLOP_TTS_PRESET=vox|hev`) target Keith Bell only. VOX values + come from NetTTS `nettts_flextalk_vox_profile.reg`: pitch 3, rate 17, prominences 22, + head size 52, volume 70 (factory: 27/33/17/46/100); `vox` also implies VOX prosody. + `hev` (pitch 15, rate 24) is an unverified starting point - HL1 credits the suit sounds to Kelly Bailey with no published settings. - Flextalk rate/pitch are applied as **vendor tags** (`\!R` = duration factor, `\!%` = pitch multiplier), never `-s`/`-p`, because ITTSAttributes override the diff --git a/docs/TTS-DESIGN.md b/docs/TTS-DESIGN.md index 292ad62..780efc3 100644 --- a/docs/TTS-DESIGN.md +++ b/docs/TTS-DESIGN.md @@ -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=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 | -| Speaking Rate | 33 | 15 | 24 | +| Pitch | 27 | 3 | 15 | +| Speaking Rate | 33 | 17 | 24 | | Prominences | 17 | 22 | 22 | -| Head Size | 46 | 34 | 52 | -| Lowering | 40 | 50 | 40 | +| Head Size | 46 | 52 | 52 | +| Lowering | 40 | 40 | 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: @@ -473,49 +469,16 @@ Verified matrix (same line, Keith Bell): | Run | Duration | F0 median | | --- | --- | --- | | factory, no adjustments | 3.35 s | 123 Hz | -| 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 | +| 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 | - | 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`, -`Gender=0x2`, `Line Mode=0x1`, +hives, `Pitch=0x3`, `Speaking Rate=0x11`, `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 -o /.wav`, then -`tools/vox_compare.py `. - ### Inline rate/pitch controls (engine-side) The wrapper consumes NetTTS-style, line-leading controls, so Veskslop and the WebUI do diff --git a/setup/test-wrapper.sh b/setup/test-wrapper.sh index 0144efd..a218309 100755 --- a/setup/test-wrapper.sh +++ b/setup/test-wrapper.sh @@ -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. 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 pitch (both hives)" "2" "$(grep -c '"Pitch"=dword:00000000' "$STUB_REG")" -check "preset rate (both hives)" "2" "$(grep -c '"Speaking Rate"=dword:0000000f' "$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:00000011' "$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")" @@ -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 tags every word" "3" \ "$(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)" # 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. 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")" VESKSLOP_TTS_VOICE=hev run_wrapper check "hev voice writes the HEV profile (both hives)" "2" "$(grep -c '"Pitch"=dword:0000000f' "$STUB_REG")" diff --git a/setup/veskslop-tts.sh b/setup/veskslop-tts.sh index ee6737a..359b885 100755 --- a/setup/veskslop-tts.sh +++ b/setup/veskslop-tts.sh @@ -46,7 +46,7 @@ ROOT=$MICROSLOP_ROOT # The Keith Bell defaults follow the active preset so the UI's "default" snap points # 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" case "${VESKSLOP_TTS_PRESET:-}" in vox) KB_DEFAULTS="$VOX_DEFAULTS" ;; @@ -243,17 +243,15 @@ if [ -n "$SPEAKER_GUID" ]; then P_LOWER=40 P_EXPR="" - # Half-Life presets for Keith Bell (the game's announcement voice). "vox" - # starts from the approximated Valve VOX profile published by NetTTS - # (scripts/winetricks/nettts_flextalk_vox_profile.reg), then refines pitch, - # rate, head size and lowering against the actual game word assets - # (see docs/TTS-DESIGN.md). "hev" is a starting point for the HEV suit - # voice, which has no published profile. + # Half-Life presets for Keith Bell (the game's announcement voice). + # "vox" is the approximated Valve VOX profile published by NetTTS + # (scripts/winetricks/nettts_flextalk_vox_profile.reg); "hev" is a starting + # point for the HEV suit voice, which has no published profile. case "$PRESET" in vox) if [ "$ENGVOICE" = "Keith Bell" ]; then - P_PITCH=0; P_RATE=15; P_VOLUME=70 - P_PROM=22; P_HEAD=34; P_LOWER=50 + P_PITCH=3; P_RATE=17; P_VOLUME=70 + P_PROM=22; P_HEAD=52; P_LOWER=40 fi ;; hev)