#!/usr/bin/env bash # Set Voice - Fixed version for audiogame-manager # Set a variable to make mac compatibility easier... sed="sed" grep="grep" if [[ "$(uname)" == "Darwin" ]]; then sed="gsed" grep="ggrep" fi export grep export sed # 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=( [b:]="the wine bottle to use." [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 } msgbox() { dialog --clear --msgbox "$*" 0 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 ${wine}server -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="$(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}}" ${wine}server -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="$(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}}" ${wine}server -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 b) if ! [[ -d ~/".local/wine/${OPTARG}" ]]; then echo "Invalid wine bottle specified." exit 1 fi export bottle=~/".local/wine/${OPTARG}" export WINEPREFIX=~/".local/wine/${OPTARG}" ;; 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 # Get the desired wine bottle # Offer a list of wine bottles if one isn't specified on the command line. if [[ -z "${bottle}" ]]; then declare -a bottle for i in $(find ~/.local/wine/ -maxdepth 1 -type d -not -name 'wine' | sort) ; do bottle+=("$i" "${i##*/}") done export WINEPREFIX="$(dialog --backtitle "Use the up and down arrow keys to find the option you want, then press enter to select it." \ --clear \ --no-tags \ --menu "Select A Wine Bottle" 0 0 0 "${bottle[@]}" --stdout)" fi if [[ -z "${WINEPREFIX}" ]]; then exit 0 fi export bottle="$WINEPREFIX" # Get wine version if available - Use wine32 for SAPI games if [[ -r "${WINEPREFIX}/agm.conf" ]]; then source "${WINEPREFIX}/agm.conf" export WINE export WINESERVER fi # Use wine32 installation from audiogame-manager wine32Dir="${XDG_DATA_HOME:-$HOME/.local/share}/audiogame-manager/wine32" if [[ -f "$wine32Dir/bin/wine" ]]; then wine="$wine32Dir/bin/wine" else wine="${WINE:-$(command -v wine)}" fi # 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' 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