#!/usr/bin/env bash # Set Voice - Fixed version for audiogame-manager # Settings to improve accessibility of dialog. export DIALOGOPTS='--insecure --no-lines --visit-items' # Turn off debug messages export WINEDEBUG="-all" # Set DISPLAY, as needed if [ -z "$DISPLAY" ] ; then export DISPLAY=:0 fi # Handle arguments declare -A command=( [h]="This help screen." [r:]="Specify voice rate, default is 7, options are 0-9 or A for fastest." [v:]="Voice name, the voice to use, same as in the menu." ) help() { echo "${0##*/}" echo "Released under the terms of the Common Public Attribution License Version 1.0" echo -e "This is a Stormux project: https://stormux.org\n" echo -e "Usage:\n" echo "With no arguments, open the game launcher." for i in "${!command[@]}" ; do echo "-${i/:/ }: ${command[${i}]}" done | sort exit 0 } menulist() { declare -a menuList for i in "${@}" ; do menuList+=("$i" "$i") done dialog --backtitle "Use the up and down arrow keys to find the option you want, then press enter to select it." \ --clear \ --extra-button \ --extra-label "Test Voice" \ --no-tags \ --menu "Please select one" 0 0 0 "${menuList[@]}" --stdout return $? } restore_voice() { if [[ $doRestore -eq 0 ]]; then wineserver -k sed -i -E -e 's/"DefaultTokenId"="HKEY_LOCAL_MACHINE\\\\(SOFTWARE|Software)\\\\(Wow6432Node\\\\|)Microsoft\\\\Speech\\\\Voices\\\\Token(Enum|)s\\\\[^"]+"/"DefaultTokenId"="'"${oldVoice//\\/\\\\}"'"/g' "${WINEPREFIX}/user.reg" fi } set_voice() { doRestore=1 local tmp="$1" local fullVoice local counter=0 for x in "${voiceList[@]}" ; do [[ "$x" = "$tmp" ]] && break counter=$((counter + 1)) done local RHVoiceName RHVoiceName="$(find "${WINEPREFIX}/drive_c/ProgramData/Olga Yakovleva/RHVoice/data/voices/" -maxdepth 1 -type d 2>/dev/null | head -1)" RHVoiceName="${RHVoiceName##*/}" fullVoice="${voiceListFullName[$counter]}" fullVoice="${fullVoice/RHVoice/RHVoice\\\\${RHVoiceName}}" wineserver -k # Remove any existing rate change for voices sed -i '/"DefaultTTSRate"=dword:/d' "${WINEPREFIX}/user.reg" sed -i -E -e 's/"DefaultTokenId"="HKEY_LOCAL_MACHINE\\\\(SOFTWARE|Software)\\\\(Wow6432Node\\\\|)Microsoft\\\\Speech\\\\Voices\\\\Token(Enum|)s\\\\[^"]+"/"DefaultTokenId"="HKEY_LOCAL_MACHINE\\\\'"${fullVoice//\\/\\\\}"'"\n"DefaultTTSRate"=dword:0000000'"${rate:-7}"'/g' "${WINEPREFIX}/user.reg" } test_voice() { doRestore=0 local tmp="$1" local fullVoice local counter=0 for x in "${voiceList[@]}" ; do [[ "$x" = "$tmp" ]] && break counter=$((counter + 1)) done local RHVoiceName RHVoiceName="$(find "${WINEPREFIX}/drive_c/ProgramData/Olga Yakovleva/RHVoice/data/voices/" -maxdepth 1 -type d 2>/dev/null | head -1)" RHVoiceName="${RHVoiceName##*/}" fullVoice="${voiceListFullName[$counter]}" fullVoice="${fullVoice/RHVoice/RHVoice\\\\${RHVoiceName}}" wineserver -k sed -i -E -e 's/"DefaultTokenId"="HKEY_LOCAL_MACHINE\\\\(SOFTWARE|Software)\\\\(Wow6432Node\\\\|)Microsoft\\\\Speech\\\\Voices\\\\Token(Enum|)s\\\\[^"]+"/"DefaultTokenId"="HKEY_LOCAL_MACHINE\\\\'"${fullVoice//\\/\\\\}"'"/g' "${WINEPREFIX}/user.reg" cat << "EOF" > "${bottle}/drive_c/windows/temp/speak.vbs" dim speechobject set speechobject=createobject("sapi.spvoice") speechobject.speak "This is a test of your chosen voice. It contains multiple sentences and punctuation, and is designed to give a full representation of this voices qualities." EOF wine cscript "c:\windows\temp\speak.vbs" } # Handle voice restore, but only if voice changed doRestore=1 trap restore_voice SIGINT # Convert the keys of the command associative array to a format usable by getopts args="${!command[*]}" args="${args//[[:space:]]/}" while getopts "${args}" i ; do case "$i" in h) help;; r) if ! [[ "${OPTARG}" =~ ^[0-9A]$ ]]; then echo "Invalid rate specified. Arguments must be 0-9 or A." exit 1 fi rate="${OPTARG}" ;; v) voice="${OPTARG}";; esac done # Set wine bottle path to fixed location export WINEPREFIX="$HOME/.local/wine64" export bottle="$WINEPREFIX" # In case the user hasn't run a game using sapi in this prefix yet, let's try to initialize all the registry keys properly. cat << "EOF" > "${bottle}/drive_c/windows/temp/speak.vbs" dim speechobject set speechobject=createobject("sapi.spvoice") speechobject.speak "" EOF wine cscript "c:\windows\temp\speak.vbs" # Create an array of available voices. ifs="$IFS" IFS=$'\n' mapfile -t voiceListFullName < <(grep -P '\[Software\\\\(Wow6432Node\\\\|)Microsoft\\\\Speech\\\\Voices\\\\Token(Enum|)s\\\\[^\\]+\].*' "${WINEPREFIX}/system.reg" | sed -E -e 's/\[([^]]+)\].*/\1/g') IFS="$ifs" voiceList=() for x in "${voiceListFullName[@]}" ; do voiceList+=("$(echo "$x" | sed -E -e 's/Software\\\\(Wow6432Node\\\\|)Microsoft\\\\Speech\\\\Voices\\\\Token(Enum|)s\\\\(.+)/\3/g')") done oldVoice="$(grep -P '"DefaultTokenId"="HKEY_LOCAL_MACHINE\\\\(SOFTWARE|Software)\\\\(Wow6432Node\\\\|)Microsoft\\\\Speech\\\\Voices\\\\Token(Enum|)s\\\\[^"]+"' "${WINEPREFIX}/user.reg" | sed -E -e 's/"DefaultTokenId"="([^"]+)"/\1/g')" exit=1 if [[ "${#voiceList[@]}" -eq 0 ]]; then dialog --msgbox "No voices found." -1 -1 exit 1 fi while [[ $exit -ne 0 ]] ; do if [[ -z "${voice}" ]]; then voice="$(menulist "${voiceList[@]}")" fi case $? in 0) set_voice "$voice" "${rate:-7}" ; exit=0 ;; 3) test_voice "$voice";; *) restore_voice ; exit=0 ;; esac done exit 0