Apply TTS presets with vendor tags and both hives
This commit is contained in:
@@ -40,6 +40,14 @@ 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] C64 Correct preset application. The design's bug-fix section landed in the wrapper:
|
||||||
|
Flextalk rate/pitch now ride as sticky `\!R` / `\!%` vendor tags (attributes only for MSTTS),
|
||||||
|
neutral sliders emit neither, word mode tags every word, and presets write the audible profile
|
||||||
|
to both registry hives with Gender/Line Mode plus a Default Speaker pointer. `--voices`
|
||||||
|
reflects the active preset in the Keith Bell `defaults=` flags so the panel snap points match.
|
||||||
|
New `microslopsam/setup/test-wrapper.sh` verifies the whole contract with stubbed wine and
|
||||||
|
engine binaries (tags vs attributes, registry shape, preset extras, implied VOX, metadata);
|
||||||
|
all checks pass.
|
||||||
- [x] C63 Half-Life presets. The wrapper now implements `VESKSLOP_TTS_PRESET=vox|hev` (Keith
|
- [x] C63 Half-Life presets. The wrapper now implements `VESKSLOP_TTS_PRESET=vox|hev` (Keith
|
||||||
Bell profile: VOX pitch 3/rate 17/prominence 22/head 52/volume 70, HEV pitch 15/rate 24,
|
Bell profile: VOX pitch 3/rate 17/prominence 22/head 52/volume 70, HEV pitch 15/rate 24,
|
||||||
expression 59; other voices untouched; preset < profile < expression; the `vox` preset implies
|
expression 59; other voices untouched; preset < profile < expression; the `vox` preset implies
|
||||||
|
|||||||
+7
-1
@@ -164,7 +164,13 @@ from the TTS panel:
|
|||||||
A **preset** select (`tts.preset`: factory, VOX, HEV) shifts the Keith Bell speaker profile to
|
A **preset** select (`tts.preset`: factory, VOX, HEV) shifts the Keith Bell speaker profile to
|
||||||
the Half-Life approximation; `vox` also implies VOX prosody unless VOX is explicitly off, and
|
the Half-Life approximation; `vox` also implies VOX prosody unless VOX is explicitly off, and
|
||||||
overrides travel as `VESKSLOP_TTS_PRESET` / `{preset}`. Precedence is preset < profile <
|
overrides travel as `VESKSLOP_TTS_PRESET` / `{preset}`. Precedence is preset < profile <
|
||||||
expression, so the sliders still win over the preset.
|
expression, so the sliders still win over the preset. The wrapper writes the audible profile to
|
||||||
|
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
|
||||||
|
`ITTSAttributes`; attributes only apply to MSTTS voices. Neutral sliders emit neither, so the
|
||||||
|
preset is reproduced exactly. `sh microslopsam/setup/test-wrapper.sh` checks this contract with
|
||||||
|
stubbed wine and engine binaries, no Wine install needed. The preset values also show up in the
|
||||||
|
voice metadata defaults, so the panel's "default" snap points match what the engine gets.
|
||||||
- **Test lines** - Attention, Laboratory, Critical, Health, and Radiation, plus two custom lines
|
- **Test lines** - Attention, Laboratory, Critical, Health, and Radiation, plus two custom lines
|
||||||
kept in `localStorage`. Each button speaks the line with the current profile, so a change can be
|
kept in `localStorage`. Each button speaks the line with the current profile, so a change can be
|
||||||
auditioned without touching the real queue.
|
auditioned without touching the real queue.
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ pure-native build, and keep a path to a fully portable engine.
|
|||||||
- AT&T Flextalk: Keith Bell, Kerry Watson
|
- AT&T Flextalk: Keith Bell, Kerry Watson
|
||||||
- Test output: `build/test_sam.wav` (22.05 kHz), `build/test_Keith_Bell.wav`,
|
- Test output: `build/test_sam.wav` (22.05 kHz), `build/test_Keith_Bell.wav`,
|
||||||
`build/test_Kerry_Watson.wav` (11.025 kHz).
|
`build/test_Kerry_Watson.wav` (11.025 kHz).
|
||||||
|
- Wrapper contract test (no Wine needed, stubs wine and the engine host):
|
||||||
|
`sh setup/test-wrapper.sh`.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
|||||||
Executable
+110
@@ -0,0 +1,110 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Regression test for the Veskslop TTS wrapper contract. No Wine needed: the wine
|
||||||
|
# and engine-host binaries are stubbed, so this checks the arguments the wrapper
|
||||||
|
# builds, the registry import it generates, and the voice metadata it publishes.
|
||||||
|
#
|
||||||
|
# The bug this guards against: rate/pitch sent as ITTSAttributes (-s/-p) override
|
||||||
|
# the Flextalk speaker profile, so the VOX/HEV presets sounded too fast and not
|
||||||
|
# deep enough. Flextalk must use the sticky vendor tags (\!R / \!%), and neutral
|
||||||
|
# sliders must emit neither.
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||||
|
WRAP="$HERE/veskslop-tts.sh"
|
||||||
|
WORK=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$WORK"' EXIT
|
||||||
|
|
||||||
|
mkdir -p "$WORK/build" "$WORK/bin" "$WORK/out"
|
||||||
|
"${MICROSLOP_FFMPEG:-ffmpeg}" -hide_banner -loglevel error -f lavfi \
|
||||||
|
-i "sine=frequency=200:duration=0.3" -ar 11025 -ac 1 -c:a pcm_s16le -y "$WORK/sine.wav"
|
||||||
|
|
||||||
|
cat > "$WORK/build/sapi4host.exe" <<'STUB'
|
||||||
|
#!/bin/sh
|
||||||
|
{ printf 'ARGV:'; for a in "$@"; do printf ' [%s]' "$a"; done; printf '\n'; } >> "$STUB_LOG"
|
||||||
|
OUT=""
|
||||||
|
while [ "$#" -ge 2 ]; do
|
||||||
|
[ "$1" = "-o" ] && OUT=$2
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
cp "$STUB_SINE" "$OUT"
|
||||||
|
STUB
|
||||||
|
|
||||||
|
cat > "$WORK/bin/wine" <<'STUB'
|
||||||
|
#!/bin/sh
|
||||||
|
[ "${1:-}" = "reg" ] && [ "${2:-}" = "import" ] && cp "$3" "$STUB_REG"
|
||||||
|
exit 0
|
||||||
|
STUB
|
||||||
|
chmod +x "$WORK/build/sapi4host.exe" "$WORK/bin/wine"
|
||||||
|
|
||||||
|
printf 'Attention all personnel.\n' > "$WORK/text.txt"
|
||||||
|
|
||||||
|
export MICROSLOP_BUILD="$WORK/build"
|
||||||
|
export MICROSLOP_WINE="$WORK/bin/wine"
|
||||||
|
export MICROSLOP_FFMPEG="${MICROSLOP_FFMPEG:-ffmpeg}"
|
||||||
|
export DISPLAY=:99
|
||||||
|
export STUB_LOG="$WORK/host.log"
|
||||||
|
export STUB_REG="$WORK/profile.reg"
|
||||||
|
export STUB_SINE="$WORK/sine.wav"
|
||||||
|
|
||||||
|
FAIL=0
|
||||||
|
check() {
|
||||||
|
if [ "$2" = "$3" ]; then
|
||||||
|
echo "ok - $1"
|
||||||
|
else
|
||||||
|
echo "FAIL - $1: expected [$2], got [$3]"
|
||||||
|
FAIL=1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_wrapper() {
|
||||||
|
: > "$STUB_LOG"
|
||||||
|
sh "$WRAP" "$WORK/out.wav" "$WORK/text.txt" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# The spoken text is the -t argument; the engine host writes to the wrapper's temp file.
|
||||||
|
text_for() {
|
||||||
|
sed -n '1s/.*\[-t\] \[\(.*\)\] \[-o\].*/\1/p' "$STUB_LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
attr_count() {
|
||||||
|
grep -oE '\[-s\]|\[-p\]' "$STUB_LOG" | wc -l | tr -d ' '
|
||||||
|
}
|
||||||
|
|
||||||
|
# Neutral Flextalk sliders emit neither attributes nor tags.
|
||||||
|
VESKSLOP_TTS_VOICE=keith-bell run_wrapper
|
||||||
|
check "neutral flextalk text" "Attention all personnel." "$(text_for)"
|
||||||
|
check "neutral flextalk has no attributes" "0" "$(attr_count)"
|
||||||
|
|
||||||
|
# Adjustments ride as tags; attributes stay out even at non-neutral values.
|
||||||
|
VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_VOX=0 \
|
||||||
|
VESKSLOP_TTS_SPEED=1.3 VESKSLOP_TTS_PITCH=3 run_wrapper
|
||||||
|
check "flextalk rate tag" "\!R0.769 \!%1.189 Attention all personnel." "$(text_for)"
|
||||||
|
check "flextalk has no attributes" "0" "$(attr_count)"
|
||||||
|
|
||||||
|
# MSTTS has no vendor tags and keeps the attributes.
|
||||||
|
VESKSLOP_TTS_VOICE=sam VESKSLOP_TTS_SPEED=1.3 VESKSLOP_TTS_PITCH=3 run_wrapper
|
||||||
|
check "mstts text has no tags" "Attention all personnel." "$(text_for)"
|
||||||
|
check "mstts keeps attributes" "2" "$(attr_count)"
|
||||||
|
|
||||||
|
# The vox preset implies VOX prosody when VESKSLOP_TTS_VOX is unset.
|
||||||
|
VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_PRESET=vox run_wrapper
|
||||||
|
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: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")"
|
||||||
|
|
||||||
|
# A plain Flextalk run keeps the defaults and writes no preset extras.
|
||||||
|
VESKSLOP_TTS_VOICE=keith-bell run_wrapper
|
||||||
|
check "plain run writes both hives" "2" "$(grep -c 'Speakers\\{11260610' "$STUB_REG")"
|
||||||
|
check "plain run has no preset extras" "0" "$(grep -cE 'Gender|Line Mode|Default Speaker' "$STUB_REG")"
|
||||||
|
|
||||||
|
# Voice metadata follows the active preset.
|
||||||
|
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')"
|
||||||
|
|
||||||
|
[ "$FAIL" = "0" ] && echo "wrapper tests passed" || { echo "wrapper tests failed"; exit 1; }
|
||||||
@@ -33,7 +33,14 @@ ROOT=$MICROSLOP_ROOT
|
|||||||
# `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=pitch:27:rate:33:prominences:17:head_size:46:lowering:40:expression:59,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"
|
# The Keith Bell defaults follow the active preset so the UI's "default" snap points match
|
||||||
|
# the values the preset actually writes.
|
||||||
|
case "${VESKSLOP_TTS_PRESET:-}" in
|
||||||
|
vox) KB_DEFAULTS="pitch:3:rate:17:prominences:22:head_size:52:lowering:40:expression:59" ;;
|
||||||
|
hev) KB_DEFAULTS="pitch:15:rate:24:prominences:22:head_size:52:lowering:40:expression:59" ;;
|
||||||
|
*) KB_DEFAULTS="pitch:27:rate:33:prominences:17:head_size:46:lowering:40:expression:59" ;;
|
||||||
|
esac
|
||||||
|
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"
|
||||||
|
|
||||||
if [ "${1:-}" = "--voices" ]; then
|
if [ "${1:-}" = "--voices" ]; then
|
||||||
echo "$VOICES"
|
echo "$VOICES"
|
||||||
@@ -153,18 +160,33 @@ if [ -f "$TEXTFILE" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Build the engine arguments from the (possibly command-overridden) speed/pitch.
|
# Build the engine arguments from the (possibly command-overridden) speed/pitch.
|
||||||
|
# Flextalk: rate and pitch ride as sticky vendor tags. Setting them through
|
||||||
|
# ITTSAttributes (-s/-p) overrides the speaker profile, which is the bug that made
|
||||||
|
# presets sound "too fast and not deep enough". MSTTS has no tags, so it keeps -s/-p.
|
||||||
SET_ARGS=""
|
SET_ARGS=""
|
||||||
if [ -n "$SPEED" ]; then
|
TAG_PREFIX=""
|
||||||
|
if [ -n "$SPEAKER_GUID" ]; then
|
||||||
|
if [ -n "$SPEED" ]; then
|
||||||
|
R=$(awk -v m="$SPEED" 'BEGIN{ if (m==1) exit; v=1/m; if (v<0.25) v=0.25; if (v>4.0) v=4.0; printf "%.3f", v }')
|
||||||
|
[ -n "$R" ] && TAG_PREFIX="${TAG_PREFIX}\\!R${R} "
|
||||||
|
fi
|
||||||
|
if [ -n "$PITCH" ]; then
|
||||||
|
P=$(awk -v st="$PITCH" 'BEGIN{ if (st==0) exit; v=exp(log(2)*st/12); if (v<0.5) v=0.5; if (v>2.0) v=2.0; printf "%.3f", v }')
|
||||||
|
[ -n "$P" ] && TAG_PREFIX="${TAG_PREFIX}\\!%${P} "
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -n "$SPEED" ]; then
|
||||||
S=$(awk -v m="$SPEED" 'BEGIN{printf "%d", 150*m+0.5}')
|
S=$(awk -v m="$SPEED" 'BEGIN{printf "%d", 150*m+0.5}')
|
||||||
[ "$S" -lt 50 ] && S=50
|
[ "$S" -lt 50 ] && S=50
|
||||||
[ "$S" -gt "$SMAX" ] && S=$SMAX
|
[ "$S" -gt "$SMAX" ] && S=$SMAX
|
||||||
SET_ARGS="$SET_ARGS -s $S"
|
SET_ARGS="$SET_ARGS -s $S"
|
||||||
fi
|
fi
|
||||||
if [ -n "$PITCH" ]; then
|
if [ -n "$PITCH" ]; then
|
||||||
P=$(awk -v st="$PITCH" -v nat="$NAT_PITCH" 'BEGIN{printf "%d", nat*exp(log(2)*st/12)+0.5}')
|
P=$(awk -v st="$PITCH" -v nat="$NAT_PITCH" 'BEGIN{printf "%d", nat*exp(log(2)*st/12)+0.5}')
|
||||||
[ "$P" -lt 50 ] && P=50
|
[ "$P" -lt 50 ] && P=50
|
||||||
[ "$P" -gt "$PMAX" ] && P=$PMAX
|
[ "$P" -gt "$PMAX" ] && P=$PMAX
|
||||||
SET_ARGS="$SET_ARGS -p $P"
|
SET_ARGS="$SET_ARGS -p $P"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Apply the Flextalk profile before synthesis. Profiles are read at voice load and
|
# Apply the Flextalk profile before synthesis. Profiles are read at voice load and
|
||||||
@@ -227,12 +249,21 @@ if [ -n "$SPEAKER_GUID" ]; then
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
REG_KEY="HKLM\\Software\\AT&T\\Watson\\2.0\\FlexTalk\\2.0\\Speakers\\$SPEAKER_GUID"
|
IS_PRESET=0
|
||||||
|
if [ "$ENGVOICE" = "Keith Bell" ]; then
|
||||||
|
case "$PRESET" in
|
||||||
|
vox|hev) IS_PRESET=1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
# Use printf, not echo: dash's echo interprets \2 in the key path as an
|
# Use printf, not echo: dash's echo interprets \2 in the key path as an
|
||||||
# escape (STX), which corrupts the key and makes the import a silent no-op.
|
# escape (STX), which corrupts the key and makes the import a silent no-op.
|
||||||
|
# NetTTS parity: the audible profile goes to both hives. Presets also write the
|
||||||
|
# informational keys from Valve's setup, and point Default Speaker at Keith Bell.
|
||||||
{
|
{
|
||||||
printf 'Windows Registry Editor Version 5.00\n\n'
|
printf 'Windows Registry Editor Version 5.00\n\n'
|
||||||
printf '[%s]\n' "$REG_KEY"
|
for HIVE in 'HKEY_LOCAL_MACHINE' 'HKEY_CURRENT_USER'; do
|
||||||
|
printf '[%s\\Software\\AT&T\\Watson\\2.0\\FlexTalk\\2.0\\Speakers\\%s]\n' "$HIVE" "$SPEAKER_GUID"
|
||||||
printf '"Pitch"=dword:%08x\n' "$P_PITCH"
|
printf '"Pitch"=dword:%08x\n' "$P_PITCH"
|
||||||
printf '"Speaking Rate"=dword:%08x\n' "$P_RATE"
|
printf '"Speaking Rate"=dword:%08x\n' "$P_RATE"
|
||||||
printf '"Volume"=dword:%08x\n' "$P_VOLUME"
|
printf '"Volume"=dword:%08x\n' "$P_VOLUME"
|
||||||
@@ -240,6 +271,19 @@ if [ -n "$SPEAKER_GUID" ]; then
|
|||||||
printf '"Prominences"=dword:%08x\n' "$P_PROM"
|
printf '"Prominences"=dword:%08x\n' "$P_PROM"
|
||||||
printf '"Head Size"=dword:%08x\n' "$P_HEAD"
|
printf '"Head Size"=dword:%08x\n' "$P_HEAD"
|
||||||
printf '"Lowering"=dword:%08x\n' "$P_LOWER"
|
printf '"Lowering"=dword:%08x\n' "$P_LOWER"
|
||||||
|
if [ "$IS_PRESET" = "1" ]; then
|
||||||
|
printf '"Gender"=dword:00000002\n'
|
||||||
|
printf '"Line Mode"=dword:00000001\n'
|
||||||
|
fi
|
||||||
|
printf '\n'
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$IS_PRESET" = "1" ]; then
|
||||||
|
for HIVE in 'HKEY_LOCAL_MACHINE' 'HKEY_CURRENT_USER'; do
|
||||||
|
printf '[%s\\Software\\AT&T\\Watson\\2.0\\FlexTalk\\2.0\\Speakers]\n' "$HIVE"
|
||||||
|
printf '"Default Speaker"="%s"\n\n' "$SPEAKER_GUID"
|
||||||
|
done
|
||||||
|
fi
|
||||||
} > "$TMP/profile.reg"
|
} > "$TMP/profile.reg"
|
||||||
|
|
||||||
# Guard against silent reg-import failures (a malformed key would corrupt
|
# Guard against silent reg-import failures (a malformed key would corrupt
|
||||||
@@ -322,7 +366,7 @@ if [ "$WORDS" = "1" ] && [ -n "$SPEAKER_GUID" ] && [ "$VOX" != "1" ]; then
|
|||||||
INDEX=0
|
INDEX=0
|
||||||
for word in $(cat "$TXT"); do
|
for word in $(cat "$TXT"); do
|
||||||
INDEX=$((INDEX + 1))
|
INDEX=$((INDEX + 1))
|
||||||
try_run_host "$TMP/w$INDEX.wav" "$word"
|
try_run_host "$TMP/w$INDEX.wav" "${TAG_PREFIX}${word}"
|
||||||
echo "file '$TMP/w$INDEX.wav'" >> "$TMP/list.txt"
|
echo "file '$TMP/w$INDEX.wav'" >> "$TMP/list.txt"
|
||||||
|
|
||||||
if [ "$GAP" -gt 0 ]; then
|
if [ "$GAP" -gt 0 ]; then
|
||||||
@@ -340,7 +384,7 @@ if [ "$WORDS" = "1" ] && [ -n "$SPEAKER_GUID" ] && [ "$VOX" != "1" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
try_run_host "$RAW" "$(cat "$TXT")"
|
try_run_host "$RAW" "${TAG_PREFIX}$(cat "$TXT")"
|
||||||
fi
|
fi
|
||||||
[ -s "$RAW" ] || { echo "engine produced no audio" >&2; exit 1; }
|
[ -s "$RAW" ] || { echo "engine produced no audio" >&2; exit 1; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user