Expose the Half-Life voices and tidy the radio kit
This commit is contained in:
@@ -40,6 +40,12 @@ mobile draft at 390px.
|
|||||||
- [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB
|
- [x] C11 preview caching: Discord-hosted image/GIF previews (WebP 320px, hash-addressed, 5 MiB
|
||||||
cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`:
|
cap, one attempt) run after every walk in both modes. Live run `...-20260916090722`:
|
||||||
`previewed: 2`; thumbs served over `/api/media/<scrape>/<item>/thumb` (200).
|
`previewed: 2`; thumbs served over `/api/media/<scrape>/<item>/thumb` (200).
|
||||||
|
- [x] C68 Half-Life voices and a tidier Radio kit. `vox` and `hev` are now real voices in the
|
||||||
|
list (Keith Bell with the VOX/HEV profile; VOX implies its prosody), so the preset dropdown and
|
||||||
|
the VOX/clean toggles left the Radio kit; the clean option appears only for the VOX voice, and
|
||||||
|
every remaining field has a visible label (echo delay/level, crunch bits/Hz, word gap). The
|
||||||
|
daemon auto-enables prosody for the `vox` voice and the UI no longer sends stale `preset`/`vox`
|
||||||
|
values. Wrapper tests cover both voices and the profile they write.
|
||||||
- [x] C67 Crunch, echo options, and streaming word mode. The wrapper gained a separate
|
- [x] C67 Crunch, echo options, and streaming word mode. The wrapper gained a separate
|
||||||
compression stage applied last (presets hl90/hl90hq/telephone/adpcm/crush plus bit-depth and
|
compression stage applied last (presets hl90/hl90hq/telephone/adpcm/crush plus bit-depth and
|
||||||
rate overrides, output still 48 kHz s16le), echo delay/level knobs, and `VESKSLOP_TTS_STREAM=1`
|
rate overrides, output still 48 kHz s16le), echo delay/level knobs, and `VESKSLOP_TTS_STREAM=1`
|
||||||
|
|||||||
+5
-4
@@ -173,10 +173,11 @@ from the TTS panel:
|
|||||||
word: measured first sound 0.5 s instead of 2.4 s on a ten-word line. Streaming skips the
|
word: measured first sound 0.5 s instead of 2.4 s on a ten-word line. Streaming skips the
|
||||||
loudness measurement (`VESKSLOP_TTS_STREAM_GAIN_DB` compensates if needed) and cannot use VOX
|
loudness measurement (`VESKSLOP_TTS_STREAM_GAIN_DB` compensates if needed) and cannot use VOX
|
||||||
prosody, which needs the whole sentence.
|
prosody, which needs the whole sentence.
|
||||||
A **preset** select (`tts.preset`: factory, VOX, HEV) shifts the Keith Bell speaker profile to
|
The Half-Life voices are first-class entries in the voice list: **VOX (Half-Life)** and **HEV
|
||||||
the Half-Life approximation; `vox` also implies VOX prosody unless VOX is explicitly off, and
|
suit** are Keith Bell with the corresponding profile preset, and VOX carries its prosody with it
|
||||||
overrides travel as `VESKSLOP_TTS_PRESET` / `{preset}`. Precedence is preset < profile <
|
(so no separate prosody toggle). Selecting the VOX voice shows its **clean (no stutter)** option
|
||||||
expression, so the sliders still win over the preset. The wrapper writes the audible profile to
|
in the profile area. The underlying `tts.preset` / `vox` / `voxClean` values remain available to
|
||||||
|
the API and MCP; precedence is preset < profile < expression, so the sliders still win. The wrapper writes the audible profile to
|
||||||
both registry hives (plus the preset's Gender/Line Mode and a Default Speaker pointer), and for
|
both registry hives (plus the preset's Gender/Line Mode and a Default Speaker pointer), and for
|
||||||
Flextalk it sends rate/pitch as the sticky `\!R` / `\!%` vendor tags rather than
|
Flextalk it sends rate/pitch as the sticky `\!R` / `\!%` vendor tags rather than
|
||||||
`ITTSAttributes`; attributes only apply to MSTTS voices. Neutral sliders emit neither, so the
|
`ITTSAttributes`; attributes only apply to MSTTS voices. Neutral sliders emit neither, so the
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export MICROSLOP_BUILD="$WORK/build"
|
|||||||
export MICROSLOP_WINE="$WORK/bin/wine"
|
export MICROSLOP_WINE="$WORK/bin/wine"
|
||||||
export MICROSLOP_FFMPEG="${MICROSLOP_FFMPEG:-ffmpeg}"
|
export MICROSLOP_FFMPEG="${MICROSLOP_FFMPEG:-ffmpeg}"
|
||||||
export DISPLAY=:99
|
export DISPLAY=:99
|
||||||
|
export WINEPREFIX="$WORK/prefix"
|
||||||
export STUB_LOG="$WORK/host.log"
|
export STUB_LOG="$WORK/host.log"
|
||||||
export STUB_REG="$WORK/profile.reg"
|
export STUB_REG="$WORK/profile.reg"
|
||||||
export STUB_SINE="$WORK/sine.wav"
|
export STUB_SINE="$WORK/sine.wav"
|
||||||
@@ -136,6 +137,14 @@ check "stream is decodable" "0" \
|
|||||||
check "stream has a gap per word break" "2" \
|
check "stream has a gap per word break" "2" \
|
||||||
"$("${MICROSLOP_FFMPEG:-ffmpeg}" -hide_banner -i "$WORK/out.wav" -af silencedetect=noise=-45dB:d=0.15 -f null - 2>&1 | grep -c silence_start)"
|
"$("${MICROSLOP_FFMPEG:-ffmpeg}" -hide_banner -i "$WORK/out.wav" -af silencedetect=noise=-45dB:d=0.15 -f null - 2>&1 | grep -c silence_start)"
|
||||||
|
|
||||||
|
# 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: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")"
|
||||||
|
check "hev voice has no prosody" "0" "$(grep -c 'wH1' "$STUB_LOG")"
|
||||||
|
|
||||||
# Voice metadata follows the active preset.
|
# Voice metadata follows the active preset.
|
||||||
check "voices reflect hev" "1" \
|
check "voices reflect hev" "1" \
|
||||||
"$(VESKSLOP_TTS_PRESET=hev sh "$WRAP" --voices | tr ',' '\n' | grep -c 'keith-bell|Keith Bell|male|en|expression=59+defaults=pitch:15:rate:24')"
|
"$(VESKSLOP_TTS_PRESET=hev sh "$WRAP" --voices | tr ',' '\n' | grep -c 'keith-bell|Keith Bell|male|en|expression=59+defaults=pitch:15:rate:24')"
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
# VESKSLOP_TTS_HUM_MS leading low hum in ms, 0 = off (default 80)
|
# VESKSLOP_TTS_HUM_MS leading low hum in ms, 0 = off (default 80)
|
||||||
# VESKSLOP_TTS_HUM_HZ hum frequency, 30-200 (default 75)
|
# VESKSLOP_TTS_HUM_HZ hum frequency, 30-200 (default 75)
|
||||||
# VESKSLOP_TTS_HUM_DB hum peak level, -80..-6 (default -48)
|
# VESKSLOP_TTS_HUM_DB hum peak level, -80..-6 (default -48)
|
||||||
|
# voice `vox`/`hev` Half-Life profiles as first-class voices
|
||||||
# VESKSLOP_TTS_PRESET vox | hev (Half-Life profiles, Keith Bell;
|
# VESKSLOP_TTS_PRESET vox | hev (Half-Life profiles, Keith Bell;
|
||||||
# vox also enables VOX prosody)
|
# vox also enables VOX prosody)
|
||||||
#
|
#
|
||||||
@@ -44,9 +45,11 @@ 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: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
|
case "${VESKSLOP_TTS_PRESET:-}" in
|
||||||
vox) KB_DEFAULTS="pitch:3:rate:17:prominences:22:head_size:52:lowering:40:expression:59" ;;
|
vox) KB_DEFAULTS="$VOX_DEFAULTS" ;;
|
||||||
hev) KB_DEFAULTS="pitch:15:rate:24:prominences:22:head_size:52:lowering:40:expression:59" ;;
|
hev) KB_DEFAULTS="$HEV_DEFAULTS" ;;
|
||||||
*) KB_DEFAULTS="pitch:27:rate:33:prominences:17:head_size:46:lowering:40:expression:59" ;;
|
*) KB_DEFAULTS="pitch:27:rate:33:prominences:17:head_size:46:lowering:40:expression:59" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -54,7 +57,7 @@ esac
|
|||||||
# `expression=NN` marks a Flextalk voice with an Expression profile parameter and
|
# `expression=NN` marks a Flextalk voice with an Expression profile parameter and
|
||||||
# its registry default. Expression is applied at voice load, so the wrapper writes
|
# its registry default. Expression is applied at voice load, so the wrapper writes
|
||||||
# it into the Wine registry before synthesis.
|
# it into the Wine registry before synthesis.
|
||||||
VOICES="sam|Sam|male|en,mary|Mary|female|en,mike|Mike|male|en,keith-bell|Keith Bell|male|en|expression=59+defaults=$KB_DEFAULTS,kerry-watson|Kerry Watson|female|en|expression=100+defaults=pitch:65:rate:32:prominences:17:head_size:45:lowering:40:expression:100,mike-telephone|Mike (for Telephone)|male|en,mary-telephone|Mary (for Telephone)|female|en,mike-hall|Mike in Hall|male|en,mike-stadium|Mike in Stadium|male|en,mike-space|Mike in Space|male|en,mary-hall|Mary in Hall|female|en,mary-stadium|Mary in Stadium|female|en,mary-space|Mary in Space|female|en"
|
VOICES="sam|Sam|male|en,mary|Mary|female|en,mike|Mike|male|en,keith-bell|Keith Bell|male|en|expression=59+defaults=$KB_DEFAULTS,vox|VOX (Half-Life)|male|en|expression=59+defaults=$VOX_DEFAULTS,hev|HEV suit|male|en|expression=59+defaults=$HEV_DEFAULTS,kerry-watson|Kerry Watson|female|en|expression=100+defaults=pitch:65:rate:32:prominences:17:head_size:45:lowering:40:expression:100,mike-telephone|Mike (for Telephone)|male|en,mary-telephone|Mary (for Telephone)|female|en,mike-hall|Mike in Hall|male|en,mike-stadium|Mike in Stadium|male|en,mike-space|Mike in Space|male|en,mary-hall|Mary in Hall|female|en,mary-stadium|Mary in Stadium|female|en,mary-space|Mary in Space|female|en"
|
||||||
|
|
||||||
if [ "${1:-}" = "--voices" ]; then
|
if [ "${1:-}" = "--voices" ]; then
|
||||||
echo "$VOICES"
|
echo "$VOICES"
|
||||||
@@ -82,6 +85,13 @@ WORD_GAP=${VESKSLOP_TTS_WORD_GAP_MS:-120}
|
|||||||
VOX=${VESKSLOP_TTS_VOX:-0}
|
VOX=${VESKSLOP_TTS_VOX:-0}
|
||||||
VOX_CLEAN=${VESKSLOP_TTS_VOX_CLEAN:-0}
|
VOX_CLEAN=${VESKSLOP_TTS_VOX_CLEAN:-0}
|
||||||
PRESET=${VESKSLOP_TTS_PRESET:-}
|
PRESET=${VESKSLOP_TTS_PRESET:-}
|
||||||
|
|
||||||
|
# The Half-Life voices are Keith Bell with a fixed profile preset; selecting one
|
||||||
|
# stands in for VESKSLOP_TTS_PRESET.
|
||||||
|
case "$VOICE" in
|
||||||
|
vox) [ -n "$PRESET" ] || PRESET=vox ;;
|
||||||
|
hev) [ -n "$PRESET" ] || PRESET=hev ;;
|
||||||
|
esac
|
||||||
# The VOX preset is the Half-Life announcement voice: it implies VOX prosody
|
# The VOX preset is the Half-Life announcement voice: it implies VOX prosody
|
||||||
# unless the caller explicitly disables it.
|
# unless the caller explicitly disables it.
|
||||||
if [ "$PRESET" = "vox" ] && [ -z "${VESKSLOP_TTS_VOX:-}" ]; then
|
if [ "$PRESET" = "vox" ] && [ -z "${VESKSLOP_TTS_VOX:-}" ]; then
|
||||||
@@ -96,6 +106,8 @@ case "$VOICE" in
|
|||||||
mary) ENGVOICE="Mary" ;;
|
mary) ENGVOICE="Mary" ;;
|
||||||
mike) ENGVOICE="Mike" ;;
|
mike) ENGVOICE="Mike" ;;
|
||||||
keith-bell) ENGVOICE="Keith Bell" ;;
|
keith-bell) ENGVOICE="Keith Bell" ;;
|
||||||
|
vox) ENGVOICE="Keith Bell" ;;
|
||||||
|
hev) ENGVOICE="Keith Bell" ;;
|
||||||
kerry-watson) ENGVOICE="Kerry Watson" ;;
|
kerry-watson) ENGVOICE="Kerry Watson" ;;
|
||||||
mike-telephone) ENGVOICE="Mike (for Telephone)" ;;
|
mike-telephone) ENGVOICE="Mike (for Telephone)" ;;
|
||||||
mary-telephone) ENGVOICE="Mary (for Telephone)" ;;
|
mary-telephone) ENGVOICE="Mary (for Telephone)" ;;
|
||||||
|
|||||||
+8
-18
@@ -2033,10 +2033,7 @@ function fillProfileControls() {
|
|||||||
$("#tts-stream-words").checked = state.speech.streamWords !== false;
|
$("#tts-stream-words").checked = state.speech.streamWords !== false;
|
||||||
|
|
||||||
$("#tts-words").checked = state.speech.words === true;
|
$("#tts-words").checked = state.speech.words === true;
|
||||||
$("#tts-preset").value = state.speech.preset ?? "";
|
|
||||||
$("#tts-vox").checked = state.speech.vox === true;
|
|
||||||
$("#tts-vox-clean").checked = state.speech.voxClean === true;
|
$("#tts-vox-clean").checked = state.speech.voxClean === true;
|
||||||
$("#tts-vox-clean").disabled = state.speech.vox !== true;
|
|
||||||
const gap = Number(state.speech.wordGapMs);
|
const gap = Number(state.speech.wordGapMs);
|
||||||
$("#tts-word-gap").value = Number.isFinite(gap) && state.speech.wordGapMs !== null ? gap : 120;
|
$("#tts-word-gap").value = Number.isFinite(gap) && state.speech.wordGapMs !== null ? gap : 120;
|
||||||
$("#tts-word-gap-value").textContent = `${$("#tts-word-gap").value}ms`;
|
$("#tts-word-gap-value").textContent = `${$("#tts-word-gap").value}ms`;
|
||||||
@@ -2069,9 +2066,10 @@ function effectOptions() {
|
|||||||
echo: Number($("#tts-echo").value),
|
echo: Number($("#tts-echo").value),
|
||||||
words: $("#tts-words").checked,
|
words: $("#tts-words").checked,
|
||||||
wordGapMs: Number($("#tts-word-gap").value),
|
wordGapMs: Number($("#tts-word-gap").value),
|
||||||
vox: $("#tts-vox").checked,
|
// VOX and the Half-Life profile are part of the `vox`/`hev` voices now.
|
||||||
|
vox: false,
|
||||||
|
preset: null,
|
||||||
voxClean: $("#tts-vox-clean").checked,
|
voxClean: $("#tts-vox-clean").checked,
|
||||||
preset: $("#tts-preset").value || null,
|
|
||||||
echoDelayMs: $("#tts-echo-delay").value ? Number($("#tts-echo-delay").value) : null,
|
echoDelayMs: $("#tts-echo-delay").value ? Number($("#tts-echo-delay").value) : null,
|
||||||
echoLevel: $("#tts-echo-level").value ? Number($("#tts-echo-level").value) : null,
|
echoLevel: $("#tts-echo-level").value ? Number($("#tts-echo-level").value) : null,
|
||||||
crunch: $("#tts-crunch").value || null,
|
crunch: $("#tts-crunch").value || null,
|
||||||
@@ -2084,6 +2082,10 @@ function syncVoiceControls(voices, selected) {
|
|||||||
const voice = (voices ?? []).find(entry => entry.id === selected);
|
const voice = (voices ?? []).find(entry => entry.id === selected);
|
||||||
const pitchable = !voice || voice.supportsPitch !== false;
|
const pitchable = !voice || voice.supportsPitch !== false;
|
||||||
|
|
||||||
|
// The Half-Life voices carry VOX prosody; their clean toggle lives in the profile area.
|
||||||
|
const voxOptions = $("#tts-vox-options");
|
||||||
|
if (voxOptions) voxOptions.hidden = selected !== "vox";
|
||||||
|
|
||||||
$("#tts-pitch-control").hidden = !pitchable;
|
$("#tts-pitch-control").hidden = !pitchable;
|
||||||
$("#tts-pitch").disabled = !pitchable;
|
$("#tts-pitch").disabled = !pitchable;
|
||||||
|
|
||||||
@@ -2753,19 +2755,7 @@ function init() {
|
|||||||
$(`#${id}`).addEventListener("change", () => void saveSpeech());
|
$(`#${id}`).addEventListener("change", () => void saveSpeech());
|
||||||
}
|
}
|
||||||
$("#tts-words").addEventListener("change", () => void saveSpeech());
|
$("#tts-words").addEventListener("change", () => void saveSpeech());
|
||||||
$("#tts-vox").addEventListener("change", () => {
|
$("#tts-vox-clean").addEventListener("change", () => void saveSpeech());
|
||||||
$("#tts-vox-clean").disabled = !$("#tts-vox").checked;
|
|
||||||
void saveSpeech();
|
|
||||||
});
|
|
||||||
$("#tts-preset").addEventListener("change", () => {
|
|
||||||
// Picking the VOX preset turns on VOX prosody as a default. Word mode still wins
|
|
||||||
// for word-by-word delivery, and turning VOX off keeps the preset.
|
|
||||||
if ($("#tts-preset").value === "vox") {
|
|
||||||
$("#tts-vox").checked = true;
|
|
||||||
$("#tts-vox-clean").disabled = false;
|
|
||||||
}
|
|
||||||
void saveSpeech();
|
|
||||||
});
|
|
||||||
$("#tts-vox-clean").addEventListener("change", () => void saveSpeech());
|
$("#tts-vox-clean").addEventListener("change", () => void saveSpeech());
|
||||||
for (const id of [
|
for (const id of [
|
||||||
"#tts-echo-delay",
|
"#tts-echo-delay",
|
||||||
|
|||||||
+15
-23
@@ -550,8 +550,10 @@
|
|||||||
<span class="vk-micro-label">echo</span>
|
<span class="vk-micro-label">echo</span>
|
||||||
<input id="tts-echo" type="range" min="0" max="100" step="5" value="0" />
|
<input id="tts-echo" type="range" min="0" max="100" step="5" value="0" />
|
||||||
<span id="tts-echo-value" class="vk-mono vk-dim">off</span>
|
<span id="tts-echo-value" class="vk-mono vk-dim">off</span>
|
||||||
<input id="tts-echo-delay" class="input vk-num" type="number" min="10" max="400" placeholder="ms" />
|
<span class="vk-micro-label">delay</span>
|
||||||
<input id="tts-echo-level" class="input vk-num" type="number" min="0" max="0.95" step="0.05" placeholder="lvl" />
|
<input id="tts-echo-delay" class="input vk-num" type="number" min="10" max="400" />
|
||||||
|
<span class="vk-micro-label">level</span>
|
||||||
|
<input id="tts-echo-level" class="input vk-num" type="number" min="0" max="0.95" step="0.05" />
|
||||||
</div>
|
</div>
|
||||||
<div class="vk-profile-row">
|
<div class="vk-profile-row">
|
||||||
<span class="vk-micro-label">crunch</span>
|
<span class="vk-micro-label">crunch</span>
|
||||||
@@ -563,38 +565,28 @@
|
|||||||
<option value="adpcm">ADPCM · 4-bit</option>
|
<option value="adpcm">ADPCM · 4-bit</option>
|
||||||
<option value="crush">crush · custom</option>
|
<option value="crush">crush · custom</option>
|
||||||
</select>
|
</select>
|
||||||
<input id="tts-crunch-bits" class="input vk-num" type="number" min="1" max="16" placeholder="bits" />
|
<span class="vk-micro-label">bits</span>
|
||||||
<input id="tts-crunch-rate" class="input vk-num" type="number" min="4000" max="48000" step="250" placeholder="Hz" />
|
<input id="tts-crunch-bits" class="input vk-num" type="number" min="1" max="16" />
|
||||||
|
<span class="vk-micro-label">Hz</span>
|
||||||
|
<input id="tts-crunch-rate" class="input vk-num" type="number" min="4000" max="48000" step="250" />
|
||||||
</div>
|
</div>
|
||||||
<div class="vk-profile-row">
|
<div class="vk-profile-row">
|
||||||
<label class="vk-tts-check"><input id="tts-words" type="checkbox" /> word mode</label>
|
<label class="vk-tts-check"><input id="tts-words" type="checkbox" /> word mode</label>
|
||||||
|
<span class="vk-micro-label">gap</span>
|
||||||
<input id="tts-word-gap" type="range" min="0" max="500" step="10" value="120" />
|
<input id="tts-word-gap" type="range" min="0" max="500" step="10" value="120" />
|
||||||
<span id="tts-word-gap-value" class="vk-mono vk-dim">120ms</span>
|
<span id="tts-word-gap-value" class="vk-mono vk-dim">120ms</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="vk-profile-row">
|
<div class="vk-profile-row">
|
||||||
<span class="vk-micro-label">preset</span>
|
<label class="vk-tts-check" title="Write the WAV as words are synthesized so playback starts on the first word. Skips the loudness measurement.">
|
||||||
<select id="tts-preset" class="input">
|
<input id="tts-stream-words" type="checkbox" /> stream word mode
|
||||||
<option value="">factory</option>
|
|
||||||
<option value="vox">VOX (Black Mesa)</option>
|
|
||||||
<option value="hev">HEV suit</option>
|
|
||||||
</select>
|
|
||||||
<span class="vk-mono vk-dim">Keith Bell</span>
|
|
||||||
</div>
|
|
||||||
<div class="vk-profile-row">
|
|
||||||
<label class="vk-tts-check">
|
|
||||||
<input id="tts-vox" type="checkbox" /> VOX prosody
|
|
||||||
<span
|
|
||||||
class="vk-info"
|
|
||||||
title="NetTTS-style Black Mesa delivery for the Flextalk voices: sentence breaks, lead-ins, weighted pauses, and vendor tags. Needs python3. Overrides word mode."
|
|
||||||
>i</span
|
|
||||||
>
|
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="vk-profile-row" id="tts-vox-options" hidden>
|
||||||
|
<span class="vk-micro-label">VOX prosody</span>
|
||||||
|
<span class="vk-mono vk-dim">included with this voice</span>
|
||||||
<label class="vk-tts-check">
|
<label class="vk-tts-check">
|
||||||
<input id="tts-vox-clean" type="checkbox" /> clean (no stutter)
|
<input id="tts-vox-clean" type="checkbox" /> clean (no stutter)
|
||||||
</label>
|
</label>
|
||||||
<label class="vk-tts-check" title="Write the WAV as words are synthesized so playback starts on the first word. Skips the loudness measurement; set a fixed gain below if needed.">
|
|
||||||
<input id="tts-stream-words" type="checkbox" /> stream word mode
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="vk-profile-section">
|
<section class="vk-profile-section">
|
||||||
|
|||||||
+3
-1
@@ -765,7 +765,9 @@ export function startWebUi(deps: WebUiDeps): WebUiHandle {
|
|||||||
const words = req.body?.words !== undefined ? req.body.words === true : prefs.tts.words;
|
const words = req.body?.words !== undefined ? req.body.words === true : prefs.tts.words;
|
||||||
const wordGapMs =
|
const wordGapMs =
|
||||||
req.body?.wordGapMs !== undefined ? Number(req.body.wordGapMs) : prefs.tts.wordGapMs ?? undefined;
|
req.body?.wordGapMs !== undefined ? Number(req.body.wordGapMs) : prefs.tts.wordGapMs ?? undefined;
|
||||||
const vox = req.body?.vox !== undefined ? req.body.vox === true : prefs.tts.vox;
|
// The Half-Life VOX voice carries its prosody with it.
|
||||||
|
const vox =
|
||||||
|
(req.body?.vox !== undefined ? req.body.vox === true : prefs.tts.vox) || voice === "vox";
|
||||||
const voxClean = req.body?.voxClean !== undefined ? req.body.voxClean === true : prefs.tts.voxClean;
|
const voxClean = req.body?.voxClean !== undefined ? req.body.voxClean === true : prefs.tts.voxClean;
|
||||||
const preset =
|
const preset =
|
||||||
req.body?.preset === "vox" || req.body?.preset === "hev"
|
req.body?.preset === "vox" || req.body?.preset === "hev"
|
||||||
|
|||||||
Reference in New Issue
Block a user