#!/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.77 \!%1.19 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")" # Word mode combines with a preset and skips the VOX parser, with tags per word. VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_PRESET=vox VESKSLOP_TTS_VOX=1 \ VESKSLOP_TTS_WORDS=1 VESKSLOP_TTS_WORD_GAP_MS=0 VESKSLOP_TTS_SPEED=1.25 run_wrapper check "word mode with preset synthesizes every word" "3" "$(grep -c '^ARGV:' "$STUB_LOG")" 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: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. VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_PRESET=vox VESKSLOP_TTS_WORDS=1 \ VESKSLOP_TTS_WORD_GAP_MS=0 run_wrapper check "implied VOX yields to word mode" "3" "$(grep -c '^ARGV:' "$STUB_LOG")" check "implied VOX skipped for words" "0" "$(grep -c 'wH1' "$STUB_LOG")" # Crunch keeps the output contract and changes the sound. VESKSLOP_TTS_VOICE=keith-bell run_wrapper cp "$WORK/out.wav" "$WORK/off.wav" VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_CRUNCH=telephone run_wrapper check "crunch keeps 48k s16le" "pcm_s16le,48000,1" \ "$("${MICROSLOP_FFMPEG:-ffmpeg}" -hide_banner -loglevel error -i "$WORK/out.wav" -f null - 2>&1 >/dev/null; ffprobe -v error -show_entries stream=codec_name,sample_rate,channels -of csv=p=0 "$WORK/out.wav")" check "crunch changes the audio" "1" "$([ "$(md5sum < "$WORK/off.wav")" != "$(md5sum < "$WORK/out.wav")" ] && echo 1 || echo 0)" # Streaming word mode writes a live WAV with a gap between each pair of words. VESKSLOP_TTS_VOICE=keith-bell VESKSLOP_TTS_WORDS=1 VESKSLOP_TTS_WORD_GAP_MS=200 \ VESKSLOP_TTS_STREAM=1 run_wrapper check "stream header" "RIFF" "$(head -c 4 "$WORK/out.wav")" check "stream is decodable" "0" \ "$("${MICROSLOP_FFMPEG:-ffmpeg}" -hide_banner -loglevel error -i "$WORK/out.wav" -f null - >/dev/null 2>&1; echo $?)" 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)" # 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; }