Skip to content

Instantly share code, notes, and snippets.

@alexkuz
Last active March 11, 2024 22:46
Show Gist options
  • Save alexkuz/f24f93245ff80458c9b6ec93c644c40b to your computer and use it in GitHub Desktop.
Save alexkuz/f24f93245ff80458c9b6ec93c644c40b to your computer and use it in GitHub Desktop.
Install Piper TTS as speech dispatcher module
#!/bin/sh
set -e
BASE_PIPER_VOICES_URL=${BASE_PIPER_VOICES_URL:-https://huggingface.co/rhasspy/piper-voices/resolve/main}
INSTALL_DIR=${INSTALL_DIR:-~/.local/share/speech-dispatcher-piper}
CONFIG_DIR=${CONFIG_DIR:-~/.config/speech-dispatcher/modules}
CONFIG_PATH=$CONFIG_DIR/piper-generic.conf
PLATFORM=${PLATFORM:-$(uname -m)}
PIPER_BIN_GZ_URL=${PIPER_BIN_GZ_URL:-https://github.com/rhasspy/piper/releases/latest/download/piper_linux_$PLATFORM.tar.gz}
PIPER_GENERIC_CONF=$(cat <<EOF
DefaultVoice "en_GB-alan-low"
GenericCmdDependency "sox"
GenericCmdDependency "jq"
GenericExecuteSynth \
"cd $INSTALL_DIR && \
./check_piper_voice.sh \$VOICE && \
printf %s \'\$DATA\' \
| ./piper/piper --model \'voices/\$VOICE.onnx\' --output_raw \
| sox -v \$VOLUME -r \$(jq .audio.sample_rate < \'voices/\$VOICE.onnx.json\') -c 1 \
-b 16 -e signed-integer -t raw - -t wav - tempo \$RATE pitch \$PITCH norm \
| \$PLAY_COMMAND"
GenericRateAdd 1
GenericPitchAdd 1
GenericVolumeAdd 1
GenericRateMultiply 1
GenericPitchMultiply 750
GenericVolumeMultiply 1
EOF
)
# checks if piper voice is present and download it if not
CHECK_PIPER_VOICE_SH=$(cat <<EOF
#!/bin/sh
VOICE=\$1
VOICE_MODEL=\$VOICE.onnx
VOICE_JSON=\$VOICE.onnx.json
BASE_PIPER_VOICES_URL=$BASE_PIPER_VOICES_URL
if ! [ -f "voices/\$VOICE_MODEL" ] || ! [ -f "voices/\$VOICE_JSON" ]; then
LANG_FULL=\$(echo "\$VOICE" | cut -d '-' -f 1)
LANG_SHORT=\$(echo "\$LANG_FULL" | cut -d '_' -f 1)
NAME=\$(echo "\$VOICE" | cut -d '-' -f 2)
QUALITY=\$(echo "\$VOICE" | cut -d '-' -f 3)
if ! [ -f "voices/\$VOICE_JSON" ]; then
curl -s -L -C - -o "voices/\$VOICE_JSON.download" "\$BASE_PIPER_VOICES_URL/\$LANG_SHORT/\$LANG_FULL/\$NAME/\$QUALITY/\$VOICE_JSON?download=true"
mv "voices/\$VOICE_JSON.download" "voices/\$VOICE_JSON"
fi
if ! [ -f "voices/\$VOICE_MODEL" ]; then
curl -s -L -C - -o "voices/\$VOICE_MODEL.download" "\$BASE_PIPER_VOICES_URL/\$LANG_SHORT/\$LANG_FULL/\$NAME/\$QUALITY/\$VOICE_MODEL?download=true"
mv "voices/\$VOICE_MODEL.download" "voices/\$VOICE_MODEL"
fi
fi
EOF
)
echo "\033[1;36mInstalling piper dependencies: \033[0mjq, sox"
apt -qq install jq sox
mkdir -p "$INSTALL_DIR/voices"
if ! [ -d "$CONFIG_DIR" ]; then
echo "\033[1;36mConfig directory not found, \033[0mexecuting spd-conf"
spd-conf -n
fi
# if config exists, backup old version and create new one
if [ -f "$CONFIG_PATH" ]; then
echo "\033[1;36mConfig file found, \033[0mbacking up old version"
i=1
while [ -f "$CONFIG_PATH.$i.bak" ]; do
i=$((i+1))
done
mv "$CONFIG_PATH" "$CONFIG_PATH.$i.bak"
fi
touch "$CONFIG_PATH"
echo "\033[1;36mDownload voice list and create piper config\033[0m"
VOICE_JSON=$(mktemp)
curl -s -L -o "$VOICE_JSON" "$BASE_PIPER_VOICES_URL/voices.json"
VOICES=$(jq -r 'map(.key) | @sh' < "$VOICE_JSON")
rm "$VOICE_JSON"
for VOICE in $VOICES; do
VOICE=${VOICE#\'} && VOICE=${VOICE%\'}
LANG_FULL=$(echo "$VOICE" | cut -d '-' -f 1)
# if voice contains "female", use FEMALE1 as voice name, otherwise use MALE1
VOICE_GENDER=MALE1
case "$VOICE" in
*female*)
VOICE_GENDER=FEMALE1
;;
esac
echo "AddVoice \"$LANG_FULL\" \"$VOICE_GENDER\" \"$VOICE\"" >> "$CONFIG_PATH"
done
echo "$PIPER_GENERIC_CONF" >> "$CONFIG_PATH"
echo "\033[1;36mDownload piper binaries\033[0m"
PIPER_BIN_GZ=$(mktemp)
curl -s -L -o "$PIPER_BIN_GZ" "$PIPER_BIN_GZ_URL"
rm -rf "$INSTALL_DIR/piper"
tar -xzf "$PIPER_BIN_GZ" -C "$INSTALL_DIR"
rm "$PIPER_BIN_GZ"
echo "\033[1;36mCreate check_piper_voice.sh\033[0m"
echo "$CHECK_PIPER_VOICE_SH" > "$INSTALL_DIR/check_piper_voice.sh"
chmod +x "$INSTALL_DIR/check_piper_voice.sh"
# ask if user wants to test piper
printf "\033[1;36mDo you want to test piper? [Y/n]\033[0m "
read -r YN </dev/tty
YN=${YN:-y}
case $YN in
[Yy]* )
echo "\033[1;36mPronouncing the following sentence:\033[0m \"Piper is now installed\""
spd-say -w -o piper-generic -y en_GB-alan-low "Piper is now installed"
;;
* ) ;;
esac
echo "\033[1;32mInstallation complete\033[0m"
@lamyergeier
Copy link

lamyergeier commented Jan 10, 2024

In the following snippet from above:

printf %s \'\$DATA\' \
| ./piper/piper --model \'voices/\$VOICE.onnx\' --output_raw \
| sox -v \$VOLUME -r \$(jq .audio.sample_rate < \'voices/\$VOICE.onnx.json\') -c 1 \
 -b 16 -e signed-integer -t raw - -t wav - tempo \$RATE pitch \$PITCH norm \
| \$PLAY_COMMAND"

I have a question - You have used VOLUME, RATE and PITCH variables. Are these standard variables from speech dispatcher?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment