#!/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