Version 1.0.7 terminalphone.sh

This commit is contained in:
Here ForAwhile 2026-02-19 17:47:10 +00:00
parent 25f094a77b
commit 27fc4314f0

View file

@ -9,7 +9,7 @@ set -euo pipefail
# CONFIGURATION # CONFIGURATION
#============================================================================= #=============================================================================
APP_NAME="TerminalPhone" APP_NAME="TerminalPhone"
VERSION="1.0.6" VERSION="1.0.7"
BASE_DIR="$(dirname "$(readlink -f "$0")")" BASE_DIR="$(dirname "$(readlink -f "$0")")"
DATA_DIR="$BASE_DIR/.terminalphone" DATA_DIR="$BASE_DIR/.terminalphone"
TOR_DIR="$DATA_DIR/tor_data" TOR_DIR="$DATA_DIR/tor_data"
@ -39,6 +39,16 @@ CHUNK_DURATION=1 # seconds per audio chunk
CIPHER="aes-256-cbc" # OpenSSL cipher for encryption CIPHER="aes-256-cbc" # OpenSSL cipher for encryption
SNOWFLAKE_ENABLED=0 # Snowflake bridge (off by default) SNOWFLAKE_ENABLED=0 # Snowflake bridge (off by default)
AUTO_LISTEN=0 # Auto-listen after Tor starts (off by default) AUTO_LISTEN=0 # Auto-listen after Tor starts (off by default)
VOICE_EFFECT="none" # Voice effect (none, deep, high, robot, echo, whisper, custom)
# Custom voice effect parameters (used when VOICE_EFFECT=custom)
VOICE_PITCH=0 # Pitch shift in cents (-600 to +600, 0=off)
VOICE_OVERDRIVE=0 # Overdrive gain (0=off, 5-20)
VOICE_FLANGER=0 # Flanger (0=off, 1=on)
VOICE_ECHO_DELAY=0 # Echo delay in ms (0=off, 20-200)
VOICE_ECHO_DECAY=5 # Echo decay (1-9 → 0.1-0.9)
VOICE_HIGHPASS=0 # Highpass filter freq in Hz (0=off, 300-2000)
VOICE_TREMOLO=0 # Tremolo speed in Hz (0=off, 5-40)
# ANSI Colors # ANSI Colors
RED='\033[0;31m' RED='\033[0;31m'
@ -164,6 +174,14 @@ PTT_KEY="$PTT_KEY"
CIPHER="$CIPHER" CIPHER="$CIPHER"
SNOWFLAKE_ENABLED=$SNOWFLAKE_ENABLED SNOWFLAKE_ENABLED=$SNOWFLAKE_ENABLED
AUTO_LISTEN=$AUTO_LISTEN AUTO_LISTEN=$AUTO_LISTEN
VOICE_EFFECT="$VOICE_EFFECT"
VOICE_PITCH=$VOICE_PITCH
VOICE_OVERDRIVE=$VOICE_OVERDRIVE
VOICE_FLANGER=$VOICE_FLANGER
VOICE_ECHO_DELAY=$VOICE_ECHO_DELAY
VOICE_ECHO_DECAY=$VOICE_ECHO_DECAY
VOICE_HIGHPASS=$VOICE_HIGHPASS
VOICE_TREMOLO=$VOICE_TREMOLO
EOF EOF
} }
@ -556,6 +574,48 @@ start_recording() {
fi fi
} }
# Apply voice effect to raw PCM using sox
apply_voice_effect() {
local infile="$1"
local outfile="$2"
local fmt="-t raw -r $SAMPLE_RATE -e signed -b 16 -c 1"
case "$VOICE_EFFECT" in
deep)
sox $fmt "$infile" $fmt "$outfile" pitch -400 2>/dev/null
;;
high)
sox $fmt "$infile" $fmt "$outfile" pitch 500 2>/dev/null
;;
robot)
sox $fmt "$infile" $fmt "$outfile" overdrive 10 flanger 2>/dev/null
;;
echo)
sox $fmt "$infile" $fmt "$outfile" echo 0.8 0.88 60 0.4 2>/dev/null
;;
whisper)
sox $fmt "$infile" $fmt "$outfile" highpass 1000 tremolo 20 2>/dev/null
;;
custom)
# Build sox effects chain from individual parameters
local effects=""
[ "$VOICE_PITCH" -ne 0 ] 2>/dev/null && effects="$effects pitch $VOICE_PITCH"
[ "$VOICE_OVERDRIVE" -gt 0 ] 2>/dev/null && effects="$effects overdrive $VOICE_OVERDRIVE"
[ "$VOICE_FLANGER" -eq 1 ] 2>/dev/null && effects="$effects flanger"
[ "$VOICE_ECHO_DELAY" -gt 0 ] 2>/dev/null && effects="$effects echo 0.8 0.88 $VOICE_ECHO_DELAY 0.${VOICE_ECHO_DECAY}"
[ "$VOICE_HIGHPASS" -gt 0 ] 2>/dev/null && effects="$effects highpass $VOICE_HIGHPASS"
[ "$VOICE_TREMOLO" -gt 0 ] 2>/dev/null && effects="$effects tremolo $VOICE_TREMOLO"
if [ -n "$effects" ]; then
sox $fmt "$infile" $fmt "$outfile" $effects 2>/dev/null
else
return 1 # no effects configured
fi
;;
*)
return 1 # no effect
;;
esac
}
# Stop recording and send the message # Stop recording and send the message
# Encodes, encrypts, base64-encodes, and writes to fd 4 # Encodes, encrypts, base64-encodes, and writes to fd 4
stop_and_send() { stop_and_send() {
@ -585,6 +645,16 @@ stop_and_send() {
REC_PID="" REC_PID=""
REC_FILE="" REC_FILE=""
# Apply voice effect if set
if [ -s "$raw_file" ] && [ "$VOICE_EFFECT" != "none" ]; then
local fx_file="$AUDIO_DIR/tx_fx_${_id}.raw"
if apply_voice_effect "$raw_file" "$fx_file"; then
mv "$fx_file" "$raw_file"
else
rm -f "$fx_file" 2>/dev/null
fi
fi
# Encode → encrypt → send # Encode → encrypt → send
if [ -s "$raw_file" ]; then if [ -s "$raw_file" ]; then
opusenc --raw --raw-rate "$SAMPLE_RATE" --raw-chan 1 \ opusenc --raw --raw-rate "$SAMPLE_RATE" --raw-chan 1 \
@ -1468,13 +1538,18 @@ settings_menu() {
local ptt_display="SPACE" local ptt_display="SPACE"
[ "$PTT_KEY" != " " ] && ptt_display="$PTT_KEY" [ "$PTT_KEY" != " " ] && ptt_display="$PTT_KEY"
echo -e " ${DIM}PTT key: ${NC}${WHITE}${ptt_display}${NC}\n" echo -e " ${DIM}PTT key: ${NC}${WHITE}${ptt_display}${NC}"
local vfx_display="${VOICE_EFFECT}"
[ "$vfx_display" = "none" ] && vfx_display="off"
echo -e " ${DIM}Voice effect: ${NC}${WHITE}${vfx_display}${NC}\n"
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}${NC} Change encryption cipher" echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}${NC} Change encryption cipher"
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}${NC} Change Opus encoding quality" echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}${NC} Change Opus encoding quality"
echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}${NC} Snowflake bridge (censorship circumvention)" echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}${NC} Snowflake bridge (censorship circumvention)"
echo -e " ${BOLD}${WHITE}4${NC} ${CYAN}${NC} Auto-listen (listen for calls automatically once Tor starts)" echo -e " ${BOLD}${WHITE}4${NC} ${CYAN}${NC} Auto-listen (listen for calls automatically once Tor starts)"
echo -e " ${BOLD}${WHITE}5${NC} ${CYAN}${NC} Change PTT (push-to-talk) key" echo -e " ${BOLD}${WHITE}5${NC} ${CYAN}${NC} Change PTT (push-to-talk) key"
echo -e " ${BOLD}${WHITE}6${NC} ${CYAN}${NC} Voice changer"
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}${NC} ${DIM}Back to main menu${NC}" echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}${NC} ${DIM}Back to main menu${NC}"
echo "" echo ""
echo -ne " ${BOLD}Select: ${NC}" echo -ne " ${BOLD}Select: ${NC}"
@ -1518,6 +1593,7 @@ settings_menu() {
fi fi
sleep 1 sleep 1
;; ;;
6) settings_voice ;;
0|q|Q) return ;; 0|q|Q) return ;;
*) *)
echo -e "\n ${RED}Invalid choice${NC}" echo -e "\n ${RED}Invalid choice${NC}"
@ -1762,6 +1838,137 @@ settings_snowflake() {
read -r read -r
} }
settings_voice() {
echo -e "\n${BOLD}${CYAN}═══ Voice Changer ═══${NC}\n"
echo -e " ${DIM}Current effect: ${NC}${GREEN}${VOICE_EFFECT}${NC}\n"
local effects=("none" "deep" "high" "robot" "echo" "whisper" "custom")
local descs=(
"No effect (natural voice)"
"Deep voice (pitch shifted down)"
"High voice (pitch shifted up)"
"Robot (overdrive + flanger)"
"Echo (delayed reverb)"
"Whisper (highpass + tremolo)"
"Custom (configure all parameters)"
)
local i
for i in "${!effects[@]}"; do
local num=$(( i + 1 ))
local marker=" "
if [ "${effects[$i]}" = "$VOICE_EFFECT" ]; then
marker="${GREEN}> ${NC}"
fi
echo -e " ${marker}${BOLD}${WHITE}${num}${NC} ${CYAN}${NC} ${descs[$i]}"
done
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}${NC} ${DIM}Back${NC}"
echo ""
echo -ne " ${BOLD}Select: ${NC}"
read -r vchoice
case "$vchoice" in
1) VOICE_EFFECT="none" ;;
2) VOICE_EFFECT="deep" ;;
3) VOICE_EFFECT="high" ;;
4) VOICE_EFFECT="robot" ;;
5) VOICE_EFFECT="echo" ;;
6) VOICE_EFFECT="whisper" ;;
7) VOICE_EFFECT="custom"
settings_voice_custom
;;
0|q|Q) return ;;
*)
echo -e "\n ${RED}Invalid choice${NC}"
sleep 1
return
;;
esac
save_config
log_ok "Voice effect set to: ${VOICE_EFFECT}"
sleep 1
}
settings_voice_custom() {
while true; do
clear
echo -e "\n${BOLD}${CYAN}═══ Custom Voice Effect ═══${NC}\n"
echo -e " ${DIM}Configure each parameter. Effects are combined into one chain.${NC}"
echo -e " ${DIM}Set a value to 0 to disable that effect.${NC}\n"
local _p_status="${RED}off${NC}"
[ "$VOICE_PITCH" -ne 0 ] 2>/dev/null && _p_status="${GREEN}${VOICE_PITCH} cents${NC}"
local _od_status="${RED}off${NC}"
[ "$VOICE_OVERDRIVE" -gt 0 ] 2>/dev/null && _od_status="${GREEN}${VOICE_OVERDRIVE}${NC}"
local _fl_status="${RED}off${NC}"
[ "$VOICE_FLANGER" -eq 1 ] 2>/dev/null && _fl_status="${GREEN}on${NC}"
local _ed_status="${RED}off${NC}"
[ "$VOICE_ECHO_DELAY" -gt 0 ] 2>/dev/null && _ed_status="${GREEN}${VOICE_ECHO_DELAY}ms decay 0.${VOICE_ECHO_DECAY}${NC}"
local _hp_status="${RED}off${NC}"
[ "$VOICE_HIGHPASS" -gt 0 ] 2>/dev/null && _hp_status="${GREEN}${VOICE_HIGHPASS} Hz${NC}"
local _tr_status="${RED}off${NC}"
[ "$VOICE_TREMOLO" -gt 0 ] 2>/dev/null && _tr_status="${GREEN}${VOICE_TREMOLO} Hz${NC}"
echo -e " ${BOLD}${WHITE}1${NC} ${CYAN}${NC} Pitch shift ${_p_status} ${DIM}(-600 to +600 cents)${NC}"
echo -e " ${BOLD}${WHITE}2${NC} ${CYAN}${NC} Overdrive ${_od_status} ${DIM}(0=off, 5-20)${NC}"
echo -e " ${BOLD}${WHITE}3${NC} ${CYAN}${NC} Flanger ${_fl_status} ${DIM}(0=off, 1=on)${NC}"
echo -e " ${BOLD}${WHITE}4${NC} ${CYAN}${NC} Echo ${_ed_status} ${DIM}(delay 0-200ms, decay 1-9)${NC}"
echo -e " ${BOLD}${WHITE}5${NC} ${CYAN}${NC} Highpass filter ${_hp_status} ${DIM}(0=off, 300-2000 Hz)${NC}"
echo -e " ${BOLD}${WHITE}6${NC} ${CYAN}${NC} Tremolo ${_tr_status} ${DIM}(0=off, 5-40 Hz)${NC}"
echo -e " ${BOLD}${WHITE}0${NC} ${CYAN}${NC} ${DIM}Done${NC}"
echo ""
echo -ne " ${BOLD}Select parameter: ${NC}"
read -r pchoice
case "$pchoice" in
1)
echo -ne " ${BOLD}Pitch shift (cents, -600 to +600, 0=off): ${NC}"
read -r val
[[ "$val" =~ ^-?[0-9]+$ ]] && VOICE_PITCH=$val
;;
2)
echo -ne " ${BOLD}Overdrive gain (0=off, 5-20): ${NC}"
read -r val
[[ "$val" =~ ^[0-9]+$ ]] && VOICE_OVERDRIVE=$val
;;
3)
echo -ne " ${BOLD}Flanger (0=off, 1=on): ${NC}"
read -r val
[[ "$val" =~ ^[01]$ ]] && VOICE_FLANGER=$val
;;
4)
echo -ne " ${BOLD}Echo delay (ms, 0=off, 20-200): ${NC}"
read -r val
[[ "$val" =~ ^[0-9]+$ ]] && VOICE_ECHO_DELAY=$val
if [ "$VOICE_ECHO_DELAY" -gt 0 ] 2>/dev/null; then
echo -ne " ${BOLD}Echo decay (1-9, maps to 0.1-0.9): ${NC}"
read -r val
[[ "$val" =~ ^[1-9]$ ]] && VOICE_ECHO_DECAY=$val
fi
;;
5)
echo -ne " ${BOLD}Highpass frequency (Hz, 0=off, 300-2000): ${NC}"
read -r val
[[ "$val" =~ ^[0-9]+$ ]] && VOICE_HIGHPASS=$val
;;
6)
echo -ne " ${BOLD}Tremolo speed (Hz, 0=off, 5-40): ${NC}"
read -r val
[[ "$val" =~ ^[0-9]+$ ]] && VOICE_TREMOLO=$val
;;
0|q|Q)
save_config
return
;;
*)
echo -e "\n ${RED}Invalid choice${NC}"
sleep 1
;;
esac
done
}
#============================================================================= #=============================================================================
# MAIN MENU # MAIN MENU
#============================================================================= #=============================================================================