228 lines
7.0 KiB
Bash
Executable File
228 lines
7.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Initialize variables for spd-say options
|
|
rate=""
|
|
pitch=""
|
|
pitch_range=""
|
|
punctuation_mode=""
|
|
wait_flag=""
|
|
ssml_mode=""
|
|
application_name="espeak-ng-wrapper"
|
|
|
|
# Function to check if a string is numeric
|
|
is_numeric() {
|
|
[[ "$1" =~ ^-?[0-9]+$ ]]
|
|
}
|
|
|
|
# Process arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
# Handle speed parameter (can be -s 100 or -s100)
|
|
-s*)
|
|
if [[ "$1" == "-s" && -n "$2" && $(is_numeric "$2") ]]; then
|
|
# Format: -s 100
|
|
espeak_speed="$2"
|
|
shift 2
|
|
elif [[ "$1" =~ ^-s([0-9]+)$ ]]; then
|
|
# Format: -s100
|
|
espeak_speed="${BASH_REMATCH[1]}"
|
|
shift
|
|
else
|
|
# Invalid format, skip
|
|
shift
|
|
continue
|
|
fi
|
|
|
|
# Convert espeak-ng speed to spd-say rate
|
|
spd_rate=$(( (($espeak_speed - 175) * 200) / 420 ))
|
|
|
|
# Clamp to spd-say's allowed range
|
|
if (( spd_rate > 100 )); then
|
|
spd_rate=100
|
|
elif (( spd_rate < -100 )); then
|
|
spd_rate=-100
|
|
fi
|
|
rate="-r $spd_rate"
|
|
;;
|
|
|
|
# Handle pitch parameter (can be -p 50 or -p50)
|
|
-p*)
|
|
if [[ "$1" == "-p" && -n "$2" && $(is_numeric "$2") ]]; then
|
|
# Format: -p 50
|
|
espeak_pitch="$2"
|
|
shift 2
|
|
elif [[ "$1" =~ ^-p([0-9]+)$ ]]; then
|
|
# Format: -p50
|
|
espeak_pitch="${BASH_REMATCH[1]}"
|
|
shift
|
|
else
|
|
# Invalid format, skip
|
|
shift
|
|
continue
|
|
fi
|
|
|
|
# Convert espeak-ng pitch to spd-say pitch
|
|
spd_pitch=$(( (($espeak_pitch - 50) * 200) / 99 ))
|
|
|
|
# Clamp to spd-say's allowed range
|
|
if (( spd_pitch > 100 )); then
|
|
spd_pitch=100
|
|
elif (( spd_pitch < -100 )); then
|
|
spd_pitch=-100
|
|
fi
|
|
pitch="-p $spd_pitch"
|
|
;;
|
|
|
|
# Handle pitch range parameter (can be -P 50 or -P50)
|
|
-P*)
|
|
if [[ "$1" == "-P" && -n "$2" && $(is_numeric "$2") ]]; then
|
|
# Format: -P 50
|
|
espeak_pitch_range="$2"
|
|
shift 2
|
|
elif [[ "$1" =~ ^-P([0-9]+)$ ]]; then
|
|
# Format: -P50
|
|
espeak_pitch_range="${BASH_REMATCH[1]}"
|
|
shift
|
|
else
|
|
# Invalid format, skip
|
|
shift
|
|
continue
|
|
fi
|
|
|
|
# Convert espeak-ng pitch range to spd-say pitch range
|
|
spd_pitch_range=$(( (($espeak_pitch_range - 50) * 200) / 99 ))
|
|
|
|
# Clamp to spd-say's allowed range
|
|
if (( spd_pitch_range > 100 )); then
|
|
spd_pitch_range=100
|
|
elif (( spd_pitch_range < -100 )); then
|
|
spd_pitch_range=-100
|
|
fi
|
|
pitch_range="-R $spd_pitch_range"
|
|
;;
|
|
|
|
# Handle punctuation mode parameter (can be -m none or -mnone)
|
|
-m*)
|
|
if [[ "$1" == "-m" && -n "$2" ]]; then
|
|
# Format: -m none
|
|
punct_mode="$2"
|
|
shift 2
|
|
elif [[ "$1" =~ ^-m(.+)$ ]]; then
|
|
# Format: -mnone
|
|
punct_mode="${BASH_REMATCH[1]}"
|
|
shift
|
|
else
|
|
# Invalid format, skip
|
|
shift
|
|
continue
|
|
fi
|
|
|
|
# Process the mode
|
|
if [[ "$punct_mode" == "ssml" ]]; then
|
|
# SSML mode for spd-say
|
|
ssml_mode="-x"
|
|
else
|
|
# Map to punctuation mode if specified
|
|
case "$punct_mode" in
|
|
none|some|most|all)
|
|
punctuation_mode="-m $punct_mode"
|
|
;;
|
|
esac
|
|
fi
|
|
;;
|
|
|
|
# File input parameter (can be -f file.txt or -ffile.txt)
|
|
-f*)
|
|
if [[ "$1" == "-f" && -n "$2" ]]; then
|
|
# Format: -f file.txt
|
|
input_file="$2"
|
|
shift 2
|
|
elif [[ "$1" =~ ^-f(.+)$ ]]; then
|
|
# Format: -ffile.txt
|
|
input_file="${BASH_REMATCH[1]}"
|
|
shift
|
|
else
|
|
# Invalid format, skip
|
|
shift
|
|
continue
|
|
fi
|
|
|
|
# Check if file exists
|
|
if [[ -f "$input_file" ]]; then
|
|
input_from_file="$input_file"
|
|
fi
|
|
;;
|
|
|
|
# Wait until speaking is done
|
|
-z)
|
|
wait_flag="-w"
|
|
shift
|
|
;;
|
|
|
|
# Input from stdin
|
|
--stdin)
|
|
input_from_stdin=1
|
|
shift
|
|
;;
|
|
|
|
# Silently discard voice, volume, and other unsupported options with possibly attached values
|
|
-v*|-a*|-l*|-d*|-g*|-k*|-b*|-w*|-q*|-x*|-X*|-D*|--compile*|--ipa|--path|--pho|--phonout|--punct*|--sep*|--split*|--stdout|--tie*|--version|--voices*|--load*)
|
|
# Skip the option (and its separated value if it exists)
|
|
if [[ "$1" =~ ^-[a-zA-Z]$ && -n "$2" && ! "$2" =~ ^- ]]; then
|
|
# Format with separate value: -v en-us
|
|
shift 2
|
|
else
|
|
# Format with attached value or no value: -ven-us or just -q
|
|
shift
|
|
fi
|
|
;;
|
|
|
|
# Any other unrecognized options - silently discard
|
|
-*)
|
|
# Skip the option (and its separated value if it exists)
|
|
if [[ "$1" =~ ^-[a-zA-Z]$ && -n "$2" && ! "$2" =~ ^- ]]; then
|
|
# Format with separate value
|
|
shift 2
|
|
else
|
|
# Format with attached value or no value
|
|
shift
|
|
fi
|
|
;;
|
|
|
|
# Text to speak - anything not starting with -
|
|
*)
|
|
# Collect all non-option arguments as text to speak
|
|
if [[ -z "$text" ]]; then
|
|
text="$1"
|
|
else
|
|
text="$text $1"
|
|
fi
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Build the spd-say command with only the supported converted options
|
|
spd_cmd="spd-say $rate $pitch $pitch_range $punctuation_mode $ssml_mode $wait_flag -N $application_name"
|
|
|
|
# Handle the input based on what was provided
|
|
if [[ -n "$input_from_file" ]]; then
|
|
# Input from specified file
|
|
spd_cmd="$spd_cmd -e --"
|
|
cat "$input_from_file" | $spd_cmd
|
|
elif [[ -n "$input_from_stdin" ]]; then
|
|
# Input from stdin all at once
|
|
spd_cmd="$spd_cmd -e --"
|
|
cat | $spd_cmd
|
|
elif [[ -n "$text" ]]; then
|
|
# Text provided as arguments
|
|
$spd_cmd -- "$text"
|
|
else
|
|
# No text provided, read from stdin line by line
|
|
spd_cmd="$spd_cmd -e --"
|
|
cat | $spd_cmd
|
|
fi
|
|
|
|
# Exit with success
|
|
exit 0
|