diff --git a/usr/bin/espeak b/usr/bin/espeak new file mode 100755 index 0000000..06b7583 --- /dev/null +++ b/usr/bin/espeak @@ -0,0 +1,227 @@ +#!/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